Compare commits
24 Commits
2.91.93
...
overview-r
Author | SHA1 | Date | |
---|---|---|---|
9efa271888 | |||
9ed3ce9006 | |||
b400fc2837 | |||
a66af6fbac | |||
1bbc138ed2 | |||
868d4756e4 | |||
dfa3be59dc | |||
62677336a5 | |||
264a82cbfc | |||
f8cfd9f903 | |||
13f8cd3bcf | |||
e1f336374b | |||
0ba1387d19 | |||
8832ce3a2b | |||
91d7a022a9 | |||
95d15725fc | |||
bb2b7d83af | |||
0abac6f67d | |||
e00398e2ac | |||
0f19492545 | |||
9d46a6ceea | |||
6432de95cc | |||
738fc375c0 | |||
4e9a530a64 |
16
.gitignore
vendored
@ -18,10 +18,13 @@ config
|
||||
configure
|
||||
data/gnome-shell.desktop
|
||||
data/gnome-shell.desktop.in
|
||||
data/gnome-shell-clock-preferences.desktop
|
||||
data/gnome-shell-clock-preferences.desktop.in
|
||||
data/gschemas.compiled
|
||||
data/org.gnome.shell.gschema.xml
|
||||
data/org.gnome.shell.gschema.valid
|
||||
js/misc/config.js
|
||||
data/org.gnome.accessibility.magnifier.gschema.xml
|
||||
data/org.gnome.accessibility.magnifier.gschema.valid
|
||||
intltool-extract.in
|
||||
intltool-merge.in
|
||||
intltool-update.in
|
||||
@ -40,23 +43,16 @@ src/*-enum-types.[ch]
|
||||
src/*-marshal.[ch]
|
||||
src/Makefile
|
||||
src/Makefile.in
|
||||
src/calendar-server/org.gnome.Shell.CalendarServer.service
|
||||
src/gnomeshell-taskpanel
|
||||
src/gnome-shell
|
||||
src/gnome-shell-calendar-server
|
||||
src/gnome-shell-extension-tool
|
||||
src/gnome-shell-jhbuild
|
||||
src/gnome-shell-perf-helper
|
||||
src/gnome-shell-real
|
||||
src/gnome-shell-clock-preferences
|
||||
src/run-js-test
|
||||
src/test-recorder
|
||||
src/test-recorder.ogg
|
||||
src/test-theme
|
||||
src/st.h
|
||||
src/stamp-st.h
|
||||
src/stamp-st.h.tmp
|
||||
stamp-h1
|
||||
tests/run-test.sh
|
||||
xmldocs.make
|
||||
*~
|
||||
*.patch
|
||||
*.sw?
|
||||
|
132
configure.ac
@ -1,14 +1,11 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[2.91.93],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[2.91.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_AUX_DIR([config])
|
||||
|
||||
AC_SUBST([PACKAGE_NAME], ["$PACKAGE_NAME"])
|
||||
AC_SUBST([PACKAGE_VERSION], ["$PACKAGE_VERSION"])
|
||||
|
||||
AM_INIT_AUTOMAKE([1.10 dist-bzip2 no-dist-gzip foreign])
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
@ -52,106 +49,65 @@ AC_MSG_CHECKING([for GStreamer (needed for recording functionality)])
|
||||
if $PKG_CONFIG --exists gstreamer-0.10 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
build_recorder=true
|
||||
recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
|
||||
recorder_modules="gstreamer-0.10 gstreamer-base-0.10 xfixes"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.5.15
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=0.7.11
|
||||
MUTTER_MIN_VERSION=2.91.93
|
||||
GTK_MIN_VERSION=3.0.0
|
||||
CLUTTER_MIN_VERSION=1.3.14
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.6.11
|
||||
GJS_MIN_VERSION=0.7
|
||||
MUTTER_MIN_VERSION=2.91.0
|
||||
GTK_MIN_VERSION=2.91.0
|
||||
GIO_MIN_VERSION=2.25.9
|
||||
LIBECAL_MIN_VERSION=2.32.0
|
||||
LIBEDATASERVER_MIN_VERSION=1.2.0
|
||||
LIBEDATASERVERUI2_MIN_VERSION=1.2.0
|
||||
LIBEDATASERVERUI3_MIN_VERSION=2.91.6
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.13.12
|
||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
|
||||
# Collect more than 20 libraries for a prize!
|
||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
|
||||
gio-unix-2.0 dbus-glib-1 libxml-2.0
|
||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||
libmutter >= $MUTTER_MIN_VERSION
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
libgnome-menu $recorder_modules gconf-2.0
|
||||
gdk-x11-3.0
|
||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||
libstartup-notification-1.0
|
||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||
libcanberra
|
||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes)
|
||||
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
|
||||
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
|
||||
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
|
||||
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
|
||||
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
|
||||
# NM is the only typelib we use that we don't jhbuild
|
||||
PKG_CHECK_EXISTS([libnm-glib >= 0.8.995],
|
||||
[NM_TYPELIBDIR=`$PKG_CONFIG --variable=libdir libnm-glib`/girepository-1.0
|
||||
if test "$INTROSPECTION_TYPELIBDIR" != "$NM_TYPELIBDIR"; then
|
||||
JHBUILD_TYPELIBDIR="$JHBUILD_TYPELIBDIR:$NM_TYPELIBDIR"
|
||||
fi])
|
||||
AC_SUBST(JHBUILD_TYPELIBDIR)
|
||||
PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION
|
||||
gio-unix-2.0 dbus-glib-1
|
||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||
mutter-plugins >= $MUTTER_MIN_VERSION
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
libgnome-menu $recorder_modules gconf-2.0
|
||||
gdk-x11-3.0
|
||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||
libstartup-notification-1.0
|
||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||
libcanberra)
|
||||
|
||||
saved_CFLAGS=$CFLAGS
|
||||
saved_LIBS=$LIBS
|
||||
CFLAGS=$GNOME_SHELL_CFLAGS
|
||||
LIBS=$GNOME_SHELL_LIBS
|
||||
CFLAGS=$MUTTER_PLUGIN_CFLAGS
|
||||
LIBS=$MUTTER_PLUGIN_LIBS
|
||||
# sn_startup_sequence_get_application_id, we can replace with a version check later
|
||||
AC_CHECK_FUNCS(JS_NewGlobalObject sn_startup_sequence_get_application_id XFixesCreatePointerBarrier)
|
||||
AC_CHECK_FUNCS(JS_NewGlobalObject sn_startup_sequence_get_application_id)
|
||||
CFLAGS=$saved_CFLAGS
|
||||
LIBS=$saved_LIBS
|
||||
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 gnome-desktop-3.0 >= 2.90.0 x11)
|
||||
PKG_CHECK_MODULES(TIDY, clutter-1.0)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 gnome-desktop-3.0 >= 2.90.0)
|
||||
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-3.0)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
||||
|
||||
PKG_CHECK_MODULES(JS_TEST, clutter-x11-1.0 gjs-1.0 gobject-introspection-1.0 gtk+-3.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
|
||||
|
||||
AC_MSG_CHECKING([for bluetooth support])
|
||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 2.90.0],
|
||||
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=libdir gnome-bluetooth-1.0`/gnome-bluetooth
|
||||
BLUETOOTH_LIBS="-L'$BLUETOOTH_DIR' -lgnome-bluetooth-applet"
|
||||
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
|
||||
AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
|
||||
AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
|
||||
AC_SUBST([HAVE_BLUETOOTH],[1])
|
||||
AC_MSG_RESULT([yes])],
|
||||
[AC_DEFINE([HAVE_BLUETOOTH],[0])
|
||||
AC_SUBST([HAVE_BLUETOOTH],[0])
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
# Default to libedataserverui-3.0, but allow falling back to 1.2
|
||||
PKG_CHECK_EXISTS(libedataserverui-3.0,
|
||||
[EDS_API=3.0
|
||||
LIBEDATASERVERUI_MIN_VERSION=$LIBEDATASERVERUI3_MIN_VERSION],
|
||||
[EDS_API=1.2
|
||||
LIBEDATASERVERUI_MIN_VERSION=$LIBEDATASERVERUI2_MIN_VERSION])
|
||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-$EDS_API >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
|
||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
||||
AC_SUBST(CALENDAR_SERVER_LIBS)
|
||||
|
||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
|
||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
|
||||
AC_SUBST(MUTTER_GIR_DIR)
|
||||
AC_SUBST(MUTTER_TYPELIB_DIR)
|
||||
MUTTER_BIN_DIR=`$PKG_CONFIG --variable=exec_prefix mutter-plugins`/bin
|
||||
# FIXME: metacity-plugins.pc should point directly to its .gir file
|
||||
MUTTER_LIB_DIR=`$PKG_CONFIG --variable=libdir mutter-plugins`
|
||||
MUTTER_PLUGIN_DIR=`$PKG_CONFIG --variable=plugindir mutter-plugins`
|
||||
AC_SUBST(MUTTER_BIN_DIR)
|
||||
AC_SUBST(MUTTER_LIB_DIR)
|
||||
AC_SUBST(MUTTER_PLUGIN_DIR)
|
||||
|
||||
GJS_JS_DIR=`$PKG_CONFIG --variable=jsdir gjs-1.0`
|
||||
GJS_JS_NATIVE_DIR=`$PKG_CONFIG --variable=jsnativedir gjs-1.0`
|
||||
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
|
||||
AC_SUBST(GJS_JS_DIR)
|
||||
AC_SUBST(GJS_JS_NATIVE_DIR)
|
||||
AC_SUBST(GJS_CONSOLE)
|
||||
|
||||
AC_CHECK_FUNCS(fdwalk)
|
||||
@ -198,15 +154,21 @@ if test "$enable_compile_warnings" != no ; then
|
||||
fi
|
||||
changequote([,])dnl
|
||||
|
||||
AC_ARG_ENABLE(jhbuild-wrapper-script,
|
||||
AS_HELP_STRING([--jhbuild-wrapper-script=yes],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
|
||||
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
|
||||
AC_PATH_PROG(mutter, [mutter])
|
||||
AC_SUBST(mutter)
|
||||
|
||||
AC_MSG_CHECKING([if mutter was compiled with GTK+-3.0])
|
||||
if $PKG_CONFIG --libs libmutter-private | grep gtk-x11-3 >/dev/null; then
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
AC_MSG_ERROR([GNOME Shell requires Mutter to be compiled against GTK+-3.0])
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
data/Makefile
|
||||
js/Makefile
|
||||
js/misc/config.js
|
||||
src/Makefile
|
||||
tests/Makefile
|
||||
po/Makefile.in
|
||||
|
@ -1,5 +1,5 @@
|
||||
desktopdir=$(datadir)/applications
|
||||
desktop_DATA = gnome-shell.desktop
|
||||
desktop_DATA = gnome-shell.desktop gnome-shell-clock-preferences.desktop
|
||||
|
||||
# We substitute in bindir so it works as an autostart
|
||||
# file when built in a non-system prefix
|
||||
@ -12,32 +12,27 @@ desktop_DATA = gnome-shell.desktop
|
||||
%.desktop:%.desktop.in
|
||||
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
|
||||
|
||||
searchprovidersdir = $(pkgdatadir)/search_providers
|
||||
dist_searchproviders_DATA = \
|
||||
search_providers/google.xml \
|
||||
search_providers/wikipedia.xml
|
||||
dist_pkgdata_DATA = clock-preferences.ui
|
||||
|
||||
imagesdir = $(pkgdatadir)/images
|
||||
dist_images_DATA = \
|
||||
close-black.svg \
|
||||
magnifier.svg
|
||||
|
||||
themedir = $(pkgdatadir)/theme
|
||||
dist_theme_DATA = \
|
||||
theme/calendar-arrow-left.svg \
|
||||
theme/calendar-arrow-right.svg \
|
||||
theme/calendar-today.svg \
|
||||
theme/add-workspace.svg \
|
||||
theme/close-window.svg \
|
||||
theme/close.svg \
|
||||
theme/corner-ripple-ltr.png \
|
||||
theme/corner-ripple-rtl.png \
|
||||
theme/corner-ripple.png \
|
||||
theme/dash-placeholder.svg \
|
||||
theme/filter-selected-ltr.svg \
|
||||
theme/filter-selected-rtl.svg \
|
||||
theme/dialog-error.svg \
|
||||
theme/gnome-shell.css \
|
||||
theme/mosaic-view-active.svg \
|
||||
theme/mosaic-view.svg \
|
||||
theme/move-window-on-new.svg \
|
||||
theme/panel-border.svg \
|
||||
theme/panel-button-border.svg \
|
||||
theme/panel-button-highlight-narrow.svg \
|
||||
theme/panel-button-highlight-wide.svg \
|
||||
theme/process-working.svg \
|
||||
theme/process-working.png \
|
||||
theme/remove-workspace.svg \
|
||||
theme/running-indicator.svg \
|
||||
theme/scroll-button-down-hover.png \
|
||||
theme/scroll-button-down.png \
|
||||
@ -50,15 +45,16 @@ dist_theme_DATA = \
|
||||
theme/separator-white.png \
|
||||
theme/single-view-active.svg \
|
||||
theme/single-view.svg \
|
||||
theme/source-button-border.svg \
|
||||
theme/toggle-off-us.svg \
|
||||
theme/toggle-off-intl.svg \
|
||||
theme/toggle-on-us.svg \
|
||||
theme/toggle-on-intl.svg \
|
||||
theme/ws-switch-arrow-up.svg \
|
||||
theme/ws-switch-arrow-down.svg
|
||||
theme/ws-switch-arrow-left.svg \
|
||||
theme/ws-switch-arrow-right.svg
|
||||
|
||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
||||
gsettings_SCHEMAS = \
|
||||
org.gnome.accessibility.magnifier.gschema.xml \
|
||||
org.gnome.shell.gschema.xml
|
||||
|
||||
@INTLTOOL_XML_NOMERGE_RULE@
|
||||
@GSETTINGS_RULES@
|
||||
@ -75,6 +71,11 @@ all-local: gschemas.compiled
|
||||
gconfschemadir = @GCONF_SCHEMA_FILE_DIR@
|
||||
gconfschema_DATA = gnome-shell.schemas
|
||||
|
||||
menudir = $(sysconfdir)/xdg/menus
|
||||
|
||||
menu_DATA = \
|
||||
gs-applications.menu
|
||||
|
||||
shadersdir = $(pkgdatadir)/shaders
|
||||
shaders_DATA = \
|
||||
shaders/dim-window.glsl
|
||||
@ -86,13 +87,16 @@ install-data-local:
|
||||
|
||||
EXTRA_DIST = \
|
||||
gnome-shell.desktop.in.in \
|
||||
gnome-shell-clock-preferences.desktop.in.in \
|
||||
$(menu_DATA) \
|
||||
$(gconfschema_DATA) \
|
||||
$(shaders_DATA) \
|
||||
org.gnome.accessibility.magnifier.gschema.xml.in \
|
||||
org.gnome.shell.gschema.xml.in
|
||||
|
||||
CLEANFILES = \
|
||||
gnome-shell.desktop.in \
|
||||
gnome-shell-clock-preferences.desktop.in \
|
||||
$(desktop_DATA) \
|
||||
$(gsettings_SCHEMAS) \
|
||||
gschemas.compiled
|
||||
|
188
data/clock-preferences.ui
Normal file
@ -0,0 +1,188 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface domain="gnome-shell">
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy project-wide -->
|
||||
<object class="GtkDialog" id="prefs-dialog">
|
||||
<property name="border_width">5</property>
|
||||
<property name="title" translatable="yes">Clock Preferences</property>
|
||||
<property name="window_position">center</property>
|
||||
<property name="type_hint">normal</property>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">18</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment1">
|
||||
<property name="visible">True</property>
|
||||
<property name="top_padding">6</property>
|
||||
<property name="left_padding">12</property>
|
||||
<property name="right_padding">6</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="12hr_radio">
|
||||
<property name="label" translatable="yes">_12 hour format</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="24hr_radio">
|
||||
<property name="label" translatable="yes">_24 hour format</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">12hr_radio</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label_format">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Clock Format</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame2">
|
||||
<property name="visible">True</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
<property name="top_padding">6</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="date_check">
|
||||
<property name="label" translatable="yes">Show the _date</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="seconds_check">
|
||||
<property name="label" translatable="yes">Show seco_nds</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label_display">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Panel Display</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">6</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area1">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="prefs_close_button">
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="0">prefs_close_button</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
</interface>
|
66
data/close-black.svg
Normal file
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Foreground"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="16px"
|
||||
height="16px"
|
||||
viewBox="0 0 16 16"
|
||||
enable-background="new 0 0 16 16"
|
||||
xml:space="preserve"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.46+devel"
|
||||
sodipodi:docname="close-black.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
||||
id="metadata2399"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs2397"><linearGradient
|
||||
id="linearGradient3173"><stop
|
||||
style="stop-color:#c4c4c4;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3175" /><stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3177" /></linearGradient><inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 8 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="16 : 8 : 1"
|
||||
inkscape:persp3d-origin="8 : 5.3333333 : 1"
|
||||
id="perspective2401" /></defs><sodipodi:namedview
|
||||
inkscape:window-height="811"
|
||||
inkscape:window-width="1272"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
guidetolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
objecttolerance="10.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
id="base"
|
||||
showgrid="false"
|
||||
inkscape:zoom="32.125"
|
||||
inkscape:cx="8"
|
||||
inkscape:cy="10.440056"
|
||||
inkscape:window-x="40"
|
||||
inkscape:window-y="40"
|
||||
inkscape:current-layer="Foreground" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10.5,3.5l2,2L10,8l2.5,2.5l-2,2L8,10l-2.5,2.5l-2-2L6,8L3.5,5.5l2-2L8,6L10.5,3.5 z M0,8c0-4.418,3.582-8,8-8s8,3.582,8,8s-3.582,8-8,8S0,12.418,0,8z"
|
||||
id="path2394"
|
||||
style="fill-opacity:1;fill:#545454" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
15
data/gnome-shell-clock-preferences.desktop.in.in
Normal file
@ -0,0 +1,15 @@
|
||||
[Desktop Entry]
|
||||
_Name=Clock
|
||||
_Comment=Customize the panel clock
|
||||
Exec=@bindir@/gnome-shell-clock-preferences
|
||||
Icon=gnome-panel-clock
|
||||
Terminal=false
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=GNOME;GTK;Settings;DesktopSettings;
|
||||
OnlyShowIn=GNOME;
|
||||
X-GNOME-ShellOnly=true
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
X-GNOME-Bugzilla-Component=general
|
||||
X-GNOME-Bugzilla-Version=@VERSION@
|
@ -7,10 +7,9 @@ X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
X-GNOME-Bugzilla-Component=general
|
||||
X-GNOME-Bugzilla-Version=@VERSION@
|
||||
Categories=GNOME;GTK;Core;
|
||||
Categories=GNOME;GTK;Utility;Core;
|
||||
OnlyShowIn=GNOME;
|
||||
NoDisplay=true
|
||||
X-GNOME-Autostart-Phase=WindowManager
|
||||
X-GNOME-Provides=panel;windowmanager;
|
||||
X-GNOME-Autostart-Notify=true
|
||||
X-GNOME-AutoRestart=true
|
||||
|
@ -22,7 +22,7 @@
|
||||
<applyto>/desktop/gnome/shell/windows/button_layout</applyto>
|
||||
<owner>gnome-shell</owner>
|
||||
<type>string</type>
|
||||
<default>:close</default>
|
||||
<default>:minimize,maximize,close</default>
|
||||
<locale name="C">
|
||||
<short>Arrangement of buttons on the titlebar</short>
|
||||
<long>
|
||||
@ -44,20 +44,19 @@
|
||||
</schema>
|
||||
|
||||
<schema>
|
||||
<key>/schemas/desktop/gnome/shell/windows/edge_tiling</key>
|
||||
<applyto>/desktop/gnome/shell/windows/edge_tiling</applyto>
|
||||
<key>/schemas/desktop/gnome/shell/windows/side_by_side_tiling</key>
|
||||
<applyto>/desktop/gnome/shell/windows/side_by_side_tiling</applyto>
|
||||
<owner>gnome-shell</owner>
|
||||
<type>bool</type>
|
||||
<default>true</default>
|
||||
<locale name="C">
|
||||
<short>enable edge tiling when dropping windows on screen edges</short>
|
||||
<short>enable side-by-side tiling when dropping windows on screen edges</short>
|
||||
<long>
|
||||
If enabled, dropping windows on vertical screen edges maximizes them
|
||||
If enabled, dropping windows on screen edges maximizes them
|
||||
vertically and resizes them horizontally to cover half of the
|
||||
available area. Dropping windows on the top screen edge maximizes them
|
||||
completely.
|
||||
available area.
|
||||
|
||||
This key overrides /apps/metacity/general/edge_tiling when
|
||||
This key overrides /apps/metacity/general/side_by_side_tiling when
|
||||
running GNOME Shell.
|
||||
</long>
|
||||
</locale>
|
||||
@ -81,20 +80,5 @@
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
<schema>
|
||||
<key>/schemas/desktop/gnome/shell/windows/workspaces_only_on_primary</key>
|
||||
<applyto>/desktop/gnome/shell/windows/workspaces_only_on_primary</applyto>
|
||||
<owner>gnome-shell</owner>
|
||||
<type>bool</type>
|
||||
<default>true</default>
|
||||
<locale name="C">
|
||||
<short>Workspaces only on primary monitor</short>
|
||||
<long>
|
||||
This key overrides /apps/mutter/general/workspaces_only_on_primary when
|
||||
running GNOME Shell.
|
||||
</long>
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
</schemalist>
|
||||
</gconfschemafile>
|
||||
|
45
data/gs-applications.menu
Normal file
@ -0,0 +1,45 @@
|
||||
<Menu>
|
||||
<DefaultLayout>
|
||||
<Menuname>Apps</Menuname>
|
||||
<Menuname>Games</Menuname>
|
||||
<Menuname>Tools</Menuname>
|
||||
</DefaultLayout>
|
||||
<Name>Applications</Name>
|
||||
<AppDir>/usr/local/share/applications</AppDir>
|
||||
<DefaultAppDirs/>
|
||||
<Menu>
|
||||
<Name>Games</Name>
|
||||
<Include>
|
||||
<And>
|
||||
<Category>Game</Category>
|
||||
</And>
|
||||
</Include>
|
||||
</Menu>
|
||||
<Menu>
|
||||
<Name>Tools</Name>
|
||||
<Include>
|
||||
<Category>Development</Category>
|
||||
<And>
|
||||
<Category>System</Category>
|
||||
<Not>
|
||||
<Category>Settings</Category>
|
||||
</Not>
|
||||
</And>
|
||||
<Category>Utility</Category>
|
||||
</Include>
|
||||
</Menu>
|
||||
<Menu>
|
||||
<Name>Apps</Name>
|
||||
<OnlyUnallocated/>
|
||||
<Include>
|
||||
<And>
|
||||
<Or>
|
||||
<Category>Documentation</Category>
|
||||
<Not><Category>Core</Category></Not>
|
||||
</Or>
|
||||
<Not><Category>Settings</Category></Not>
|
||||
<Not><Category>Screensaver</Category></Not>
|
||||
</And>
|
||||
</Include>
|
||||
</Menu>
|
||||
</Menu>
|
80
data/magnifier.svg
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.0"
|
||||
id="Foreground"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
enable-background="new 0 0 29 18"
|
||||
xml:space="preserve"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.46+devel"
|
||||
sodipodi:docname="magnifier.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
||||
id="metadata16"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs14"><inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 9 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="29 : 9 : 1"
|
||||
inkscape:persp3d-origin="14.5 : 6 : 1"
|
||||
id="perspective18" /></defs><sodipodi:namedview
|
||||
inkscape:window-height="728"
|
||||
inkscape:window-width="1103"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="1"
|
||||
guidetolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
objecttolerance="10.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#000000"
|
||||
id="base"
|
||||
showgrid="true"
|
||||
inkscape:zoom="27.260185"
|
||||
inkscape:cx="9.5844061"
|
||||
inkscape:cy="9.4435574"
|
||||
inkscape:window-x="142"
|
||||
inkscape:window-y="26"
|
||||
inkscape:current-layer="Foreground"
|
||||
inkscape:snap-global="true"
|
||||
showguides="false"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2391"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" /></sodipodi:namedview>
|
||||
|
||||
<g
|
||||
id="g5"
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
transform="translate(-4,-0.023114)">
|
||||
<path
|
||||
d="m 6.246,13.98 c -0.319,-0.319 -0.319,-0.837 0,-1.157 L 9.963,9.106 c 0.319,-0.319 0.837,-0.319 1.157,0 l 0.786,0.787 c 0.32,0.319 0.32,0.837 0,1.157 l -3.717,3.717 c -0.32,0.319 -0.838,0.319 -1.157,0 l -0.786,-0.787 0,0 z"
|
||||
id="path7"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
<path
|
||||
d="M 9.076,11.937"
|
||||
id="path9"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
</g>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="m 7.25,7.476886 c 0,-1.243 1.007,-2.25 2.2499998,-2.25 1.2430002,0 2.2500002,1.007 2.2500002,2.25 0,1.243 -1.007,2.25 -2.2500002,2.25 C 8.257,9.726886 7.25,8.719886 7.25,7.476886 z m -2.25,0 c 0,-2.485 2.015,-4.5 4.4999998,-4.5 2.4850002,0 4.5000002,2.015 4.5000002,4.5 0,2.4849998 -2.015,4.5 -4.5000002,4.5 C 7.015,11.976886 5,9.9618858 5,7.476886 z"
|
||||
id="path11"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
133
data/org.gnome.accessibility.magnifier.gschema.xml.in
Normal file
@ -0,0 +1,133 @@
|
||||
<schemalist>
|
||||
|
||||
<enum id="MouseTrackingMode">
|
||||
<value nick="none" value="0"/>
|
||||
<value nick="centered" value="1"/>
|
||||
<value nick="proportional" value="2"/>
|
||||
<value nick="push" value="3"/>
|
||||
</enum>
|
||||
|
||||
<enum id="ScreenPosition">
|
||||
<value nick="none" value="0"/>
|
||||
<value nick="full-screen" value="1"/>
|
||||
<value nick="top-half" value="2"/>
|
||||
<value nick="bottom-half" value="3"/>
|
||||
<value nick="left-half" value="4"/>
|
||||
<value nick="right-half" value="5"/>
|
||||
</enum>
|
||||
|
||||
<schema id="org.gnome.accessibility.magnifier"
|
||||
path="/desktop/gnome/accessibility/magnifier/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="show-magnifier" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show or hide the magnifier</_summary>
|
||||
<_description>
|
||||
Show or hide the magnifier and all of its zoom regions.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="mouse-tracking" enum="MouseTrackingMode">
|
||||
<default>'proportional'</default>
|
||||
<_summary>Mouse Tracking Mode</_summary>
|
||||
<_description>
|
||||
Determines the position of the magnified mouse image within the
|
||||
magnified view and how it reacts to system mouse movement. The values
|
||||
are
|
||||
- none: no mouse tracking;
|
||||
- centered: the mouse image is
|
||||
displayed at the center of the zoom region (which also represents
|
||||
the point under the system mouse) and the magnified contents are
|
||||
scrolled as the system mouse moves;
|
||||
- proportional: the position of the magnified mouse in the zoom region
|
||||
is proportionally the same as the position of the system mouse on screen;
|
||||
- push: when the magnified mouse intersects a boundary of the zoom
|
||||
region, the contents are scrolled into view.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="screen-position" enum="ScreenPosition">
|
||||
<default>'full-screen'</default>
|
||||
<_summary>Screen position</_summary>
|
||||
<_description>
|
||||
The magnified view either fills the entire screen, or occupies the
|
||||
top-half, bottom-half, left-half, or right-half of the screen.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="mag-factor" type="d">
|
||||
<default>2.0</default>
|
||||
<_summary>Magnification factor</_summary>
|
||||
<_description>
|
||||
The power of the magnification. A value of 1.0 means no magnification.
|
||||
A value of 2.0 doubles the size.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="lens-mode" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Enable lens mode</_summary>
|
||||
<_description>
|
||||
Whether the magnified view should be centered over the location of
|
||||
the system mouse and move with it.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="scroll-at-edges" type="b">
|
||||
<default>false</default>
|
||||
<_summary>
|
||||
Scroll magnified contents beyond the edges of the desktop
|
||||
</_summary>
|
||||
<_description>
|
||||
For centered mouse tracking, when the system pointer is at or near the
|
||||
edge of the screen, the magnified contents continue to scroll such that
|
||||
the screen edge moves into the magnified view.
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<!-- Cross-hairs -->
|
||||
<key name="show-cross-hairs" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show or hide crosshairs</_summary>
|
||||
<_description>
|
||||
Enables/disables display of crosshairs centered on the magnified
|
||||
mouse sprite.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="cross-hairs-thickness" type="i">
|
||||
<default>8</default>
|
||||
<_summary>Thickness of the crosshairs</_summary>
|
||||
<_description>
|
||||
Width of the vertical and horizontal lines that make up the crosshairs.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="cross-hairs-color" type="s">
|
||||
<default>'#ff0000'</default>
|
||||
<_summary>Color of the crosshairs</_summary>
|
||||
<_description>
|
||||
The color of the the vertical and horizontal lines that make up
|
||||
the crosshairs.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="cross-hairs-opacity" type="i">
|
||||
<default>169</default>
|
||||
<_summary>Opacity of the crosshairs</_summary>
|
||||
<_description>
|
||||
Determines the transparency of the crosshairs, from fully opaque
|
||||
to fully transparent.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="cross-hairs-length" type="i">
|
||||
<default>4096</default>
|
||||
<_summary>Length of the crosshairs</_summary>
|
||||
<_description>
|
||||
Determines the length of the vertical and horizontal lines that
|
||||
make up the crosshairs.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="cross-hairs-clip" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Clip the crosshairs at the center</_summary>
|
||||
<_description>
|
||||
Determines whether the crosshairs intersect the magnified mouse sprite,
|
||||
or are clipped such that the ends of the horizontal and vertical lines
|
||||
surround the mouse image.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
@ -1,5 +1,5 @@
|
||||
<schemalist>
|
||||
<schema id="org.gnome.shell" path="/org/gnome/shell/"
|
||||
<schema id="org.gnome.shell" path="/apps/gnome-shell/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="development-tools" type="b">
|
||||
<default>true</default>
|
||||
@ -30,31 +30,23 @@
|
||||
</_description>
|
||||
</key>
|
||||
<key name="favorite-apps" type="as">
|
||||
<default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'openoffice.org-writer.desktop', 'nautilus.desktop' ]</default>
|
||||
<default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'openoffice.org-writer.desktop' ]</default>
|
||||
<_summary>List of desktop file IDs for favorite applications</_summary>
|
||||
<_description>
|
||||
The applications corresponding to these identifiers
|
||||
will be displayed in the favorites area.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="disabled-open-search-providers" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>disabled OpenSearch providers</_summary>
|
||||
</key>
|
||||
<key name="command-history" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>History for command (Alt-F2) dialog</_summary>
|
||||
</key>
|
||||
<key name="looking-glass-history" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>History for the looking glass dialog</_summary>
|
||||
</key>
|
||||
<child name="clock" schema="org.gnome.shell.clock"/>
|
||||
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
||||
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
|
||||
<schema id="org.gnome.shell.calendar" path="/apps/gnome-shell/calendar/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="show-weekdate" type="b">
|
||||
<default>false</default>
|
||||
@ -65,25 +57,58 @@
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
|
||||
<schema id="org.gnome.shell.clock" path="/apps/gnome-shell/clock/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="format" type="s">
|
||||
<default l10n="messages" context="hour_format">
|
||||
<!-- TRANSLATORS: This is the default hour format, choose ONLY '12-hour' or '24-hour'. -->
|
||||
"12-hour"
|
||||
</default>
|
||||
<_summary>Hour format</_summary>
|
||||
<_description>
|
||||
This key specifies the hour format used by the panel clock.
|
||||
Possible values are "12-hour", "24-hour", "unix" and "custom". If set
|
||||
to "unix", the clock will display time in seconds since Epoch,
|
||||
i.e. 1970-01-01. If set to "custom", the clock will display time
|
||||
according to the format specified in the custom_format key. Note that
|
||||
if set to either "unix" or "custom", the show_date and show_seconds
|
||||
keys are ignored.
|
||||
</_description>
|
||||
<choices>
|
||||
<choice value="12-hour"/>
|
||||
<choice value="24-hour"/>
|
||||
<choice value="unix"/>
|
||||
<choice value="custom"/>
|
||||
</choices>
|
||||
</key>
|
||||
<key name="custom-format" type="s">
|
||||
<default>''</default>
|
||||
<_summary>Custom format of the clock</_summary>
|
||||
<_description>
|
||||
This key specifies the format used by the panel clock when the format
|
||||
key is set to "custom". You can use conversion specifiers understood
|
||||
by strftime() to obtain a specific format. See the strftime() manual
|
||||
for more information.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="show-seconds" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show time with seconds</_summary>
|
||||
<_description>
|
||||
If true, display seconds in time.
|
||||
If true and format is either "12-hour" or "24-hour", display seconds in time.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="show-date" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show date in clock</_summary>
|
||||
<_description>
|
||||
If true, display date in the clock, in addition to time.
|
||||
If true and format is either "12-hour" or "24-hour",
|
||||
display date in the clock, in addition to time.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
|
||||
<schema id="org.gnome.shell.recorder" path="/apps/gnome-shell/recorder/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="framerate" type="i">
|
||||
<default>15</default>
|
||||
@ -105,13 +130,11 @@
|
||||
take care of its own output - this might be used to send the output
|
||||
to an icecast server via shout2send or similar. When unset or set
|
||||
to an empty value, the default pipeline will be used. This is currently
|
||||
'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux'
|
||||
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
||||
for a guess at the optimal thread count on the system.
|
||||
'videorate ! theoraenc ! oggmux' and records to Ogg Theora.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="file-extension" type="s">
|
||||
<default>'webm'</default>
|
||||
<default>'ogv'</default>
|
||||
<_summary>File extension used for storing the screencast</_summary>
|
||||
<_description>
|
||||
The filename for recorded screencasts will be a unique filename
|
||||
|
@ -1,7 +0,0 @@
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>Google</ShortName>
|
||||
<Description>Google Search</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image>
|
||||
<Url type="text/html" method="GET" template="http://www.google.com/search?q={searchTerms}"/>
|
||||
</OpenSearchDescription>
|
@ -1,44 +0,0 @@
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>Wikipedia</ShortName>
|
||||
<Description>Wikipedia, the free encyclopedia</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAEAgQAhIOEAMjHyABIR0gA6ejpAGlqaQCpqKkAKCgoAPz9%2FAAZGBkAmJiYANjZ2ABXWFcAent6ALm6uQA8OjwAiIiIiIiIiIiIiI4oiL6IiIiIgzuIV4iIiIhndo53KIiIiB%2FWvXoYiIiIfEZfWBSIiIEGi%2FfoqoiIgzuL84i9iIjpGIoMiEHoiMkos3FojmiLlUipYliEWIF%2BiDe0GoRa7D6GPbjcu1yIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</Image>
|
||||
<Url type="text/html" method="GET" template="http://{language}.wikipedia.org/wiki/Special:Search?search={searchTerms}"/>
|
||||
<!-- The criterion for being below is being listed with more than 100,000
|
||||
articles on http://meta.wikimedia.org/wiki/List_of_Wikipedias -->
|
||||
<Language>ar</Language>
|
||||
<Language>bg</Language>
|
||||
<Language>ca</Language>
|
||||
<Language>cs</Language>
|
||||
<Language>da</Language>
|
||||
<Language>de</Language>
|
||||
<Language>en</Language>
|
||||
<Language>eo</Language>
|
||||
<Language>es</Language>
|
||||
<Language>fa</Language>
|
||||
<Language>fi</Language>
|
||||
<Language>fr</Language>
|
||||
<Language>he</Language>
|
||||
<Language>hu</Language>
|
||||
<Language>id</Language>
|
||||
<Language>it</Language>
|
||||
<Language>ja</Language>
|
||||
<Language>ko</Language>
|
||||
<Language>lt</Language>
|
||||
<Language>nl</Language>
|
||||
<Language>no</Language>
|
||||
<Language>pl</Language>
|
||||
<Language>pt</Language>
|
||||
<Language>ro</Language>
|
||||
<Language>ru</Language>
|
||||
<Language>sk</Language>
|
||||
<Language>sl</Language>
|
||||
<Language>sr</Language>
|
||||
<Language>sv</Language>
|
||||
<Language>tr</Language>
|
||||
<Language>uk</Language>
|
||||
<Language>vi</Language>
|
||||
<Language>vo</Language>
|
||||
<Language>war</Language>
|
||||
<Language>zh</Language>
|
||||
</OpenSearchDescription>
|
98
data/theme/add-workspace.svg
Normal file
@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="23"
|
||||
height="15"
|
||||
id="svg6375"
|
||||
version="1.1"
|
||||
inkscape:version="0.47pre4 r22446"
|
||||
sodipodi:docname="New document 13">
|
||||
<defs
|
||||
id="defs6377">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 16 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="32 : 16 : 1"
|
||||
inkscape:persp3d-origin="16 : 10.666667 : 1"
|
||||
id="perspective6383" />
|
||||
<inkscape:perspective
|
||||
id="perspective6366"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="11.197802"
|
||||
inkscape:cx="16"
|
||||
inkscape:cy="16"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="997"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata6380">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(0,-17)">
|
||||
<g
|
||||
style="display:inline"
|
||||
id="g6243"
|
||||
transform="translate(-986.28859,-658.2796)">
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:0.98770495;stroke:#666666;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
id="rect5318"
|
||||
width="22"
|
||||
height="14"
|
||||
x="986.89801"
|
||||
y="675.86743"
|
||||
rx="0.49999979"
|
||||
ry="0.5" />
|
||||
<g
|
||||
id="g5320"
|
||||
transform="translate(402.77304,-12.882544)">
|
||||
<path
|
||||
id="path5322"
|
||||
d="m 595.125,692.53048 0,6.43903"
|
||||
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
id="path5324"
|
||||
d="m 598.34451,695.75 -6.43902,0"
|
||||
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
@ -1,82 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="16"
|
||||
height="16"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48+devel r9942 custom"
|
||||
sodipodi:docname="New document 4">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="8.984481"
|
||||
inkscape:cy="5.6224906"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="930"
|
||||
inkscape:window-height="681"
|
||||
inkscape:window-x="1892"
|
||||
inkscape:window-y="272"
|
||||
inkscape:window-maximized="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid17403"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1036.3622)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.43015847;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
id="path18028"
|
||||
sodipodi:sides="3"
|
||||
sodipodi:cx="84.5"
|
||||
sodipodi:cy="337.5"
|
||||
sodipodi:r1="5"
|
||||
sodipodi:r2="2.5"
|
||||
sodipodi:arg1="0.52359878"
|
||||
sodipodi:arg2="1.5707963"
|
||||
inkscape:flatsided="true"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 88.830127,340 80.169873,340 84.5,332.5 z"
|
||||
transform="matrix(0,1.3621708,0.99186247,0,-325.48222,929.32667)" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
@ -1,82 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="16"
|
||||
height="16"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48+devel r9942 custom"
|
||||
sodipodi:docname="arrow-left.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="7.7366092"
|
||||
inkscape:cy="6.4536271"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="930"
|
||||
inkscape:window-height="681"
|
||||
inkscape:window-x="1892"
|
||||
inkscape:window-y="272"
|
||||
inkscape:window-maximized="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid17403"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1036.3622)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.43015847;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
id="path18028"
|
||||
sodipodi:sides="3"
|
||||
sodipodi:cx="84.5"
|
||||
sodipodi:cy="337.5"
|
||||
sodipodi:r1="5"
|
||||
sodipodi:r2="2.5"
|
||||
sodipodi:arg1="0.52359878"
|
||||
sodipodi:arg2="1.5707963"
|
||||
inkscape:flatsided="true"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 88.830127,340 80.169873,340 84.5,332.5 z"
|
||||
transform="matrix(0,1.3621708,-0.99186247,0,342.48324,929.32667)" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
@ -1,187 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="28"
|
||||
height="25"
|
||||
id="svg10621"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="calendar-today.svg">
|
||||
<defs
|
||||
id="defs10623">
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient99561-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient34508-1-3">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop34510-1-9" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop34512-4-5" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="42"
|
||||
fy="30"
|
||||
fx="51"
|
||||
cy="30"
|
||||
cx="51"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient10592"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3770"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3007"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3067"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3072"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient2997"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="8.3750933"
|
||||
inkscape:cy="8.0837211"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata10626">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-469.08263,-536.99307)">
|
||||
<g
|
||||
id="g3003">
|
||||
<path
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||
transform="matrix(0.43692393,0,0,1.3783114,460.60467,517.48289)"
|
||||
sodipodi:end="6.2831853"
|
||||
sodipodi:start="3.1415927"
|
||||
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="42"
|
||||
sodipodi:cy="30"
|
||||
sodipodi:cx="51"
|
||||
id="path34506-3"
|
||||
style="opacity:0.4625;color:#000000;fill:url(#radialGradient2997);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:type="arc" />
|
||||
<rect
|
||||
y="558.85046"
|
||||
x="468.96878"
|
||||
height="3.1425927"
|
||||
width="28.149134"
|
||||
id="rect2996"
|
||||
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 5.7 KiB |
@ -1,26 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.0"
|
||||
id="Foreground"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 23.272727 23.272727"
|
||||
width="22"
|
||||
height="22"
|
||||
viewBox="0 0 16 16"
|
||||
enable-background="new 0 0 16 16"
|
||||
xml:space="preserve"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.48+devel r10081 custom"
|
||||
inkscape:version="0.46"
|
||||
sodipodi:docname="close-window.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
||||
id="metadata2399"><rdf:RDF><cc:Work
|
||||
@ -39,49 +37,11 @@
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="16 : 8 : 1"
|
||||
inkscape:persp3d-origin="8 : 5.3333333 : 1"
|
||||
id="perspective2401" /><filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter16494-4"
|
||||
x="-0.20989846"
|
||||
width="1.4197969"
|
||||
y="-0.20903821"
|
||||
height="1.4180764"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.3282637"
|
||||
id="feGaussianBlur16496-8" /></filter><radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient16498-6"
|
||||
id="radialGradient16504-1"
|
||||
cx="7.6582627"
|
||||
cy="5.8191104"
|
||||
fx="7.6582627"
|
||||
fy="5.8191104"
|
||||
r="8.6928644"
|
||||
gradientTransform="matrix(1.0474339,0,0,1.0517402,-0.3632615,-0.42032492)"
|
||||
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient16498-6"><stop
|
||||
style="stop-color:#7b7b7b;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop16500-8" /><stop
|
||||
style="stop-color:#101010;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop16502-0" /></linearGradient><filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter16524-9"
|
||||
x="-0.212979"
|
||||
width="1.425958"
|
||||
y="-0.21305652"
|
||||
height="1.426113"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.71020915"
|
||||
id="feGaussianBlur16526-0" /></filter></defs><sodipodi:namedview
|
||||
inkscape:window-height="1114"
|
||||
inkscape:window-width="1463"
|
||||
id="perspective2401" /></defs><sodipodi:namedview
|
||||
inkscape:window-height="999"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageopacity="1"
|
||||
guidetolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
objecttolerance="10.0"
|
||||
@ -90,63 +50,27 @@
|
||||
pagecolor="#000000"
|
||||
id="base"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="10.720189"
|
||||
inkscape:cy="13.739577"
|
||||
inkscape:zoom="25.648691"
|
||||
inkscape:cx="8.8097603"
|
||||
inkscape:cy="9.0472789"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:current-layer="Foreground"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-maximized="0"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid11246"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" /></sodipodi:namedview>
|
||||
inkscape:guide-bbox="true" />
|
||||
|
||||
<g
|
||||
style="display:inline"
|
||||
id="g16402-8"
|
||||
transform="translate(4.7533483,2.8238929)"><g
|
||||
id="g3175-4"><path
|
||||
sodipodi:type="inkscape:offset"
|
||||
inkscape:radius="0"
|
||||
inkscape:original="M 7.65625 0.125 C 3.2589349 0.125 -0.3125 3.7070002 -0.3125 8.125 C -0.3125 12.543001 3.2589349 16.125 7.65625 16.125 C 12.053566 16.125 15.625 12.543001 15.625 8.125 C 15.625 3.7070002 12.053566 0.125 7.65625 0.125 z "
|
||||
xlink:href="#path2394-32"
|
||||
style="opacity:0.52994014;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.18181825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16494-4);enable-background:accumulate"
|
||||
id="path16480-5"
|
||||
inkscape:href="#path2394-32"
|
||||
d="m 7.65625,0.125 c -4.3973151,0 -7.96875,3.5820002 -7.96875,8 0,4.418001 3.5714349,8 7.96875,8 4.397316,0 7.96875,-3.581999 7.96875,-8 0,-4.4179998 -3.571434,-8 -7.96875,-8 z"
|
||||
transform="translate(0,1.028519)" /><path
|
||||
clip-rule="evenodd"
|
||||
d="m -0.30428257,8.1237596 c 0,-4.4179998 3.56522987,-7.9999996 7.96254497,-7.9999996 4.3973156,0 7.9625456,3.5819998 7.9625456,7.9999996 0,4.4180014 -3.56523,8.0000004 -7.9625456,8.0000004 -4.3973151,0 -7.96254497,-3.581999 -7.96254497,-8.0000004 z"
|
||||
id="path2394-32"
|
||||
style="color:#000000;fill:url(#radialGradient16504-1);fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.4545455;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:nodetypes="csssc"
|
||||
inkscape:connector-curvature="0" /><g
|
||||
id="g3172-6" /></g><g
|
||||
transform="matrix(0.72727273,0,0,0.72727273,2.368236,2.1803254)"
|
||||
style="fill:#ffffff;fill-opacity:1;display:inline"
|
||||
id="g27275-6-6"
|
||||
inkscape:label="window-close"><g
|
||||
style="fill:#ffffff;fill-opacity:1;display:inline"
|
||||
id="g27277-1-1"
|
||||
transform="translate(-41,-760)"><path
|
||||
sodipodi:type="inkscape:offset"
|
||||
inkscape:radius="0"
|
||||
inkscape:original="M 44.21875 764.1875 L 44.21875 765.1875 C 44.19684 765.46825 44.289258 765.74287 44.5 765.9375 L 46.78125 768.21875 L 44.5 770.46875 C 44.31181 770.65692 44.218747 770.92221 44.21875 771.1875 L 44.21875 772.1875 L 45.21875 772.1875 C 45.48404 772.1875 45.749336 772.09444 45.9375 771.90625 L 48.21875 769.625 L 50.5 771.90625 C 50.688164 772.0944 50.953449 772.18749 51.21875 772.1875 L 52.21875 772.1875 L 52.21875 771.1875 C 52.218742 770.9222 52.125688 770.65692 51.9375 770.46875 L 49.6875 768.21875 L 51.96875 765.9375 C 52.18441 765.73815 52.21875 765.47397 52.21875 765.1875 L 52.21875 764.1875 L 51.21875 764.1875 C 50.977922 764.1945 50.796875 764.2695 50.53125 764.5 L 48.21875 766.78125 L 45.9375 764.5 C 45.75987 764.31608 45.504951 764.1987 45.25 764.1875 C 45.23954 764.18704 45.22912 764.18738 45.21875 764.1875 L 44.21875 764.1875 z "
|
||||
xlink:href="#path27279-0-5"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;color:#bebebe;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.78124988;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16524-9);enable-background:new;font-family:Andale Mono;-inkscape-font-specification:Andale Mono"
|
||||
id="path16506-5"
|
||||
inkscape:href="#path27279-0-5"
|
||||
d="m 44.21875,764.1875 0,1 c -0.02191,0.28075 0.07051,0.55537 0.28125,0.75 l 2.28125,2.28125 -2.28125,2.25 c -0.18819,0.18817 -0.281253,0.45346 -0.28125,0.71875 l 0,1 1,0 c 0.26529,0 0.530586,-0.0931 0.71875,-0.28125 L 48.21875,769.625 50.5,771.90625 c 0.188164,0.18815 0.453449,0.28124 0.71875,0.28125 l 1,0 0,-1 c -8e-6,-0.2653 -0.09306,-0.53058 -0.28125,-0.71875 l -2.25,-2.25 2.28125,-2.28125 c 0.21566,-0.19935 0.25,-0.46353 0.25,-0.75 l 0,-1 -1,0 c -0.240828,0.007 -0.421875,0.082 -0.6875,0.3125 l -2.3125,2.28125 L 45.9375,764.5 c -0.17763,-0.18392 -0.432549,-0.3013 -0.6875,-0.3125 -0.01046,-4.6e-4 -0.02088,-1.2e-4 -0.03125,0 l -1,0 z"
|
||||
transform="translate(0,1.3535534)" /><path
|
||||
sodipodi:nodetypes="ccsccccccccccccccccccccccc"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;color:#bebebe;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.78124988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new;font-family:Andale Mono;-inkscape-font-specification:Andale Mono"
|
||||
id="path27279-0-5"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 44.226475,764.17222 1,0 c 0.01037,-1.2e-4 0.02079,-4.6e-4 0.03125,0 0.254951,0.0112 0.50987,0.12858 0.6875,0.3125 l 2.28125,2.28125 2.3125,-2.28125 c 0.265625,-0.2305 0.446672,-0.3055 0.6875,-0.3125 l 1,0 0,1 c 0,0.28647 -0.03434,0.55065 -0.25,0.75 l -2.28125,2.28125 2.25,2.25 c 0.188188,0.18817 0.281242,0.45345 0.28125,0.71875 l 0,1 -1,0 c -0.265301,-1e-5 -0.530586,-0.0931 -0.71875,-0.28125 l -2.28125,-2.28125 -2.28125,2.28125 c -0.188164,0.18819 -0.45346,0.28125 -0.71875,0.28125 l -1,0 0,-1 c -3e-6,-0.26529 0.09306,-0.53058 0.28125,-0.71875 l 2.28125,-2.25 -2.28125,-2.28125 c -0.210742,-0.19463 -0.30316,-0.46925 -0.28125,-0.75 l 0,-1 z" /></g></g></g></svg>
|
||||
id="g3175"><path
|
||||
sodipodi:nodetypes="csssc"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.59217799;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path2394"
|
||||
d="M 0.83987936,8.0425327 C 0.83987936,4.0805265 4.0712155,0.86823453 8.0567103,0.86823453 C 12.042205,0.86823453 15.273542,4.0805265 15.273542,8.0425327 C 15.273542,12.004539 12.042205,15.216831 8.0567103,15.216831 C 4.0712155,15.216831 0.83987936,12.004539 0.83987936,8.0425327 z"
|
||||
clip-rule="evenodd" /><g
|
||||
id="g3172"><path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.67127273;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 5.4242673,5.3313047 L 10.515414,10.421272 L 10.714004,10.646491"
|
||||
id="path3152" /></g></g><path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.67127273;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 5.4402527,10.650392 L 10.688082,5.3573033"
|
||||
id="path3154"
|
||||
sodipodi:nodetypes="cc" /></svg>
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
222
data/theme/dialog-error.svg
Normal file
@ -0,0 +1,222 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="24"
|
||||
id="svg4908"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="dialog-error.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
inkscape:export-filename="/home/andreas/project/gnome-icon-theme/scalable/actions/process-stop.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
version="1.0">
|
||||
<defs
|
||||
id="defs4910">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 24 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="48 : 24 : 1"
|
||||
inkscape:persp3d-origin="24 : 16 : 1"
|
||||
id="perspective25" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(1.349881,0,0,1.349881,-3.498814,-1.810859)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="9.7183542"
|
||||
fy="4.9892726"
|
||||
fx="9.6893959"
|
||||
cy="4.9892726"
|
||||
cx="9.6893959"
|
||||
id="radialGradient5177"
|
||||
xlink:href="#linearGradient5171"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.417917,0,0,2.417917,-14.17917,-4.903184)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="9.7785711"
|
||||
fy="3.458019"
|
||||
fx="10"
|
||||
cy="3.458019"
|
||||
cx="10"
|
||||
id="radialGradient5157"
|
||||
xlink:href="#linearGradient5151"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.928125,0,0,0.3143011,0.7718789,12.358015)"
|
||||
r="9.0598059"
|
||||
fy="18.022524"
|
||||
fx="10.739184"
|
||||
cy="18.022524"
|
||||
cx="10.739184"
|
||||
id="radialGradient5145"
|
||||
xlink:href="#linearGradient5139"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient5139"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop5141"
|
||||
offset="0"
|
||||
style="stop-color:black;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop5143"
|
||||
offset="1"
|
||||
style="stop-color:black;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5151"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop5153"
|
||||
offset="0"
|
||||
style="stop-color:white;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop5155"
|
||||
offset="1"
|
||||
style="stop-color:white;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5171">
|
||||
<stop
|
||||
id="stop5173"
|
||||
offset="0"
|
||||
style="stop-color:#fe3a00;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop5175"
|
||||
offset="1"
|
||||
style="stop-color:#c00;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="24.442987"
|
||||
inkscape:cy="10.142308"
|
||||
inkscape:current-layer="g7001"
|
||||
showgrid="false"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1674"
|
||||
inkscape:window-height="970"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
width="48px"
|
||||
height="48px"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata4913">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Stop Process</dc:title>
|
||||
<dc:date>December 2006</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Jakub Steiner</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>Andreas Nilsson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>stop</rdf:li>
|
||||
<rdf:li>halt</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Notice" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/ShareAlike" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/SourceCode" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(0,-24)">
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
id="g7001"
|
||||
transform="matrix(1.4566048,0,0,1.4455352,0.4112881,1.2324709)">
|
||||
<path
|
||||
transform="matrix(0.91468137,0,0,0.70055266,-1.8812476,17.474032)"
|
||||
d="m 19.79899,18.022524 a 9.0598059,3.0935922 0 1 1 -18.1196115,0 9.0598059,3.0935922 0 1 1 18.1196115,0 z"
|
||||
sodipodi:ry="3.0935922"
|
||||
sodipodi:rx="9.0598059"
|
||||
sodipodi:cy="18.022524"
|
||||
sodipodi:cx="10.739184"
|
||||
id="path5137"
|
||||
style="color:#000000;fill:url(#radialGradient5145);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.87347736,0,0,0.83068052,-0.79308842,15.602788)"
|
||||
d="m 19.25,9.625 a 9.25,9.25 0 1 1 -18.5,0 9.25,9.25 0 1 1 18.5,0 z"
|
||||
sodipodi:ry="9.25"
|
||||
sodipodi:rx="9.25"
|
||||
sodipodi:cy="9.625"
|
||||
sodipodi:cx="10"
|
||||
id="path4262"
|
||||
style="color:#000000;fill:url(#radialGradient5177);fill-opacity:1;fill-rule:nonzero;stroke:#a40000;stroke-width:0.47435912;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:0.35393258;color:#000000;fill:none;stroke:url(#radialGradient5157);stroke-width:0.49999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="path5149"
|
||||
sodipodi:cx="10"
|
||||
sodipodi:cy="9.625"
|
||||
sodipodi:rx="9.25"
|
||||
sodipodi:ry="9.25"
|
||||
d="m 19.25,9.625 a 9.25,9.25 0 1 1 -18.5,0 9.25,9.25 0 1 1 18.5,0 z"
|
||||
transform="matrix(0.82868359,0,0,0.78808147,-0.34515141,16.012803)" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
id="path5159"
|
||||
d="m 4.834121,20.642783 6.215127,5.91061"
|
||||
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.21219134;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
<path
|
||||
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.21219146;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="M 11.04925,20.622826 4.8159529,26.553393"
|
||||
id="path5161"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.0 KiB |
@ -1,81 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="10"
|
||||
height="20"
|
||||
id="svg10003"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="filter-selected-ltr.svg">
|
||||
<defs
|
||||
id="defs10005">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective10011" />
|
||||
<inkscape:perspective
|
||||
id="perspective9998"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.5"
|
||||
inkscape:cx="32.363636"
|
||||
inkscape:cy="10.181818"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="839"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata10008">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(0,-44)">
|
||||
<path
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/app-picker.png"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="rect34320"
|
||||
d="m 10.369085,54.181804 -10.55634072,10.55636 -1e-5,-21.11269 z"
|
||||
style="opacity:0.21000001;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.7 KiB |
@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="3"
|
||||
height="10"
|
||||
id="svg2"
|
||||
version="1.1">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.43599999000000000;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect3779"
|
||||
width="3"
|
||||
height="10"
|
||||
x="0"
|
||||
y="0" />
|
||||
<rect
|
||||
style="fill:#536272;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect3796"
|
||||
width="3"
|
||||
height="1"
|
||||
x="0"
|
||||
y="9" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 787 B |
@ -1,74 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="21"
|
||||
height="10"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="panel-button-border.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="44.8"
|
||||
inkscape:cx="8.6594891"
|
||||
inkscape:cy="5.7029946"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1"
|
||||
guidetolerance="10000"
|
||||
objecttolerance="10000">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3792"
|
||||
empspacing="10"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect3796"
|
||||
width="3"
|
||||
height="2"
|
||||
x="9"
|
||||
y="8" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.0 KiB |
@ -1,111 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="30"
|
||||
height="25"
|
||||
id="svg10621"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="panel-button-highlight-narrow.svg">
|
||||
<defs
|
||||
id="defs10623">
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient99561-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient34508-1-3">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop34510-1-9" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop34512-4-5" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="42"
|
||||
fy="30"
|
||||
fx="51"
|
||||
cy="30"
|
||||
cx="51"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient10592"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
inkscape:collect="always" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.979899"
|
||||
inkscape:cx="-171.36384"
|
||||
inkscape:cy="-53.255157"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata10626">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-468.08632,-537.03477)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:0.4625;color:#000000;fill:url(#radialGradient10592);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path34506-3"
|
||||
sodipodi:cx="51"
|
||||
sodipodi:cy="30"
|
||||
sodipodi:rx="42"
|
||||
sodipodi:ry="16"
|
||||
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
|
||||
sodipodi:start="3.1415927"
|
||||
sodipodi:end="6.2831853"
|
||||
transform="matrix(0.35714286,0,0,1.5625,464.87203,515.15977)"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.5 KiB |
@ -1,111 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="84"
|
||||
height="25"
|
||||
id="svg10621"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="panel-button-highlight-wide.svg">
|
||||
<defs
|
||||
id="defs10623">
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient99561-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient34508-1-3">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop34510-1-9" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop34512-4-5" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="42"
|
||||
fy="30"
|
||||
fx="51"
|
||||
cy="30"
|
||||
cx="51"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient10592"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
inkscape:collect="always" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.979899"
|
||||
inkscape:cx="-118.50071"
|
||||
inkscape:cy="27.304508"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata10626">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-441.08632,-537.03477)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:0.4625;color:#000000;fill:url(#radialGradient10592);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path34506-3"
|
||||
sodipodi:cx="51"
|
||||
sodipodi:cy="30"
|
||||
sodipodi:rx="42"
|
||||
sodipodi:ry="16"
|
||||
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
|
||||
sodipodi:start="3.1415927"
|
||||
sodipodi:end="6.2831853"
|
||||
transform="matrix(1,0,0,1.5625,432.08632,515.15977)"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.5 KiB |
BIN
data/theme/process-working.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
@ -1,261 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg5369"
|
||||
version="1.1"
|
||||
inkscape:version="0.48+devel r10053 custom"
|
||||
width="96"
|
||||
height="48"
|
||||
sodipodi:docname="process-working.svg"
|
||||
style="display:inline">
|
||||
<metadata
|
||||
id="metadata5375">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs5373" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#808080"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1975"
|
||||
inkscape:window-height="1098"
|
||||
id="namedview5371"
|
||||
showgrid="true"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:zoom="16"
|
||||
inkscape:cx="53.997662"
|
||||
inkscape:cy="22.367695"
|
||||
inkscape:window-x="1600"
|
||||
inkscape:window-y="33"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer2">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid11933"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
inkscape:label="tiles"
|
||||
style="display:none">
|
||||
<rect
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="rect12451"
|
||||
width="24"
|
||||
height="24"
|
||||
x="0"
|
||||
y="0" />
|
||||
<rect
|
||||
y="24"
|
||||
x="0"
|
||||
height="24"
|
||||
width="24"
|
||||
id="rect12453"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<rect
|
||||
y="0"
|
||||
x="24"
|
||||
height="24"
|
||||
width="24"
|
||||
id="rect12455"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<rect
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="rect12457"
|
||||
width="24"
|
||||
height="24"
|
||||
x="24"
|
||||
y="24" />
|
||||
<rect
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="rect12459"
|
||||
width="24"
|
||||
height="24"
|
||||
x="48"
|
||||
y="0" />
|
||||
<rect
|
||||
y="24"
|
||||
x="48"
|
||||
height="24"
|
||||
width="24"
|
||||
id="rect12461"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<rect
|
||||
y="0"
|
||||
x="72"
|
||||
height="24"
|
||||
width="24"
|
||||
id="rect12463"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<rect
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="rect12465"
|
||||
width="24"
|
||||
height="24"
|
||||
x="72"
|
||||
y="24" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="spinner">
|
||||
<g
|
||||
transform="matrix(0.28240106,0,0,0.28240106,146.92015,-382.52444)"
|
||||
id="g10450-5"
|
||||
style="display:inline">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:0.6;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="m -477.76072,1373.3569 0,9.4717"
|
||||
id="path18768"
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:transform-center-y="-4.6808838" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:transform-center-y="-3.3099227"
|
||||
sodipodi:nodetypes="cc"
|
||||
id="path18770"
|
||||
d="m -461.0171,1380.2922 -7.23427,7.3824"
|
||||
style="opacity:0.7;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
inkscape:transform-center-x="-3.3098966" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:transform-center-x="-4.6808962"
|
||||
style="opacity:0.8;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="m -454.08163,1397.0359 -9.47165,0"
|
||||
id="path18772"
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:transform-center-y="-2.6596956e-05" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc"
|
||||
id="path18774"
|
||||
d="m -461.01709,1413.7796 -6.93831,-7.0864"
|
||||
style="opacity:0.9;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
inkscape:transform-center-x="-3.3098966"
|
||||
inkscape:transform-center-y="3.3098652" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:transform-center-y="4.6808757"
|
||||
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="m -477.76074,1420.715 9e-5,-9.4716"
|
||||
id="path18776"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc"
|
||||
id="path18778"
|
||||
d="m -494.50442,1413.7796 6.79048,-6.9384"
|
||||
style="opacity:0.3;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
inkscape:transform-center-y="3.3098769"
|
||||
inkscape:transform-center-x="3.3098883" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:transform-center-x="4.6808941"
|
||||
style="opacity:0.4;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="m -501.43987,1397.0359 9.47174,0"
|
||||
id="path18780"
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:transform-center-y="-2.6596956e-05" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc"
|
||||
id="path18782"
|
||||
d="m -494.5044,1380.2922 6.64243,6.9384"
|
||||
style="opacity:0.5;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
inkscape:transform-center-x="3.3098902"
|
||||
inkscape:transform-center-y="-3.3099302" />
|
||||
</g>
|
||||
<use
|
||||
style="display:inline"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g10450-5"
|
||||
id="use4981"
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,36,-4.9705636)"
|
||||
width="400"
|
||||
height="400" />
|
||||
<use
|
||||
style="display:inline"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use4981"
|
||||
id="use4983"
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,43.032478,-21.909695)"
|
||||
width="400"
|
||||
height="400" />
|
||||
<use
|
||||
style="display:inline"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use4983"
|
||||
id="use4985"
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,50.081986,-38.904617)"
|
||||
width="400"
|
||||
height="400" />
|
||||
<use
|
||||
style="display:inline"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use4985"
|
||||
id="use4987"
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,-38.919996,-31.872139)"
|
||||
width="400"
|
||||
height="400" />
|
||||
<use
|
||||
style="display:inline"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use4987"
|
||||
id="use4989"
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,52.986628,2.0890543)"
|
||||
width="400"
|
||||
height="400" />
|
||||
<use
|
||||
style="display:inline"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use4989"
|
||||
id="use4991"
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,60.013026,-14.912936)"
|
||||
width="400"
|
||||
height="400" />
|
||||
<use
|
||||
style="display:inline"
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#use4991"
|
||||
id="use4993"
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,67.022396,-31.859127)"
|
||||
width="400"
|
||||
height="400" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 9.8 KiB |
@ -9,23 +9,23 @@
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="10"
|
||||
height="20"
|
||||
id="svg10003"
|
||||
width="23"
|
||||
height="15"
|
||||
id="svg5501"
|
||||
version="1.1"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="filter-selected.svg">
|
||||
inkscape:version="0.47pre4 r22446"
|
||||
sodipodi:docname="add-workspace.svg">
|
||||
<defs
|
||||
id="defs10005">
|
||||
id="defs5503">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_x="0 : 16 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective10011" />
|
||||
inkscape:vp_z="32 : 16 : 1"
|
||||
inkscape:persp3d-origin="16 : 10.666667 : 1"
|
||||
id="perspective5509" />
|
||||
<inkscape:perspective
|
||||
id="perspective9998"
|
||||
id="perspective5314"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
@ -39,27 +39,29 @@
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.5"
|
||||
inkscape:cx="32"
|
||||
inkscape:cy="10.181818"
|
||||
inkscape:zoom="11.197802"
|
||||
inkscape:cx="-0.074583208"
|
||||
inkscape:cy="16"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="994"
|
||||
inkscape:window-height="997"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-grids="true"
|
||||
inkscape:snap-bbox="true" />
|
||||
<metadata
|
||||
id="metadata10008">
|
||||
id="metadata5506">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
@ -67,15 +69,24 @@
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(0,-44)">
|
||||
<path
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/app-picker.png"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="rect34320"
|
||||
d="m -0.18726572,54.181804 10.55634072,10.55636 10e-6,-21.11269 z"
|
||||
style="opacity:0.21000001;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
transform="translate(0,-17)">
|
||||
<g
|
||||
style="display:inline"
|
||||
id="g6239"
|
||||
transform="translate(-953.97989,-657.32287)">
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:0.98770495;stroke:#666666;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
id="rect5318-6"
|
||||
width="22"
|
||||
height="14"
|
||||
x="954.5"
|
||||
y="675"
|
||||
rx="0.49999979"
|
||||
ry="0.5" />
|
||||
<path
|
||||
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
d="m 968.71951,682 -6.43902,0"
|
||||
id="path5324-5" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.9 KiB |
@ -5,8 +5,8 @@
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="119.97824"
|
||||
height="119.97824"
|
||||
width="74.01342"
|
||||
height="74.006706"
|
||||
id="svg7355"
|
||||
version="1.1">
|
||||
<defs
|
||||
@ -66,7 +66,7 @@
|
||||
</defs>
|
||||
<g
|
||||
id="layer1"
|
||||
transform="matrix(1.6213276,0,0,1.6213276,-431.6347,-272.5745)">
|
||||
transform="translate(-266.21629,-168.11809)">
|
||||
<g
|
||||
style="display:inline"
|
||||
id="g30864"
|
||||
@ -83,8 +83,7 @@
|
||||
<path
|
||||
id="rect34520"
|
||||
d="m 84.506708,167.95508 c 6e-6,1.96759 -1.584022,3.55162 -3.551629,3.55163 l -65.910146,0 c -1.967608,-1e-5 -3.551648,-1.58402 -3.551643,-3.55164"
|
||||
style="opacity:0.2;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
style="opacity:0.2;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.6 KiB |
@ -1,74 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="21"
|
||||
height="10"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="source-button-border.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="44.8"
|
||||
inkscape:cx="8.704132"
|
||||
inkscape:cy="5.7029946"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="1145"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1"
|
||||
guidetolerance="10000"
|
||||
objecttolerance="10000">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3792"
|
||||
empspacing="10"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect3796"
|
||||
width="19"
|
||||
height="2"
|
||||
x="1"
|
||||
y="8" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.0 KiB |
96
data/theme/ws-switch-arrow-left.svg
Normal file
@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="96" height="96" id="svg25070" version="1.1" inkscape:version="0.47 r22583" sodipodi:docname="dark-arrow-larger.svg">
|
||||
<defs id="defs25072">
|
||||
<inkscape:perspective sodipodi:type="inkscape:persp3d" inkscape:vp_x="0 : 24 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="48 : 24 : 1" inkscape:persp3d-origin="24 : 16 : 1" id="perspective25078"/>
|
||||
<inkscape:perspective id="perspective24985" inkscape:persp3d-origin="0.5 : 0.33333333 : 1" inkscape:vp_z="1 : 0.5 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_x="0 : 0.5 : 1" sodipodi:type="inkscape:persp3d"/>
|
||||
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4034-0-4" id="linearGradient24957" gradientUnits="userSpaceOnUse" gradientTransform="translate(6)" x1="-86.552246" y1="185.439" x2="-83.37072" y2="197.31261"/>
|
||||
<linearGradient inkscape:collect="always" id="linearGradient4034-0-4">
|
||||
<stop style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" offset="0" id="stop4036-5-7"/>
|
||||
<stop style="stop-color: rgb(186, 189, 182); stop-opacity: 1;" offset="1" id="stop4038-9-6"/>
|
||||
</linearGradient>
|
||||
<filter id="filter24765" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix24767" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix24769" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4632-1-3-9-3-2" id="linearGradient24955" gradientUnits="userSpaceOnUse" gradientTransform="translate(-5)" x1="-74.520325" y1="169.06032" x2="-74.520325" y2="205.94189"/>
|
||||
<linearGradient id="linearGradient4632-1-3-9-3-2">
|
||||
<stop style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" offset="0" id="stop4634-1-8-3-9-0"/>
|
||||
<stop id="stop4636-1-9-9-8-8" offset="0.0274937" style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"/>
|
||||
<stop id="stop4638-8-3-9-6-6" offset="0.274937" style="stop-color: rgb(242, 242, 242); stop-opacity: 1;"/>
|
||||
<stop id="stop4640-8-5-7-8-9" offset="0.38707438" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
|
||||
<stop id="stop4642-5-41-9-6-9" offset="0.66528589" style="stop-color: rgb(217, 218, 216); stop-opacity: 1;"/>
|
||||
<stop id="stop4644-5-2-7-9-2" offset="0.76745707" style="stop-color: rgb(223, 224, 221); stop-opacity: 1;"/>
|
||||
<stop style="stop-color: rgb(240, 240, 240); stop-opacity: 1;" offset="1" id="stop4646-3-2-3-7-3"/>
|
||||
</linearGradient>
|
||||
<radialGradient inkscape:collect="always" xlink:href="#linearGradient4869-4-1" id="radialGradient24959" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)" cx="-33.412369" cy="185.74171" fx="-33.412369" fy="185.74171" r="2.3554697"/>
|
||||
<linearGradient id="linearGradient4869-4-1">
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4871-6-2"/>
|
||||
<stop id="stop4879-7-4" offset="0.31807542" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
|
||||
<stop id="stop4877-6-1" offset="0.74691135" style="stop-color: rgb(200, 201, 198); stop-opacity: 1;"/>
|
||||
<stop style="stop-color: rgb(211, 215, 207); stop-opacity: 1;" offset="1" id="stop4873-1-0"/>
|
||||
</linearGradient>
|
||||
<filter id="filter25011" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25013" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25015" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<radialGradient inkscape:collect="always" xlink:href="#linearGradient4869-4-0" id="radialGradient24961" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)" cx="-33.412369" cy="185.74171" fx="-33.412369" fy="185.74171" r="2.3554697"/>
|
||||
<linearGradient id="linearGradient4869-4-0">
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4871-6-8"/>
|
||||
<stop id="stop4879-7-5" offset="0.31807542" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
|
||||
<stop id="stop4877-6-5" offset="0.74691135" style="stop-color: rgb(200, 201, 198); stop-opacity: 1;"/>
|
||||
<stop style="stop-color: rgb(211, 215, 207); stop-opacity: 1;" offset="1" id="stop4873-1-4"/>
|
||||
</linearGradient>
|
||||
<filter id="filter25023" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25025" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25027" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4941" id="linearGradient24963" gradientUnits="userSpaceOnUse" x1="-39.858727" y1="184.61784" x2="-38.244785" y2="188.84898"/>
|
||||
<linearGradient inkscape:collect="always" id="linearGradient4941">
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4943"/>
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" offset="1" id="stop4945"/>
|
||||
</linearGradient>
|
||||
<filter id="filter25033" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25035" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25037" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4941-7" id="linearGradient24965" gradientUnits="userSpaceOnUse" x1="-39.858727" y1="184.61784" x2="-38.244785" y2="188.84898"/>
|
||||
<linearGradient inkscape:collect="always" id="linearGradient4941-7">
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4943-2"/>
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" offset="1" id="stop4945-5"/>
|
||||
</linearGradient>
|
||||
<filter id="filter25043" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25045" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25047" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="filter25049" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25051" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25053" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="filter25055" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25057" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25059" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="2.8284271" inkscape:cx="48.631638" inkscape:cy="57.536221" inkscape:current-layer="layer1" showgrid="true" inkscape:grid-bbox="true" inkscape:document-units="px" inkscape:window-width="1200" inkscape:window-height="851" inkscape:window-x="0" inkscape:window-y="52" inkscape:window-maximized="0"/>
|
||||
<metadata id="metadata25075">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer" transform="translate(0, 48)">
|
||||
<g id="g4030-1-8" transform="matrix(2, 0, 0, 2, 193.25, -374.967)" style="stroke: rgb(0, 0, 0); display: inline; stroke-opacity: 1;">
|
||||
<path sodipodi:nodetypes="ccc" id="path3165-7-3" d="m -72.5,173.5 -14,14 14,14" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 7; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;"/>
|
||||
</g>
|
||||
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.523439; visibility: visible; display: inline;" id="path4050-2-7-9-4" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(3.34328, 0, 0, 3.34328, 185.28, -623.176)"/>
|
||||
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.523439; visibility: visible; display: inline;" id="path4050-2-7-9-4-8" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(3.34328, 0, 0, 3.34328, 207.28, -623.176)"/>
|
||||
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 0.697921; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;" id="path4050-2-7-9-4-0" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(2.86565, 0, 0, 2.86565, 166.846, -534.143)"/>
|
||||
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 0.697921; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;" id="path4050-2-7-9-4-0-9" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(2.86565, 0, 0, 2.86565, 188.846, -534.143)"/>
|
||||
<path style="overflow: visible; marker: none; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; text-indent: 0pt; text-align: start; text-decoration: none; line-height: normal; letter-spacing: normal; word-spacing: normal; text-transform: none; direction: ltr; text-anchor: start; opacity: 0.35; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 1; stroke-miterlimit: 4; stroke-dasharray: none; visibility: visible; display: inline; font-family: Bitstream Vera Sans; stroke-opacity: 1;" d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z" id="path3165-7-3-1" sodipodi:nodetypes="ccccscccsc" transform="matrix(2, 0, 0, 2, -586, -765.967)"/>
|
||||
<path style="overflow: visible; marker: none; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; text-indent: 0pt; text-align: start; text-decoration: none; line-height: normal; letter-spacing: normal; word-spacing: normal; text-transform: none; direction: ltr; text-anchor: start; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 1; stroke-linecap: round; stroke-miterlimit: 4; stroke-dasharray: none; visibility: visible; display: inline; font-family: Bitstream Vera Sans; stroke-opacity: 1;" d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383" id="path3165-7-3-1-9" sodipodi:nodetypes="ccccccc" transform="matrix(2, 0, 0, 2, -586, -765.967)"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 13 KiB |
@ -2,51 +2,13 @@
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg25070"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="ws-switch-arrow-down.svg">
|
||||
<metadata
|
||||
id="metadata3353">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="718"
|
||||
inkscape:window-height="480"
|
||||
id="namedview3351"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.6979167"
|
||||
inkscape:cx="48"
|
||||
inkscape:cy="48"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg25070" />
|
||||
id="svg25070">
|
||||
<defs
|
||||
id="defs25072">
|
||||
<linearGradient
|
||||
@ -326,7 +288,7 @@
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
transform="matrix(0,1,-1,0,48.0003,4.1307112e-7)"
|
||||
transform="translate(0,48)"
|
||||
id="layer1">
|
||||
<g
|
||||
transform="matrix(-2,0,0,2,-97.2497,-374.967)"
|
||||
@ -335,42 +297,35 @@
|
||||
<path
|
||||
d="m -72.5,173.5 -14,14 14,14"
|
||||
id="path3165-7-3"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
</g>
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-3.34328,0,0,3.34328,-89.2797,-623.176)"
|
||||
id="path4050-2-7-9-4"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-3.34328,0,0,3.34328,-111.2797,-623.176)"
|
||||
id="path4050-2-7-9-4-8"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-2.86565,0,0,2.86565,-70.8457,-534.143)"
|
||||
id="path4050-2-7-9-4-0"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-2.86565,0,0,2.86565,-92.8457,-534.143)"
|
||||
id="path4050-2-7-9-4-0-9"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
<path
|
||||
d="m 47.87528,-34.0295 c 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25 -32.25,32.25 c -2.2253,2.2253 -6.2747,2.2253 -8.5,0 -2.2253,-2.22528 -2.2253,-6.2747 0,-8.5 l 23.75,-23.75 -23.75,-23.75 c -1.73168,-1.6731 -2.295,-4.44228 -1.3546,-6.65894 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 z"
|
||||
id="path3165-7-3-1"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" />
|
||||
<path
|
||||
d="m 41.8316,28.09418 c -0.014,-1.58898 0.54158,-3.18406 1.66868,-4.31118 l 23.75,-23.75 m -25.1046,-30.40894 c 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25"
|
||||
id="path3165-7-3-1-9"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
@ -1,447 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg25070"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="ws-switch-arrow-up.svg">
|
||||
<defs
|
||||
id="defs25072">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 24 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="48 : 24 : 1"
|
||||
inkscape:persp3d-origin="24 : 16 : 1"
|
||||
id="perspective25078" />
|
||||
<inkscape:perspective
|
||||
id="perspective24985"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4034-0-4"
|
||||
id="linearGradient24957"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(6)"
|
||||
x1="-86.552246"
|
||||
y1="185.439"
|
||||
x2="-83.37072"
|
||||
y2="197.31261" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4034-0-4">
|
||||
<stop
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4036-5-7" />
|
||||
<stop
|
||||
style="stop-color: rgb(186, 189, 182); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4038-9-6" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter24765"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix24767"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix24769"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4632-1-3-9-3-2"
|
||||
id="linearGradient24955"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-5)"
|
||||
x1="-74.520325"
|
||||
y1="169.06032"
|
||||
x2="-74.520325"
|
||||
y2="205.94189" />
|
||||
<linearGradient
|
||||
id="linearGradient4632-1-3-9-3-2">
|
||||
<stop
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4634-1-8-3-9-0" />
|
||||
<stop
|
||||
id="stop4636-1-9-9-8-8"
|
||||
offset="0.0274937"
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4638-8-3-9-6-6"
|
||||
offset="0.274937"
|
||||
style="stop-color: rgb(242, 242, 242); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4640-8-5-7-8-9"
|
||||
offset="0.38707438"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4642-5-41-9-6-9"
|
||||
offset="0.66528589"
|
||||
style="stop-color: rgb(217, 218, 216); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4644-5-2-7-9-2"
|
||||
offset="0.76745707"
|
||||
style="stop-color: rgb(223, 224, 221); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(240, 240, 240); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4646-3-2-3-7-3" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4869-4-1"
|
||||
id="radialGradient24959"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
r="2.3554697" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-1">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4871-6-2" />
|
||||
<stop
|
||||
id="stop4879-7-4"
|
||||
offset="0.31807542"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4877-6-1"
|
||||
offset="0.74691135"
|
||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4873-1-0" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25011"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25013"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25015"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4869-4-0"
|
||||
id="radialGradient24961"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
r="2.3554697" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-0">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4871-6-8" />
|
||||
<stop
|
||||
id="stop4879-7-5"
|
||||
offset="0.31807542"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4877-6-5"
|
||||
offset="0.74691135"
|
||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4873-1-4" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25023"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25025"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25027"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4941"
|
||||
id="linearGradient24963"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4941">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4943" />
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
||||
offset="1"
|
||||
id="stop4945" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25033"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25035"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25037"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4941-7"
|
||||
id="linearGradient24965"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4941-7">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4943-2" />
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
||||
offset="1"
|
||||
id="stop4945-5" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25043"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25045"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25047"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter25049"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25051"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25053"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter25055"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25057"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25059"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8284271"
|
||||
inkscape:cx="-12.356322"
|
||||
inkscape:cy="57.536221"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1200"
|
||||
inkscape:window-height="840"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata25075">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(0, 48)">
|
||||
<g
|
||||
id="g3181"
|
||||
transform="matrix(0,1,-1,0,48.0003,-48)">
|
||||
<g
|
||||
style="stroke:#000000;stroke-opacity:1;display:inline"
|
||||
transform="matrix(2,0,0,2,193.25,-374.967)"
|
||||
id="g4030-1-8">
|
||||
<path
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="m -72.5,173.5 -14,14 14,14"
|
||||
id="path3165-7-3"
|
||||
sodipodi:nodetypes="ccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<path
|
||||
transform="matrix(3.34328,0,0,3.34328,185.28,-623.176)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(3.34328,0,0,3.34328,207.28,-623.176)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-8"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2.86565,0,0,2.86565,166.846,-534.143)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-0"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2.86565,0,0,2.86565,188.846,-534.143)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-0-9"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
||||
sodipodi:nodetypes="ccccscccsc"
|
||||
id="path3165-7-3-1"
|
||||
d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
id="path3165-7-3-1-9"
|
||||
d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 16 KiB |
@ -6,22 +6,6 @@
|
||||
|
||||
<name xml:lang="en">GNOME Shell</name>
|
||||
<shortdesc xml:lang="en">Next generation GNOME desktop shell</shortdesc>
|
||||
<description>GNOME Shell provides core user interface functions for the GNOME 3
|
||||
desktop, like switching to windows and launching applications.
|
||||
GNOME Shell takes advantage of the capabilities of modern graphics
|
||||
hardware and introduces innovative user interface concepts to
|
||||
provide a visually attractive and easy to use experience.
|
||||
|
||||
Tarball releases are provided largely for distributions to build
|
||||
packages. If you are interested in building GNOME Shell from source,
|
||||
we would recommend building from version control using the build
|
||||
script described at:
|
||||
|
||||
http://live.gnome.org/GnomeShell
|
||||
|
||||
Not only will that give you the very latest version of this rapidly
|
||||
changing project, it will be much easier than get GNOME Shell and
|
||||
its dependencies to build from tarballs.</description>
|
||||
<!--
|
||||
<homepage rdf:resource="http://live.gnome.org/GnomeShell" />
|
||||
-->
|
||||
@ -49,7 +33,7 @@ its dependencies to build from tarballs.</description>
|
||||
<foaf:Person>
|
||||
<foaf:name>Colin Walters</foaf:name>
|
||||
<foaf:mbox rdf:resource="mailto:walters@verbum.org" />
|
||||
<gnome:userid>walters</gnome:userid>
|
||||
<gnome:userid>cwalters</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
|
@ -2,30 +2,26 @@
|
||||
jsdir = $(pkgdatadir)/js
|
||||
|
||||
nobase_dist_js_DATA = \
|
||||
misc/config.js \
|
||||
misc/docInfo.js \
|
||||
misc/fileUtils.js \
|
||||
misc/format.js \
|
||||
misc/gnomeSession.js \
|
||||
misc/history.js \
|
||||
misc/modemManager.js \
|
||||
misc/params.js \
|
||||
misc/util.js \
|
||||
misc/telepathy.js \
|
||||
perf/core.js \
|
||||
prefs/clockPreferences.js \
|
||||
ui/altTab.js \
|
||||
ui/appDisplay.js \
|
||||
ui/appFavorites.js \
|
||||
ui/boxpointer.js \
|
||||
ui/calendar.js \
|
||||
ui/chrome.js \
|
||||
ui/ctrlAltTab.js \
|
||||
ui/dash.js \
|
||||
ui/dateMenu.js \
|
||||
ui/dnd.js \
|
||||
ui/docDisplay.js \
|
||||
ui/endSessionDialog.js \
|
||||
ui/environment.js \
|
||||
ui/extensionSystem.js \
|
||||
ui/genericDisplay.js \
|
||||
ui/iconGrid.js \
|
||||
ui/lightbox.js \
|
||||
ui/link.js \
|
||||
@ -34,13 +30,11 @@ nobase_dist_js_DATA = \
|
||||
ui/magnifierDBus.js \
|
||||
ui/main.js \
|
||||
ui/messageTray.js \
|
||||
ui/modalDialog.js \
|
||||
ui/notificationDaemon.js \
|
||||
ui/overview.js \
|
||||
ui/panel.js \
|
||||
ui/panelMenu.js \
|
||||
ui/placeDisplay.js \
|
||||
ui/polkitAuthenticationAgent.js \
|
||||
ui/popupMenu.js \
|
||||
ui/runDialog.js \
|
||||
ui/scripting.js \
|
||||
@ -50,18 +44,13 @@ nobase_dist_js_DATA = \
|
||||
ui/statusIconDispatcher.js \
|
||||
ui/statusMenu.js \
|
||||
ui/status/accessibility.js \
|
||||
ui/status/keyboard.js \
|
||||
ui/status/network.js \
|
||||
ui/status/power.js \
|
||||
ui/status/volume.js \
|
||||
ui/status/bluetooth.js \
|
||||
ui/telepathyClient.js \
|
||||
ui/tweener.js \
|
||||
ui/viewSelector.js \
|
||||
ui/windowAttentionHandler.js \
|
||||
ui/windowManager.js \
|
||||
ui/workspace.js \
|
||||
ui/workspaceThumbnail.js \
|
||||
ui/workspacesView.js \
|
||||
ui/workspaceSwitcherPopup.js \
|
||||
ui/xdndHandler.js
|
||||
ui/workspaceSwitcherPopup.js
|
||||
|
@ -1,10 +0,0 @@
|
||||
/* mode: js2; indent-tabs-mode: nil; tab-size: 4 */
|
||||
/* The name of this package (not localized) */
|
||||
const PACKAGE_NAME = '@PACKAGE_NAME@';
|
||||
/* The version of this package */
|
||||
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
|
||||
/* The version of GJS we're linking to */
|
||||
const GJS_VERSION = '@GJS_VERSION@';
|
||||
/* 1 if gnome-bluetooth is available, 0 otherwise */
|
||||
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
|
||||
|
@ -29,8 +29,8 @@ DocInfo.prototype = {
|
||||
return St.TextureCache.get_default().load_recent_thumbnail(size, this.recentInfo);
|
||||
},
|
||||
|
||||
launch : function(workspaceIndex) {
|
||||
Shell.DocSystem.get_default().open(this.recentInfo, workspaceIndex);
|
||||
launch : function() {
|
||||
Shell.DocSystem.get_default().open(this.recentInfo);
|
||||
},
|
||||
|
||||
matchTerms: function(terms) {
|
||||
@ -60,7 +60,8 @@ function getDocManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* DocManager wraps the DocSystem, primarily to expose DocInfo objects.
|
||||
* DocManager wraps the DocSystem, primarily to expose DocInfo objects
|
||||
* which conform to the GenericDisplay item API.
|
||||
*/
|
||||
function DocManager() {
|
||||
this._init();
|
||||
|
@ -2,13 +2,11 @@
|
||||
|
||||
const DBus = imports.dbus;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const PresenceIface = {
|
||||
name: 'org.gnome.SessionManager.Presence',
|
||||
methods: [{ name: 'SetStatus',
|
||||
inSignature: 'u',
|
||||
outSignature: '' }],
|
||||
inSignature: 'u' }],
|
||||
properties: [{ name: 'status',
|
||||
signature: 'u',
|
||||
access: 'readwrite' }],
|
||||
@ -45,81 +43,3 @@ Presence.prototype = {
|
||||
}
|
||||
};
|
||||
DBus.proxifyPrototype(Presence.prototype, PresenceIface);
|
||||
|
||||
// Note inhibitors are immutable objects, so they don't
|
||||
// change at runtime (changes always come in the form
|
||||
// of new inhibitors)
|
||||
const InhibitorIface = {
|
||||
name: 'org.gnome.SessionManager.Inhibitor',
|
||||
properties: [{ name: 'app_id',
|
||||
signature: 's',
|
||||
access: 'readonly' },
|
||||
{ name: 'client_id',
|
||||
signature: 's',
|
||||
access: 'readonly' },
|
||||
{ name: 'reason',
|
||||
signature: 's',
|
||||
access: 'readonly' },
|
||||
{ name: 'flags',
|
||||
signature: 'u',
|
||||
access: 'readonly' },
|
||||
{ name: 'toplevel_xid',
|
||||
signature: 'u',
|
||||
access: 'readonly' },
|
||||
{ name: 'cookie',
|
||||
signature: 'u',
|
||||
access: 'readonly' }],
|
||||
};
|
||||
|
||||
function Inhibitor(objectPath) {
|
||||
this._init(objectPath);
|
||||
}
|
||||
|
||||
Inhibitor.prototype = {
|
||||
_init: function(objectPath) {
|
||||
DBus.session.proxifyObject(this,
|
||||
"org.gnome.SessionManager",
|
||||
objectPath);
|
||||
this.isLoaded = false;
|
||||
this._loadingPropertiesCount = InhibitorIface.properties.length;
|
||||
for (let i = 0; i < InhibitorIface.properties.length; i++) {
|
||||
let propertyName = InhibitorIface.properties[i].name;
|
||||
this.GetRemote(propertyName, Lang.bind(this,
|
||||
function(value, exception) {
|
||||
if (exception)
|
||||
return;
|
||||
|
||||
this[propertyName] = value;
|
||||
this._loadingPropertiesCount--;
|
||||
|
||||
if (this._loadingPropertiesCount == 0) {
|
||||
this.isLoaded = true;
|
||||
this.emit("is-loaded");
|
||||
}
|
||||
}));
|
||||
}
|
||||
},
|
||||
};
|
||||
DBus.proxifyPrototype(Inhibitor.prototype, InhibitorIface);
|
||||
Signals.addSignalMethods(Inhibitor.prototype);
|
||||
|
||||
|
||||
// Not the full interface, only the methods we use
|
||||
const SessionManagerIface = {
|
||||
name: 'org.gnome.SessionManager',
|
||||
methods: [
|
||||
{ name: 'Logout', inSignature: 'u', outSignature: '' },
|
||||
{ name: 'Shutdown', inSignature: '', outSignature: '' }
|
||||
]
|
||||
};
|
||||
|
||||
function SessionManager() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
SessionManager.prototype = {
|
||||
_init: function() {
|
||||
DBus.session.proxifyObject(this, 'org.gnome.SessionManager', '/org/gnome/SessionManager');
|
||||
}
|
||||
};
|
||||
DBus.proxifyPrototype(SessionManager.prototype, SessionManagerIface);
|
@ -1,115 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const DEFAULT_LIMIT = 512;
|
||||
|
||||
function HistoryManager(params) {
|
||||
this._init(params);
|
||||
}
|
||||
|
||||
HistoryManager.prototype = {
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { gsettingsKey: null,
|
||||
limit: DEFAULT_LIMIT,
|
||||
entry: null });
|
||||
|
||||
this._key = params.gsettingsKey;
|
||||
this._limit = params.limit;
|
||||
|
||||
this._historyIndex = 0;
|
||||
if (this._key) {
|
||||
this._history = global.settings.get_strv(this._key);
|
||||
global.settings.connect('changed::' + this._key,
|
||||
Lang.bind(this, this._historyChanged));
|
||||
|
||||
} else {
|
||||
this._history = [];
|
||||
}
|
||||
|
||||
this._entry = params.entry;
|
||||
|
||||
if (this._entry) {
|
||||
this._entry.connect('key-press-event',
|
||||
Lang.bind(this, this._onEntryKeyPress));
|
||||
}
|
||||
},
|
||||
|
||||
_historyChanged: function() {
|
||||
this._history = global.settings.get_strv(this._key);
|
||||
this._historyIndex = this._history.length;
|
||||
},
|
||||
|
||||
prevItem: function(text) {
|
||||
if (this._historyIndex <= 0)
|
||||
return text;
|
||||
|
||||
if (text)
|
||||
this._history[this._historyIndex] = text;
|
||||
this._historyIndex--;
|
||||
return this._indexChanged();
|
||||
},
|
||||
|
||||
nextItem: function(text) {
|
||||
if (this._historyIndex >= this._history.length)
|
||||
return text;
|
||||
|
||||
if (text)
|
||||
this._history[this._historyIndex] = text;
|
||||
this._historyIndex++;
|
||||
return this._indexChanged();
|
||||
},
|
||||
|
||||
lastItem: function() {
|
||||
if (this._historyIndex != this._history.length) {
|
||||
this._historyIndex = this._history.length;
|
||||
this._indexChanged();
|
||||
}
|
||||
|
||||
return this._historyIndex[this._history.length];
|
||||
},
|
||||
|
||||
addItem: function(input) {
|
||||
if (this._history.length == 0 ||
|
||||
this._history[this._history.length - 1] != input) {
|
||||
|
||||
this._history.push(input);
|
||||
this._historyIndex = this._history.length;
|
||||
this._save();
|
||||
}
|
||||
},
|
||||
|
||||
_onEntryKeyPress: function(entry, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_Up) {
|
||||
this.prevItem(entry.get_text());
|
||||
return true;
|
||||
} else if (symbol == Clutter.KEY_Down) {
|
||||
this.nextItem(entry.get_text());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_indexChanged: function() {
|
||||
let current = this._history[this._historyIndex] || '';
|
||||
this.emit('changed', current);
|
||||
|
||||
if (this._entry)
|
||||
this._entry.set_text(current);
|
||||
|
||||
return current;
|
||||
},
|
||||
|
||||
_save: function() {
|
||||
if (this._history.length > this._limit)
|
||||
this._history.splice(0, this._history.length - this._limit);
|
||||
|
||||
if (this._key)
|
||||
global.settings.set_strv(this._key, this._history);
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(HistoryManager.prototype);
|
@ -1,225 +0,0 @@
|
||||
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||
|
||||
const DBus = imports.dbus;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
// The following are not the complete interfaces, just the methods we need
|
||||
// (or may need in the future)
|
||||
|
||||
const ModemGsmNetworkInterface = {
|
||||
name: 'org.freedesktop.ModemManager.Modem.Gsm.Network',
|
||||
methods: [
|
||||
{ name: 'GetRegistrationInfo', inSignature: '', outSignature: 'uss' },
|
||||
{ name: 'GetSignalQuality', inSignature: '', outSignature: 'u' }
|
||||
],
|
||||
properties: [
|
||||
{ name: 'AccessTechnology', signature: 'u', access: 'read' }
|
||||
],
|
||||
signals: [
|
||||
{ name: 'SignalQuality', inSignature: 'u' },
|
||||
{ name: 'RegistrationInfo', inSignature: 'uss' }
|
||||
]
|
||||
};
|
||||
const ModemGsmNetworkProxy = DBus.makeProxyClass(ModemGsmNetworkInterface);
|
||||
|
||||
const ModemCdmaInterface = {
|
||||
name: 'org.freedesktop.ModemManager.Modem.Cdma',
|
||||
methods: [
|
||||
{ name: 'GetSignalQuality', inSignature: '', outSignature: 'u' },
|
||||
{ name: 'GetServingSystem', inSignature: '', outSignature: 'usu' }
|
||||
],
|
||||
signals: [
|
||||
{ name: 'SignalQuality', inSignature: 'u' }
|
||||
]
|
||||
};
|
||||
const ModemCdmaProxy = DBus.makeProxyClass(ModemCdmaInterface);
|
||||
|
||||
let _providersTable;
|
||||
function _getProvidersTable() {
|
||||
if (_providersTable)
|
||||
return _providersTable;
|
||||
let [providers, countryCodes] = Shell.mobile_providers_parse();
|
||||
return _providersTable = providers;
|
||||
}
|
||||
|
||||
function ModemGsm() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
ModemGsm.prototype = {
|
||||
_init: function(path) {
|
||||
this._proxy = new ModemGsmNetworkProxy(DBus.system, 'org.freedesktop.ModemManager', path);
|
||||
|
||||
this.signal_quality = 0;
|
||||
this.operator_name = null;
|
||||
|
||||
// Code is duplicated because the function have different signatures
|
||||
this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
|
||||
this.signal_quality = quality;
|
||||
this.emit('notify::signal-quality');
|
||||
}));
|
||||
this._proxy.connect('RegistrationInfo', Lang.bind(this, function(proxy, status, code, name) {
|
||||
this.operator_name = this._findOperatorName(name, code);
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function(result, err) {
|
||||
if (err) {
|
||||
log(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let [status, code, name] = result;
|
||||
this.operator_name = this._findOperatorName(name, code);
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
|
||||
if (err) {
|
||||
// it will return an error if the device is not connected
|
||||
this.signal_quality = 0;
|
||||
} else {
|
||||
let [quality] = result;
|
||||
this.signal_quality = quality;
|
||||
}
|
||||
this.emit('notify::signal-quality');
|
||||
}));
|
||||
},
|
||||
|
||||
_findOperatorName: function(name, opCode) {
|
||||
if (name.length != 0 && (name.length > 6 || name.length < 5)) {
|
||||
// this looks like a valid name, i.e. not an MCCMNC (that some
|
||||
// devices return when not yet connected
|
||||
return name;
|
||||
}
|
||||
if (isNaN(parseInt(name))) {
|
||||
// name is definitely not a MCCMNC, so it may be a name
|
||||
// after all; return that
|
||||
return name;
|
||||
}
|
||||
|
||||
let needle;
|
||||
if (name.length == 0 && opCode)
|
||||
needle = opCode;
|
||||
else if (name.length == 6 || name.length == 5)
|
||||
needle = name;
|
||||
else // nothing to search
|
||||
return null;
|
||||
|
||||
return this._findProviderForMCCMNC(needle);
|
||||
},
|
||||
|
||||
_findProviderForMCCMNC: function(needle) {
|
||||
let table = _getProvidersTable();
|
||||
let needlemcc = needle.substring(0, 3);
|
||||
let needlemnc = needle.substring(3, needle.length);
|
||||
|
||||
let name2, name3;
|
||||
for (let iter in table) {
|
||||
let providers = table[iter];
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
|
||||
// Search through MCC/MNC list
|
||||
let list = provider.get_gsm_mcc_mnc();
|
||||
for (let j = 0; j < list.length; j++) {
|
||||
let mccmnc = list[j];
|
||||
|
||||
// Match both 2-digit and 3-digit MNC; prefer a
|
||||
// 3-digit match if found, otherwise a 2-digit one.
|
||||
if (mccmnc.mcc != needlemcc)
|
||||
continue; // MCC was wrong
|
||||
|
||||
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
|
||||
name3 = provider.name;
|
||||
|
||||
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
|
||||
name2 = provider.name;
|
||||
|
||||
if (name2 && name3)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return name3 || name2 || null;
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(ModemGsm.prototype);
|
||||
|
||||
function ModemCdma() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
ModemCdma.prototype = {
|
||||
_init: function(path) {
|
||||
this._proxy = new ModemCdmaProxy(DBus.system, 'org.freedesktop.ModemManager', path);
|
||||
|
||||
this.signal_quality = 0;
|
||||
this.operator_name = null;
|
||||
this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
|
||||
this.signal_quality = quality;
|
||||
this.emit('notify::signal-quality');
|
||||
|
||||
// receiving this signal means the device got activated
|
||||
// and we can finally call GetServingSystem
|
||||
if (this.operator_name == null)
|
||||
this._refreshServingSystem();
|
||||
}));
|
||||
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
|
||||
if (err) {
|
||||
// it will return an error if the device is not connected
|
||||
this.signal_quality = 0;
|
||||
} else {
|
||||
let [quality] = result;
|
||||
this.signal_quality = quality;
|
||||
}
|
||||
this.emit('notify::signal-quality');
|
||||
}));
|
||||
},
|
||||
|
||||
_refreshServingSystem: function() {
|
||||
this._proxy.GetServingSystemRemote(Lang.bind(this, function(result, err) {
|
||||
if (err) {
|
||||
// it will return an error if the device is not connected
|
||||
this.operator_name = null;
|
||||
} else {
|
||||
let [bandClass, band, id] = result;
|
||||
if (name.length > 0)
|
||||
this.operator_name = this._findProviderForSid(id);
|
||||
else
|
||||
this.operator_name = null;
|
||||
}
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
},
|
||||
|
||||
_findProviderForSid: function(sid) {
|
||||
if (sid == 0)
|
||||
return null;
|
||||
|
||||
let table = _getProvidersTable();
|
||||
|
||||
// Search through each country
|
||||
for (let iter in table) {
|
||||
let providers = table[iter];
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
let cdma_sid = provider.get_cdma_sid();
|
||||
|
||||
// Search through CDMA SID list
|
||||
for (let j = 0; j < cdma_sid.length; j++) {
|
||||
if (cdma_sid[j] == sid)
|
||||
return provider.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ModemCdma.prototype);
|
361
js/misc/telepathy.js
Normal file
@ -0,0 +1,361 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const DBus = imports.dbus;
|
||||
|
||||
// D-Bus utils
|
||||
function nameToPath(name) {
|
||||
return '/' + name.replace(/\./g, '/');
|
||||
};
|
||||
|
||||
function pathToName(path) {
|
||||
if (path[0] != '/')
|
||||
throw new Error('not a D-Bus path: ' + path);
|
||||
return path.substr(1).replace(/\//g, '.');
|
||||
};
|
||||
|
||||
// This is tp_escape_as_identifier() from telepathy-glib
|
||||
function escapeAsIdentifier(name) {
|
||||
if (!name)
|
||||
return '_';
|
||||
|
||||
// first char is replaced with _XX if it's non-alpha,
|
||||
// later chars are replaced with _XX if they're non-alphanumeric
|
||||
if (name.length == 1) {
|
||||
return name.replace(/[^a-zA-Z]/, _hexEscape);
|
||||
} else {
|
||||
return (name[0].replace(/[^a-zA-Z]/, _hexEscape) +
|
||||
name.substring(1).replace(/[^a-zA-Z0-9]/g, _hexEscape));
|
||||
}
|
||||
}
|
||||
|
||||
function _hexEscape(ch) {
|
||||
return '_' + ch.charCodeAt(0).toString(16);
|
||||
}
|
||||
|
||||
// Telepathy D-Bus interface definitions. Note that most of these are
|
||||
// incomplete, and only cover the methods/properties/signals that
|
||||
// we're currently using.
|
||||
|
||||
const TELEPATHY = 'org.freedesktop.Telepathy';
|
||||
|
||||
const CLIENT_NAME = TELEPATHY + '.Client';
|
||||
const ClientIface = {
|
||||
name: CLIENT_NAME,
|
||||
properties: [
|
||||
{ name: 'Interfaces',
|
||||
signature: 'as',
|
||||
access: 'read' }
|
||||
]
|
||||
};
|
||||
|
||||
const CLIENT_APPROVER_NAME = TELEPATHY + '.Client.Approver';
|
||||
const ClientApproverIface = {
|
||||
name: CLIENT_APPROVER_NAME,
|
||||
methods: [
|
||||
{ name: 'AddDispatchOperation',
|
||||
inSignature: 'a(oa{sv})oa{sv}',
|
||||
outSignature: '' }
|
||||
],
|
||||
properties: [
|
||||
{ name: 'ApproverChannelFilter',
|
||||
signature: 'aa{sv}',
|
||||
access: 'read' }
|
||||
]
|
||||
};
|
||||
|
||||
const CLIENT_HANDLER_NAME = TELEPATHY + '.Client.Handler';
|
||||
const ClientHandlerIface = {
|
||||
name: CLIENT_HANDLER_NAME,
|
||||
methods: [
|
||||
{ name: 'HandleChannels',
|
||||
inSignature: 'ooa(oa{sv})aota{sv}',
|
||||
outSignature: '' }
|
||||
],
|
||||
properties: [
|
||||
{ name: 'HandlerChannelFilter',
|
||||
signature: 'aa{sv}',
|
||||
access: 'read' }
|
||||
]
|
||||
};
|
||||
|
||||
const CLIENT_OBSERVER_NAME = TELEPATHY + '.Client.Observer';
|
||||
const ClientObserverIface = {
|
||||
name: CLIENT_OBSERVER_NAME,
|
||||
methods: [
|
||||
{ name: 'ObserveChannels',
|
||||
inSignature: 'ooa(oa{sv})oaoa{sv}',
|
||||
outSignature: '' }
|
||||
],
|
||||
properties: [
|
||||
{ name: 'ObserverChannelFilter',
|
||||
signature: 'aa{sv}',
|
||||
access: 'read' }
|
||||
]
|
||||
};
|
||||
|
||||
const CHANNEL_DISPATCH_OPERATION_NAME = TELEPATHY + '.ChannelDispatchOperation';
|
||||
const ChannelDispatchOperationIface = {
|
||||
name: CHANNEL_DISPATCH_OPERATION_NAME,
|
||||
methods: [
|
||||
{ name: 'HandleWith',
|
||||
inSignature: 's',
|
||||
outSignature: '' },
|
||||
{ name: 'Claim',
|
||||
inSignature: '',
|
||||
outSignature: '' }
|
||||
]
|
||||
};
|
||||
let ChannelDispatchOperation = DBus.makeProxyClass(ChannelDispatchOperationIface);
|
||||
|
||||
const CONNECTION_NAME = TELEPATHY + '.Connection';
|
||||
const ConnectionIface = {
|
||||
name: CONNECTION_NAME,
|
||||
signals: [
|
||||
{ name: 'StatusChanged',
|
||||
inSignature: 'uu' }
|
||||
]
|
||||
};
|
||||
let Connection = DBus.makeProxyClass(ConnectionIface);
|
||||
|
||||
const ConnectionStatus = {
|
||||
CONNECTED: 0,
|
||||
CONNECTING: 1,
|
||||
DISCONNECTED: 2
|
||||
};
|
||||
|
||||
const CONNECTION_ALIASING_NAME = CONNECTION_NAME + '.Interface.Aliasing';
|
||||
const ConnectionAliasingIface = {
|
||||
name: CONNECTION_ALIASING_NAME,
|
||||
methods: [
|
||||
{ name: 'RequestAliases',
|
||||
inSignature: 'au',
|
||||
outSignature: 'as'
|
||||
}
|
||||
],
|
||||
signals: [
|
||||
{ name: 'AliasesChanged',
|
||||
inSignature: 'a(us)' }
|
||||
]
|
||||
};
|
||||
let ConnectionAliasing = DBus.makeProxyClass(ConnectionAliasingIface);
|
||||
|
||||
const CONNECTION_AVATARS_NAME = CONNECTION_NAME + '.Interface.Avatars';
|
||||
const ConnectionAvatarsIface = {
|
||||
name: CONNECTION_AVATARS_NAME,
|
||||
methods: [
|
||||
{ name: 'GetKnownAvatarTokens',
|
||||
inSignature: 'au',
|
||||
outSignature: 'a{us}'
|
||||
},
|
||||
{ name: 'RequestAvatars',
|
||||
inSignature: 'au',
|
||||
outSignature: ''
|
||||
}
|
||||
],
|
||||
signals: [
|
||||
{ name: 'AvatarRetrieved',
|
||||
inSignature: 'usays'
|
||||
},
|
||||
{ name: 'AvatarUpdated',
|
||||
inSignature: 'us'
|
||||
}
|
||||
]
|
||||
};
|
||||
let ConnectionAvatars = DBus.makeProxyClass(ConnectionAvatarsIface);
|
||||
|
||||
const CONNECTION_CONTACTS_NAME = CONNECTION_NAME + '.Interface.Contacts';
|
||||
const ConnectionContactsIface = {
|
||||
name: CONNECTION_CONTACTS_NAME,
|
||||
methods: [
|
||||
{ name: 'GetContactAttributes',
|
||||
inSignature: 'auasb',
|
||||
outSignature: 'a{ua{sv}}'
|
||||
}
|
||||
]
|
||||
};
|
||||
let ConnectionContacts = DBus.makeProxyClass(ConnectionContactsIface);
|
||||
|
||||
const CONNECTION_REQUESTS_NAME = CONNECTION_NAME + '.Interface.Requests';
|
||||
const ConnectionRequestsIface = {
|
||||
name: CONNECTION_REQUESTS_NAME,
|
||||
methods: [
|
||||
{ name: 'CreateChannel',
|
||||
inSignature: 'a{sv}',
|
||||
outSignature: 'oa{sv}'
|
||||
},
|
||||
{ name: 'EnsureChannel',
|
||||
inSignature: 'a{sv}',
|
||||
outSignature: 'boa{sv}'
|
||||
}
|
||||
],
|
||||
properties: [
|
||||
{ name: 'Channels',
|
||||
signature: 'a(oa{sv})',
|
||||
access: 'read' }
|
||||
],
|
||||
signals: [
|
||||
{ name: 'NewChannels',
|
||||
inSignature: 'a(oa{sv})'
|
||||
},
|
||||
{ name: 'ChannelClosed',
|
||||
inSignature: 'o'
|
||||
}
|
||||
]
|
||||
};
|
||||
let ConnectionRequests = DBus.makeProxyClass(ConnectionRequestsIface);
|
||||
|
||||
const CONNECTION_SIMPLE_PRESENCE_NAME = CONNECTION_NAME + '.Interface.SimplePresence';
|
||||
const ConnectionSimplePresenceIface = {
|
||||
name: CONNECTION_SIMPLE_PRESENCE_NAME,
|
||||
methods: [
|
||||
{ name: 'SetPresence',
|
||||
inSignature: 'ss'
|
||||
},
|
||||
{ name: 'GetPresences',
|
||||
inSignature: 'au',
|
||||
outSignature: 'a{u(uss)}'
|
||||
}
|
||||
],
|
||||
signals: [
|
||||
{ name: 'PresencesChanged',
|
||||
inSignature: 'a{u(uss)}' }
|
||||
]
|
||||
};
|
||||
let ConnectionSimplePresence = DBus.makeProxyClass(ConnectionSimplePresenceIface);
|
||||
|
||||
const ConnectionPresenceType = {
|
||||
UNSET: 0,
|
||||
OFFLINE: 1,
|
||||
AVAILABLE: 2,
|
||||
AWAY: 3,
|
||||
EXTENDED_AWAY: 4,
|
||||
HIDDEN: 5,
|
||||
BUSY: 6,
|
||||
UNKNOWN: 7,
|
||||
ERROR: 8
|
||||
};
|
||||
|
||||
const HandleType = {
|
||||
NONE: 0,
|
||||
CONTACT: 1,
|
||||
ROOM: 2,
|
||||
LIST: 3,
|
||||
GROUP: 4
|
||||
};
|
||||
|
||||
const CHANNEL_NAME = TELEPATHY + '.Channel';
|
||||
const ChannelIface = {
|
||||
name: CHANNEL_NAME,
|
||||
signals: [
|
||||
{ name: 'Closed',
|
||||
inSignature: '' }
|
||||
]
|
||||
};
|
||||
let Channel = DBus.makeProxyClass(ChannelIface);
|
||||
|
||||
const CHANNEL_TEXT_NAME = CHANNEL_NAME + '.Type.Text';
|
||||
const ChannelTextIface = {
|
||||
name: CHANNEL_TEXT_NAME,
|
||||
methods: [
|
||||
{ name: 'ListPendingMessages',
|
||||
inSignature: 'b',
|
||||
outSignature: 'a(uuuuus)'
|
||||
},
|
||||
{ name: 'AcknowledgePendingMessages',
|
||||
inSignature: 'au',
|
||||
outSignature: ''
|
||||
},
|
||||
{ name: 'Send',
|
||||
inSignature: 'us',
|
||||
outSignature: ''
|
||||
}
|
||||
],
|
||||
signals: [
|
||||
{ name: 'Received',
|
||||
inSignature: 'uuuuus' },
|
||||
{ name: 'Sent',
|
||||
inSignature: 'uus' }
|
||||
]
|
||||
};
|
||||
let ChannelText = DBus.makeProxyClass(ChannelTextIface);
|
||||
|
||||
const ChannelTextMessageType = {
|
||||
NORMAL: 0,
|
||||
ACTION: 1,
|
||||
NOTICE: 2,
|
||||
AUTO_REPLY: 3,
|
||||
DELIVERY_REPORT: 4
|
||||
};
|
||||
|
||||
const CHANNEL_CONTACT_LIST_NAME = CHANNEL_NAME + '.Type.ContactList';
|
||||
// There is no interface associated with ContactList; it's just a
|
||||
// special kind of Channel.Interface.Group
|
||||
|
||||
const CHANNEL_GROUP_NAME = CHANNEL_NAME + '.Interface.Group';
|
||||
const ChannelGroupIface = {
|
||||
name: CHANNEL_GROUP_NAME,
|
||||
properties: [
|
||||
{ name: 'Members',
|
||||
signature: 'au',
|
||||
access: 'read' }
|
||||
],
|
||||
signals: [
|
||||
{ name: 'MembersChanged',
|
||||
inSignature: 'sauauauauuu' }
|
||||
]
|
||||
};
|
||||
let ChannelGroup = DBus.makeProxyClass(ChannelGroupIface);
|
||||
|
||||
const ACCOUNT_MANAGER_NAME = TELEPATHY + '.AccountManager';
|
||||
const AccountManagerIface = {
|
||||
name: ACCOUNT_MANAGER_NAME,
|
||||
properties: [
|
||||
{ name: 'ValidAccounts',
|
||||
signature: 'ao',
|
||||
access: 'read' }
|
||||
],
|
||||
signals: [
|
||||
{ name: 'AccountValidityChanged',
|
||||
inSignature: 'ob' }
|
||||
]
|
||||
};
|
||||
let AccountManager = DBus.makeProxyClass(AccountManagerIface);
|
||||
|
||||
const ACCOUNT_NAME = TELEPATHY + '.Account';
|
||||
const AccountIface = {
|
||||
name: ACCOUNT_NAME,
|
||||
properties: [
|
||||
{ name: 'Connection',
|
||||
signature: 'o',
|
||||
access: 'read' }
|
||||
]
|
||||
};
|
||||
let Account = DBus.makeProxyClass(AccountIface);
|
||||
|
||||
const CHANNEL_DISPATCHER_NAME = TELEPATHY + '.ChannelDispatcher';
|
||||
const ChannelDispatcherIface = {
|
||||
name: CHANNEL_DISPATCHER_NAME,
|
||||
methods: [
|
||||
{ name: 'EnsureChannel',
|
||||
inSignature: 'oa{sv}xs',
|
||||
outSignature: 'o' }
|
||||
]
|
||||
};
|
||||
let ChannelDispatcher = DBus.makeProxyClass(ChannelDispatcherIface);
|
||||
|
||||
const CHANNEL_REQUEST_NAME = TELEPATHY + '.ChannelRequest';
|
||||
const ChannelRequestIface = {
|
||||
name: CHANNEL_REQUEST_NAME,
|
||||
methods: [
|
||||
{ name: 'Proceed',
|
||||
inSignature: '',
|
||||
outSignature: '' }
|
||||
],
|
||||
signals: [
|
||||
{ name: 'Failed',
|
||||
signature: 'ss' },
|
||||
{ name: 'Succeeded',
|
||||
signature: '' }
|
||||
]
|
||||
};
|
||||
let ChannelRequest = DBus.makeProxyClass(ChannelRequestIface);
|
213
js/misc/util.js
@ -1,213 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
/* http://daringfireball.net/2010/07/improved_regex_for_matching_urls */
|
||||
const _urlRegexp = new RegExp('\\b(([a-z][\\w-]+:(/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)([^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'\\".,<>?«»“”‘’]))', 'gi');
|
||||
|
||||
// findUrls:
|
||||
// @str: string to find URLs in
|
||||
//
|
||||
// Searches @str for URLs and returns an array of objects with %url
|
||||
// properties showing the matched URL string, and %pos properties indicating
|
||||
// the position within @str where the URL was found.
|
||||
//
|
||||
// Return value: the list of match objects, as described above
|
||||
function findUrls(str) {
|
||||
let res = [], match;
|
||||
while ((match = _urlRegexp.exec(str)))
|
||||
res.push({ url: match[0], pos: match.index });
|
||||
return res;
|
||||
}
|
||||
|
||||
// spawn:
|
||||
// @argv: an argv array
|
||||
//
|
||||
// Runs @argv in the background, handling any errors that occur
|
||||
// when trying to start the program.
|
||||
function spawn(argv) {
|
||||
try {
|
||||
trySpawn(argv);
|
||||
} catch (err) {
|
||||
_handleSpawnError(argv[0], err);
|
||||
}
|
||||
}
|
||||
|
||||
// spawnCommandLine:
|
||||
// @command_line: a command line
|
||||
//
|
||||
// Runs @command_line in the background, handling any errors that
|
||||
// occur when trying to parse or start the program.
|
||||
function spawnCommandLine(command_line) {
|
||||
try {
|
||||
let [success, argc, argv] = GLib.shell_parse_argv(command_line);
|
||||
trySpawn(argv);
|
||||
} catch (err) {
|
||||
_handleSpawnError(command_line, err);
|
||||
}
|
||||
}
|
||||
|
||||
// trySpawn:
|
||||
// @argv: an argv array
|
||||
//
|
||||
// Runs @argv in the background. If launching @argv fails,
|
||||
// this will throw an error.
|
||||
function trySpawn(argv)
|
||||
{
|
||||
try {
|
||||
GLib.spawn_async(null, argv, null,
|
||||
GLib.SpawnFlags.SEARCH_PATH,
|
||||
null, null);
|
||||
} catch (err) {
|
||||
if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
|
||||
err.message = _("Command not found");
|
||||
} else {
|
||||
// The exception from gjs contains an error string like:
|
||||
// Error invoking GLib.spawn_command_line_async: Failed to
|
||||
// execute child process "foo" (No such file or directory)
|
||||
// We are only interested in the part in the parentheses. (And
|
||||
// we can't pattern match the text, since it gets localized.)
|
||||
err.message = err.message.replace(/.*\((.+)\)/, '$1');
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// trySpawnCommandLine:
|
||||
// @command_line: a command line
|
||||
//
|
||||
// Runs @command_line in the background. If launching @command_line
|
||||
// fails, this will throw an error.
|
||||
function trySpawnCommandLine(command_line) {
|
||||
let success, argc, argv;
|
||||
|
||||
try {
|
||||
[success, argc, argv] = GLib.shell_parse_argv(command_line);
|
||||
} catch (err) {
|
||||
// Replace "Error invoking GLib.shell_parse_argv: " with
|
||||
// something nicer
|
||||
err.message = err.message.replace(/[^:]*: /, _("Could not parse command:") + "\n");
|
||||
throw err;
|
||||
}
|
||||
|
||||
trySpawn(argv);
|
||||
}
|
||||
|
||||
function _handleSpawnError(command, err) {
|
||||
let title = _("Execution of '%s' failed:").format(command);
|
||||
Main.notifyError(title, err.message);
|
||||
}
|
||||
|
||||
// killall:
|
||||
// @processName: a process name
|
||||
//
|
||||
// Kills @processName. If no process with the given name is found,
|
||||
// this will fail silently.
|
||||
function killall(processName) {
|
||||
try {
|
||||
// pkill is more portable than killall, but on Linux at least
|
||||
// it won't match if you pass more than 15 characters of the
|
||||
// process name... However, if you use the '-f' flag to match
|
||||
// the entire command line, it will work, but we have to be
|
||||
// careful in that case that we can match
|
||||
// '/usr/bin/processName' but not 'gedit processName.c' or
|
||||
// whatever...
|
||||
|
||||
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
|
||||
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null, null);
|
||||
// It might be useful to return success/failure, but we'd need
|
||||
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
|
||||
// the current callers care, we don't bother.
|
||||
} catch (e) {
|
||||
logError(e, 'Failed to kill ' + processName);
|
||||
}
|
||||
}
|
||||
|
||||
// This was ported from network-manager-applet
|
||||
// Copyright 2007 - 2011 Red Hat, Inc.
|
||||
// Author: Dan Williams <dcbw@redhat.com>
|
||||
|
||||
const _IGNORED_WORDS = [
|
||||
'Semiconductor',
|
||||
'Components',
|
||||
'Corporation',
|
||||
'Communications',
|
||||
'Company',
|
||||
'Corp.',
|
||||
'Corp',
|
||||
'Co.',
|
||||
'Inc.',
|
||||
'Inc',
|
||||
'Incorporated',
|
||||
'Ltd.',
|
||||
'Limited.',
|
||||
'Intel?',
|
||||
'chipset',
|
||||
'adapter',
|
||||
'[hex]',
|
||||
'NDIS',
|
||||
'Module'
|
||||
];
|
||||
|
||||
const _IGNORED_PHRASES = [
|
||||
'Multiprotocol MAC/baseband processor',
|
||||
'Wireless LAN Controller',
|
||||
'Wireless LAN Adapter',
|
||||
'Wireless Adapter',
|
||||
'Network Connection',
|
||||
'Wireless Cardbus Adapter',
|
||||
'Wireless CardBus Adapter',
|
||||
'54 Mbps Wireless PC Card',
|
||||
'Wireless PC Card',
|
||||
'Wireless PC',
|
||||
'PC Card with XJACK(r) Antenna',
|
||||
'Wireless cardbus',
|
||||
'Wireless LAN PC Card',
|
||||
'Technology Group Ltd.',
|
||||
'Communication S.p.A.',
|
||||
'Business Mobile Networks BV',
|
||||
'Mobile Broadband Minicard Composite Device',
|
||||
'Mobile Communications AB',
|
||||
'(PC-Suite Mode)'
|
||||
];
|
||||
|
||||
function fixupPCIDescription(desc) {
|
||||
desc.replace(/[_,]/, ' ');
|
||||
|
||||
/* Attempt to shorten ID by ignoring certain phrases */
|
||||
for (let i = 0; i < _IGNORED_PHRASES.length; i++) {
|
||||
let item = _IGNORED_PHRASES[i];
|
||||
let pos = desc.indexOf(item);
|
||||
if (pos != -1) {
|
||||
let before = desc.substring(0, pos);
|
||||
let after = desc.substring(pos + item.length, desc.length);
|
||||
desc = before + after;
|
||||
}
|
||||
}
|
||||
|
||||
/* Attmept to shorten ID by ignoring certain individual words */
|
||||
let words = desc.split(' ');
|
||||
let out = [ ];
|
||||
for (let i = 0; i < words; i++) {
|
||||
let item = words[i];
|
||||
|
||||
// skip empty items (that come out from consecutive spaces)
|
||||
if (item.length == 0)
|
||||
continue;
|
||||
|
||||
if (_IGNORED_WORDS.indexOf(item) == -1) {
|
||||
out.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return out.join(' ');
|
||||
}
|
@ -21,77 +21,26 @@ let METRICS = {
|
||||
overviewFpsSubsequent:
|
||||
{ description: "Frames rate when going to the overview, second time",
|
||||
units: "frames / s" },
|
||||
overviewFps5Windows:
|
||||
{ description: "Frames rate when going to the overview, 5 windows open",
|
||||
units: "frames / s" },
|
||||
overviewFps10Windows:
|
||||
{ description: "Frames rate when going to the overview, 10 windows open",
|
||||
units: "frames / s" },
|
||||
overviewFps5Maximized:
|
||||
{ description: "Frames rate when going to the overview, 5 maximized windows open",
|
||||
units: "frames / s" },
|
||||
overviewFps10Maximized:
|
||||
{ description: "Frames rate when going to the overview, 10 maximized windows open",
|
||||
units: "frames / s" },
|
||||
overviewFps5Alpha:
|
||||
{ description: "Frames rate when going to the overview, 5 alpha-transparent windows open",
|
||||
units: "frames / s" },
|
||||
overviewFps10Alpha:
|
||||
{ description: "Frames rate when going to the overview, 10 alpha-transparent windows open",
|
||||
units: "frames / s" },
|
||||
usedAfterOverview:
|
||||
{ description: "Malloc'ed bytes after the overview is shown once",
|
||||
units: "B" },
|
||||
leakedAfterOverview:
|
||||
{ description: "Additional malloc'ed bytes the second time the overview is shown",
|
||||
units: "B" },
|
||||
applicationsShowTimeFirst:
|
||||
{ description: "Time to switch to applications view, first time",
|
||||
units: "us" },
|
||||
applicationsShowTimeSubsequent:
|
||||
{ description: "Time to switch to applications view, second time",
|
||||
units: "us"}
|
||||
units: "B" }
|
||||
};
|
||||
|
||||
let WINDOW_CONFIGS = [
|
||||
{ width: 640, height: 480, alpha: false, maximized: false, count: 1, metric: 'overviewFpsSubsequent' },
|
||||
{ width: 640, height: 480, alpha: false, maximized: false, count: 5, metric: 'overviewFps5Windows' },
|
||||
{ width: 640, height: 480, alpha: false, maximized: false, count: 10, metric: 'overviewFps10Windows' },
|
||||
{ width: 640, height: 480, alpha: false, maximized: true, count: 5, metric: 'overviewFps5Maximized' },
|
||||
{ width: 640, height: 480, alpha: false, maximized: true, count: 10, metric: 'overviewFps10Maximized' },
|
||||
{ width: 640, height: 480, alpha: true, maximized: false, count: 5, metric: 'overviewFps5Alpha' },
|
||||
{ width: 640, height: 480, alpha: true, maximized: false, count: 10, metric: 'overviewFps10Alpha' }
|
||||
];
|
||||
|
||||
function run() {
|
||||
Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview");
|
||||
Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing");
|
||||
Scripting.defineScriptEvent("afterShowHide", "After a show/hide cycle for the overview");
|
||||
Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view");
|
||||
Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view");
|
||||
|
||||
Main.overview.connect('shown', function() {
|
||||
Scripting.scriptEvent('overviewShowDone');
|
||||
});
|
||||
|
||||
yield Scripting.sleep(1000);
|
||||
|
||||
for (let i = 0; i < 2 * WINDOW_CONFIGS.length; i++) {
|
||||
// We go to the overview twice for each configuration; the first time
|
||||
// to calculate the mipmaps for the windows, the second time to get
|
||||
// a clean set of numbers.
|
||||
if ((i % 2) == 0) {
|
||||
let config = WINDOW_CONFIGS[i / 2];
|
||||
yield Scripting.destroyTestWindows();
|
||||
|
||||
for (let k = 0; k < config.count; k++)
|
||||
yield Scripting.createTestWindow(config.width, config.height, config.alpha, config.maximized);
|
||||
|
||||
yield Scripting.waitTestWindows();
|
||||
yield Scripting.sleep(1000);
|
||||
yield Scripting.waitLeisure();
|
||||
}
|
||||
|
||||
yield Scripting.waitLeisure();
|
||||
for (let i = 0; i < 2; i++) {
|
||||
Scripting.scriptEvent('overviewShowStart');
|
||||
Main.overview.show();
|
||||
|
||||
@ -104,21 +53,6 @@ function run() {
|
||||
Scripting.collectStatistics();
|
||||
Scripting.scriptEvent('afterShowHide');
|
||||
}
|
||||
|
||||
yield Scripting.destroyTestWindows();
|
||||
yield Scripting.sleep(1000);
|
||||
|
||||
Main.overview.show();
|
||||
yield Scripting.waitLeisure();
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
Scripting.scriptEvent('applicationsShowStart');
|
||||
Main.overview.viewSelector.switchTab('applications');
|
||||
yield Scripting.waitLeisure();
|
||||
Scripting.scriptEvent('applicationsShowDone');
|
||||
Main.overview.viewSelector.switchTab('windows');
|
||||
yield Scripting.waitLeisure();
|
||||
}
|
||||
}
|
||||
|
||||
let showingOverview = false;
|
||||
@ -130,8 +64,6 @@ let mallocUsedSize = 0;
|
||||
let overviewShowCount = 0;
|
||||
let firstOverviewUsedSize;
|
||||
let haveSwapComplete = false;
|
||||
let applicationsShowStart;
|
||||
let applicationsShowCount = 0;
|
||||
|
||||
function script_overviewShowStart(time) {
|
||||
showingOverview = true;
|
||||
@ -147,18 +79,6 @@ function script_overviewShowDone(time) {
|
||||
finishedShowingOverview = true;
|
||||
}
|
||||
|
||||
function script_applicationsShowStart(time) {
|
||||
applicationsShowStart = time;
|
||||
}
|
||||
|
||||
function script_applicationsShowDone(time) {
|
||||
applicationsShowCount++;
|
||||
if (applicationsShowCount == 1)
|
||||
METRICS.applicationsShowTimeFirst.value = time - applicationsShowStart;
|
||||
else
|
||||
METRICS.applicationsShowTimeSubsequent.value = time - applicationsShowStart;
|
||||
}
|
||||
|
||||
function script_afterShowHide(time) {
|
||||
if (overviewShowCount == 1) {
|
||||
METRICS.usedAfterOverview.value = mallocUsedSize;
|
||||
@ -193,15 +113,9 @@ function _frameDone(time) {
|
||||
if (overviewShowCount == 1) {
|
||||
METRICS.overviewLatencyFirst.value = overviewLatency;
|
||||
METRICS.overviewFpsFirst.value = fps;
|
||||
} else if (overviewShowCount == 2) {
|
||||
} else {
|
||||
METRICS.overviewLatencySubsequent.value = overviewLatency;
|
||||
}
|
||||
|
||||
// Other than overviewFpsFirst, we collect FPS metrics the second
|
||||
// we show each window configuration. overviewShowCount is 1,2,3...
|
||||
if (overviewShowCount % 2 == 0) {
|
||||
let config = WINDOW_CONFIGS[(overviewShowCount / 2) - 1];
|
||||
METRICS[config.metric].value = fps;
|
||||
METRICS.overviewFpsSubsequent.value = fps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
97
js/prefs/clockPreferences.js
Normal file
@ -0,0 +1,97 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Gettext = imports.gettext;
|
||||
|
||||
const FORMAT_KEY = 'format';
|
||||
const SHOW_DATE_KEY = 'show-date';
|
||||
const SHOW_SECONDS_KEY = 'show-seconds';
|
||||
|
||||
|
||||
function ClockPreferences(uiFile) {
|
||||
this._init(uiFile);
|
||||
};
|
||||
|
||||
ClockPreferences.prototype = {
|
||||
_init: function(uiFile) {
|
||||
let builder = new Gtk.Builder();
|
||||
builder.add_from_file(uiFile);
|
||||
|
||||
this._dialog = builder.get_object('prefs-dialog');
|
||||
this._dialog.connect('response', Lang.bind(this, this._onResponse));
|
||||
|
||||
this._12hrRadio = builder.get_object('12hr_radio');
|
||||
this._24hrRadio = builder.get_object('24hr_radio');
|
||||
this._dateCheck = builder.get_object('date_check');
|
||||
this._secondsCheck = builder.get_object('seconds_check');
|
||||
|
||||
delete builder;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
|
||||
this._notifyId = this._settings.connect('changed',
|
||||
Lang.bind(this,
|
||||
this._updateDialog));
|
||||
|
||||
this._12hrRadio.connect('toggled', Lang.bind(this,
|
||||
function() {
|
||||
let format = this._12hrRadio.active ? '12-hour' : '24-hour';
|
||||
this._settings.set_string(FORMAT_KEY, format);
|
||||
}));
|
||||
this._dateCheck.connect('toggled', Lang.bind(this,
|
||||
function() {
|
||||
this._settings.set_boolean(SHOW_DATE_KEY,
|
||||
this._dateCheck.active);
|
||||
}));
|
||||
this._secondsCheck.connect('toggled', Lang.bind(this,
|
||||
function() {
|
||||
this._settings.set_boolean(SHOW_SECONDS_KEY,
|
||||
this._secondsCheck.active);
|
||||
}));
|
||||
|
||||
this._updateDialog();
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this._dialog.show_all();
|
||||
},
|
||||
|
||||
_updateDialog: function() {
|
||||
let format = this._settings.get_string(FORMAT_KEY);
|
||||
this._12hrRadio.active = (format == "12-hour");
|
||||
this._24hrRadio.active = (format == "24-hour");
|
||||
|
||||
this._dateCheck.active = this._settings.get_boolean(SHOW_DATE_KEY);
|
||||
this._secondsCheck.active = this._settings.get_boolean(SHOW_SECONDS_KEY);
|
||||
},
|
||||
|
||||
_onResponse: function() {
|
||||
this._dialog.destroy();
|
||||
this._settings.disconnect(this._notifyId);
|
||||
this.emit('destroy');
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ClockPreferences.prototype);
|
||||
|
||||
function main(params) {
|
||||
if ('progName' in params)
|
||||
GLib.set_prgname(params['progName']);
|
||||
if ('localeDir' in params)
|
||||
Gettext.bindtextdomain('gnome-shell', params['localeDir']);
|
||||
|
||||
Gtk.init(null, null);
|
||||
|
||||
let clockPrefs = new ClockPreferences(params['uiFile']);
|
||||
clockPrefs.connect('destroy',
|
||||
function() {
|
||||
Gtk.main_quit();
|
||||
});
|
||||
clockPrefs.show();
|
||||
|
||||
Gtk.main();
|
||||
}
|
253
js/ui/altTab.js
@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
@ -16,8 +15,6 @@ const POPUP_APPICON_SIZE = 96;
|
||||
const POPUP_SCROLL_TIME = 0.10; // seconds
|
||||
const POPUP_FADE_TIME = 0.1; // seconds
|
||||
|
||||
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
|
||||
|
||||
const DISABLE_HOVER_TIMEOUT = 500; // milliseconds
|
||||
|
||||
const THUMBNAIL_DEFAULT_SIZE = 256;
|
||||
@ -37,8 +34,7 @@ function AltTabPopup() {
|
||||
AltTabPopup.prototype = {
|
||||
_init : function() {
|
||||
this.actor = new Shell.GenericContainer({ name: 'altTabPopup',
|
||||
reactive: true,
|
||||
visible: false });
|
||||
reactive: true });
|
||||
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
@ -53,8 +49,6 @@ AltTabPopup.prototype = {
|
||||
this._thumbnailTimeoutId = 0;
|
||||
this._motionTimeoutId = 0;
|
||||
|
||||
this.thumbnailsVisible = false;
|
||||
|
||||
// Initially disable hover so we ignore the enter-event if
|
||||
// the switcher appears underneath the current pointer location
|
||||
this._disableHover();
|
||||
@ -87,7 +81,7 @@ AltTabPopup.prototype = {
|
||||
let [childMinHeight, childNaturalHeight] = this._appSwitcher.actor.get_preferred_height(primary.width - hPadding);
|
||||
let [childMinWidth, childNaturalWidth] = this._appSwitcher.actor.get_preferred_width(childNaturalHeight);
|
||||
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
|
||||
childBox.x2 = Math.min(primary.x + primary.width - hPadding, childBox.x1 + childNaturalWidth);
|
||||
childBox.x2 = Math.min(childBox.x1 + primary.width - hPadding, childBox.x1 + childNaturalWidth);
|
||||
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
|
||||
childBox.y2 = childBox.y1 + childNaturalHeight;
|
||||
this._appSwitcher.actor.allocate(childBox, flags);
|
||||
@ -121,7 +115,7 @@ AltTabPopup.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
show : function(backward, switch_group) {
|
||||
show : function(backward) {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
let apps = tracker.get_running_apps ('');
|
||||
|
||||
@ -132,13 +126,13 @@ AltTabPopup.prototype = {
|
||||
return false;
|
||||
this._haveModal = true;
|
||||
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
|
||||
this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
|
||||
this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
|
||||
this._keyReleaseEventId = global.stage.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
|
||||
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
|
||||
|
||||
this._appSwitcher = new AppSwitcher(apps, this);
|
||||
this._appSwitcher = new AppSwitcher(apps);
|
||||
this.actor.add_actor(this._appSwitcher.actor);
|
||||
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
|
||||
this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
|
||||
@ -146,16 +140,7 @@ AltTabPopup.prototype = {
|
||||
this._appIcons = this._appSwitcher.icons;
|
||||
|
||||
// Make the initial selection
|
||||
if (switch_group) {
|
||||
if (backward) {
|
||||
this._select(0, this._appIcons[0].cachedWindows.length - 1);
|
||||
} else {
|
||||
if (this._appIcons[0].cachedWindows.length > 1)
|
||||
this._select(0, 1);
|
||||
else
|
||||
this._select(0, 0);
|
||||
}
|
||||
} else if (this._appIcons.length == 1) {
|
||||
if (this._appIcons.length == 1) {
|
||||
if (!backward && this._appIcons[0].cachedWindows.length > 1) {
|
||||
// For compatibility with the multi-app case below
|
||||
this._select(0, 1, true);
|
||||
@ -227,38 +212,41 @@ AltTabPopup.prototype = {
|
||||
|
||||
_keyPressEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
let event_state = Shell.get_event_state(event);
|
||||
let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
|
||||
let action = global.screen.get_display().get_keybinding_action(event.get_key_code(), event_state);
|
||||
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
|
||||
// X allows servers to represent Shift+Tab in two different ways
|
||||
if (shift && keysym == Clutter.Tab)
|
||||
keysym = Clutter.ISO_Left_Tab;
|
||||
|
||||
this._disableHover();
|
||||
|
||||
if (action == Meta.KeyBindingAction.SWITCH_GROUP)
|
||||
this._select(this._currentApp, backwards ? this._previousWindow() : this._nextWindow());
|
||||
if (keysym == Clutter.grave)
|
||||
this._select(this._currentApp, this._nextWindow());
|
||||
else if (keysym == Clutter.asciitilde)
|
||||
this._select(this._currentApp, this._previousWindow());
|
||||
else if (keysym == Clutter.Escape)
|
||||
this.destroy();
|
||||
else if (this._thumbnailsFocused) {
|
||||
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS)
|
||||
if (backwards) {
|
||||
if (this._currentWindow == 0 || this._currentWindow == -1)
|
||||
this._select(this._previousApp());
|
||||
else
|
||||
this._select(this._currentApp, this._previousWindow());
|
||||
} else {
|
||||
if (this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
|
||||
this._select(this._nextApp());
|
||||
else
|
||||
this._select(this._currentApp, this._nextWindow());
|
||||
}
|
||||
else if (keysym == Clutter.Left)
|
||||
if (keysym == Clutter.Tab) {
|
||||
if (this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
|
||||
this._select(this._nextApp());
|
||||
else
|
||||
this._select(this._currentApp, this._nextWindow());
|
||||
} else if (keysym == Clutter.ISO_Left_Tab) {
|
||||
if (this._currentWindow == 0 || this._currentWindow == -1)
|
||||
this._select(this._previousApp());
|
||||
else
|
||||
this._select(this._currentApp, this._previousWindow());
|
||||
} else if (keysym == Clutter.Left)
|
||||
this._select(this._currentApp, this._previousWindow());
|
||||
else if (keysym == Clutter.Right)
|
||||
this._select(this._currentApp, this._nextWindow());
|
||||
else if (keysym == Clutter.Up)
|
||||
this._select(this._currentApp, null, true);
|
||||
} else {
|
||||
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS)
|
||||
this._select(backwards ? this._previousApp() : this._nextApp());
|
||||
if (keysym == Clutter.Tab)
|
||||
this._select(this._nextApp());
|
||||
else if (keysym == Clutter.ISO_Left_Tab)
|
||||
this._select(this._previousApp());
|
||||
else if (keysym == Clutter.Left)
|
||||
this._select(this._previousApp());
|
||||
else if (keysym == Clutter.Right)
|
||||
@ -376,35 +364,30 @@ AltTabPopup.prototype = {
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_popModal: function() {
|
||||
if (this._haveModal) {
|
||||
Main.popModal(this.actor);
|
||||
this._haveModal = false;
|
||||
}
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
this._popModal();
|
||||
if (this.actor.visible) {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: POPUP_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.actor.destroy();
|
||||
})
|
||||
});
|
||||
} else
|
||||
this.actor.destroy();
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: POPUP_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.actor.destroy();
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
this._popModal();
|
||||
if (this._haveModal)
|
||||
Main.popModal(this.actor);
|
||||
|
||||
if (this._thumbnails)
|
||||
this._destroyThumbnails();
|
||||
|
||||
if (this._keyPressEventId)
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
if (this._keyReleaseEventId)
|
||||
global.stage.disconnect(this._keyReleaseEventId);
|
||||
|
||||
if (this._motionTimeoutId != 0)
|
||||
Mainloop.source_remove(this._motionTimeoutId);
|
||||
if (this._thumbnailTimeoutId != 0)
|
||||
@ -475,15 +458,11 @@ AltTabPopup.prototype = {
|
||||
},
|
||||
|
||||
_destroyThumbnails : function() {
|
||||
let thumbnailsActor = this._thumbnails.actor;
|
||||
Tweener.addTween(thumbnailsActor,
|
||||
Tweener.addTween(this._thumbnails.actor,
|
||||
{ opacity: 0,
|
||||
time: THUMBNAIL_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
thumbnailsActor.destroy();
|
||||
this.thumbnailsVisible = false;
|
||||
})
|
||||
onComplete: function() { this.destroy(); }
|
||||
});
|
||||
this._thumbnails = null;
|
||||
},
|
||||
@ -499,8 +478,7 @@ AltTabPopup.prototype = {
|
||||
Tweener.addTween(this._thumbnails.actor,
|
||||
{ opacity: 255,
|
||||
time: THUMBNAIL_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function () { this.thumbnailsVisible = true; })
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -541,11 +519,16 @@ SwitcherList.prototype = {
|
||||
this._leftArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
|
||||
pseudo_class: 'highlighted' });
|
||||
this._leftArrow.connect('repaint', Lang.bind(this,
|
||||
function() { _drawArrow(this._leftArrow, St.Side.LEFT); }));
|
||||
function (area) {
|
||||
Shell.draw_box_pointer(area, Shell.PointerDirection.LEFT);
|
||||
}));
|
||||
|
||||
this._rightArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
|
||||
pseudo_class: 'highlighted' });
|
||||
this._rightArrow.connect('repaint', Lang.bind(this,
|
||||
function() { _drawArrow(this._rightArrow, St.Side.RIGHT); }));
|
||||
function (area) {
|
||||
Shell.draw_box_pointer(area, Shell.PointerDirection.RIGHT);
|
||||
}));
|
||||
|
||||
this.actor.add_actor(this._leftArrow);
|
||||
this.actor.add_actor(this._rightArrow);
|
||||
@ -602,27 +585,23 @@ SwitcherList.prototype = {
|
||||
},
|
||||
|
||||
addItem : function(item) {
|
||||
let bbox = new St.Button({ style_class: 'item-box',
|
||||
reactive: true });
|
||||
let bbox = new St.Clickable({ style_class: 'item-box',
|
||||
reactive: true });
|
||||
|
||||
bbox.set_child(item);
|
||||
this._list.add_actor(bbox);
|
||||
|
||||
let n = this._items.length;
|
||||
bbox.connect('clicked', Lang.bind(this, function() { this._onItemClicked(n); }));
|
||||
bbox.connect('enter-event', Lang.bind(this, function() { this._onItemEnter(n); }));
|
||||
bbox.connect('clicked', Lang.bind(this, function () {
|
||||
this._itemActivated(n);
|
||||
}));
|
||||
bbox.connect('enter-event', Lang.bind(this, function () {
|
||||
this._itemEntered(n);
|
||||
}));
|
||||
|
||||
this._items.push(bbox);
|
||||
},
|
||||
|
||||
_onItemClicked: function (index) {
|
||||
this._itemActivated(index);
|
||||
},
|
||||
|
||||
_onItemEnter: function (index) {
|
||||
this._itemEntered(index);
|
||||
},
|
||||
|
||||
addSeparator: function () {
|
||||
let box = new St.Bin({ style_class: 'separator' });
|
||||
this._separator = box;
|
||||
@ -630,18 +609,16 @@ SwitcherList.prototype = {
|
||||
},
|
||||
|
||||
highlight: function(index, justOutline) {
|
||||
if (this._highlighted != -1) {
|
||||
this._items[this._highlighted].remove_style_pseudo_class('outlined');
|
||||
this._items[this._highlighted].remove_style_pseudo_class('selected');
|
||||
}
|
||||
if (this._highlighted != -1)
|
||||
this._items[this._highlighted].style_class = 'item-box';
|
||||
|
||||
this._highlighted = index;
|
||||
|
||||
if (this._highlighted != -1) {
|
||||
if (justOutline)
|
||||
this._items[this._highlighted].add_style_pseudo_class('outlined');
|
||||
this._items[this._highlighted].style_class = 'outlined-item-box';
|
||||
else
|
||||
this._items[this._highlighted].add_style_pseudo_class('selected');
|
||||
this._items[this._highlighted].style_class = 'selected-item-box';
|
||||
}
|
||||
|
||||
let monitor = global.get_primary_monitor();
|
||||
@ -827,7 +804,7 @@ AppIcon.prototype = {
|
||||
this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
|
||||
vertical: true });
|
||||
this.icon = null;
|
||||
this._iconBin = new St.Bin({ x_fill: true, y_fill: true });
|
||||
this._iconBin = new St.Bin();
|
||||
|
||||
this.actor.add(this._iconBin, { x_fill: false, y_fill: false } );
|
||||
this.label = new St.Label({ text: this.app.get_name() });
|
||||
@ -841,14 +818,14 @@ AppIcon.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function AppSwitcher(apps, altTabPopup) {
|
||||
this._init(apps, altTabPopup);
|
||||
function AppSwitcher(apps) {
|
||||
this._init(apps);
|
||||
}
|
||||
|
||||
AppSwitcher.prototype = {
|
||||
__proto__ : SwitcherList.prototype,
|
||||
|
||||
_init : function(apps, altTabPopup) {
|
||||
_init : function(apps) {
|
||||
SwitcherList.prototype._init.call(this, true);
|
||||
|
||||
// Construct the AppIcons, sort by time, add to the popup
|
||||
@ -880,8 +857,6 @@ AppSwitcher.prototype = {
|
||||
|
||||
this._curApp = -1;
|
||||
this._iconSize = 0;
|
||||
this._altTabPopup = altTabPopup;
|
||||
this._mouseTimeOutId = 0;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||
@ -889,19 +864,17 @@ AppSwitcher.prototype = {
|
||||
while(this._items.length > 1 && this._items[j].style_class != 'item-box') {
|
||||
j++;
|
||||
}
|
||||
let themeNode = this._items[j].get_theme_node();
|
||||
let iconPadding = themeNode.get_horizontal_padding();
|
||||
let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT);
|
||||
let iconPadding = this._items[j].get_theme_node().get_horizontal_padding();
|
||||
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
|
||||
let iconSpacing = iconNaturalHeight + iconPadding + iconBorder;
|
||||
let iconSpacing = iconNaturalHeight + iconPadding;
|
||||
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||
if (this._separator)
|
||||
totalSpacing += this._separator.width + this._list.spacing;
|
||||
|
||||
// We just assume the whole screen here due to weirdness happing with the passed width
|
||||
let primary = global.get_primary_monitor();
|
||||
let focus = global.get_focus_monitor();
|
||||
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
|
||||
let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
|
||||
let availWidth = focus.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
|
||||
let height = 0;
|
||||
|
||||
for(let i = 0; i < iconSizes.length; i++) {
|
||||
@ -946,29 +919,6 @@ AppSwitcher.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
// We override SwitcherList's _onItemEnter method to delay
|
||||
// activation when the thumbnail list is open
|
||||
_onItemEnter: function (index) {
|
||||
if (this._mouseTimeOutId != 0)
|
||||
Mainloop.source_remove(this._mouseTimeOutId);
|
||||
if (this._altTabPopup.thumbnailsVisible) {
|
||||
this._mouseTimeOutId = Mainloop.timeout_add(APP_ICON_HOVER_TIMEOUT,
|
||||
Lang.bind(this, function () {
|
||||
this._enterItem(index);
|
||||
this._mouseTimeOutId = 0;
|
||||
return false;
|
||||
}));
|
||||
} else
|
||||
this._itemEntered(index);
|
||||
},
|
||||
|
||||
_enterItem: function(index) {
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
|
||||
if (this._items[index].contains(pickedActor))
|
||||
this._itemEntered(index);
|
||||
},
|
||||
|
||||
// We override SwitcherList's highlight() method to also deal with
|
||||
// the AppSwitcher->ThumbnailList arrows. Apps with only 1 window
|
||||
// will hide their arrows by default, but show them when their
|
||||
@ -1001,7 +951,10 @@ AppSwitcher.prototype = {
|
||||
|
||||
let n = this._arrows.length;
|
||||
let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
|
||||
arrow.connect('repaint', function() { _drawArrow(arrow, St.Side.BOTTOM); });
|
||||
arrow.connect('repaint', Lang.bind(this,
|
||||
function (area) {
|
||||
Shell.draw_box_pointer(area, Shell.PointerDirection.DOWN);
|
||||
}));
|
||||
this._list.add_actor(arrow);
|
||||
this._arrows.push(arrow);
|
||||
|
||||
@ -1087,9 +1040,6 @@ ThumbnailList.prototype = {
|
||||
|
||||
for (let i = 0; i < this._thumbnailBins.length; i++) {
|
||||
let mutterWindow = this._windows[i].get_compositor_private();
|
||||
if (!mutterWindow)
|
||||
continue;
|
||||
|
||||
let windowTexture = mutterWindow.get_texture ();
|
||||
let [width, height] = windowTexture.get_size();
|
||||
let scale = Math.min(1.0, THUMBNAIL_DEFAULT_SIZE / width, availHeight / height);
|
||||
@ -1107,46 +1057,3 @@ ThumbnailList.prototype = {
|
||||
this._thumbnailBins = new Array();
|
||||
}
|
||||
};
|
||||
|
||||
function _drawArrow(area, side) {
|
||||
let themeNode = area.get_theme_node();
|
||||
let borderColor = themeNode.get_border_color(side);
|
||||
let bodyColor = themeNode.get_foreground_color();
|
||||
|
||||
let [width, height] = area.get_surface_size ();
|
||||
let cr = area.get_context();
|
||||
|
||||
cr.setLineWidth(1.0);
|
||||
Clutter.cairo_set_source_color(cr, borderColor);
|
||||
|
||||
switch (side) {
|
||||
case St.Side.TOP:
|
||||
cr.moveTo(0, height);
|
||||
cr.lineTo(Math.floor(width * 0.5), 0);
|
||||
cr.lineTo(width, height);
|
||||
break;
|
||||
|
||||
case St.Side.BOTTOM:
|
||||
cr.moveTo(width, 0);
|
||||
cr.lineTo(Math.floor(width * 0.5), height);
|
||||
cr.lineTo(0, 0);
|
||||
break;
|
||||
|
||||
case St.Side.LEFT:
|
||||
cr.moveTo(width, height);
|
||||
cr.lineTo(0, Math.floor(height * 0.5));
|
||||
cr.lineTo(width, 0);
|
||||
break;
|
||||
|
||||
case St.Side.RIGHT:
|
||||
cr.moveTo(0, 0);
|
||||
cr.lineTo(width, Math.floor(height * 0.5));
|
||||
cr.lineTo(0, height);
|
||||
break;
|
||||
}
|
||||
|
||||
cr.strokePreserve();
|
||||
|
||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||
cr.fill();
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Lang = imports.lang;
|
||||
@ -23,7 +22,6 @@ const Workspace = imports.ui.workspace;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const MENU_POPUP_TIMEOUT = 600;
|
||||
const SCROLL_TIME = 0.1;
|
||||
|
||||
function AlphabeticalView() {
|
||||
this._init();
|
||||
@ -31,32 +29,10 @@ function AlphabeticalView() {
|
||||
|
||||
AlphabeticalView.prototype = {
|
||||
_init: function() {
|
||||
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
|
||||
this.actor = new St.BoxLayout({ vertical: true });
|
||||
this._grid = new IconGrid.IconGrid();
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
|
||||
this._filterApp = null;
|
||||
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
||||
|
||||
this.actor = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
y_align: St.Align.START,
|
||||
vfade: true });
|
||||
this.actor.add_actor(box);
|
||||
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||
function() {
|
||||
if (!this.actor.mapped)
|
||||
return;
|
||||
|
||||
let adjustment = this.actor.vscroll.adjustment;
|
||||
let direction = Overview.SwipeScrollDirection.VERTICAL;
|
||||
Main.overview.setScrollAdjustment(adjustment, direction);
|
||||
|
||||
// Reset scroll on mapping
|
||||
adjustment.value = 0;
|
||||
}));
|
||||
this.actor.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
||||
},
|
||||
|
||||
_removeAll: function() {
|
||||
@ -64,51 +40,20 @@ AlphabeticalView.prototype = {
|
||||
this._apps = [];
|
||||
},
|
||||
|
||||
_addApp: function(appInfo) {
|
||||
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
|
||||
_addApp: function(app) {
|
||||
let appIcon = new AppWellIcon(this._appSystem.get_app(app.get_id()));
|
||||
appIcon.connect('launching', Lang.bind(this, function() {
|
||||
this.emit('launching');
|
||||
}));
|
||||
appIcon._draggable.connect('drag-begin', Lang.bind(this, function() {
|
||||
this.emit('drag-begin');
|
||||
}));
|
||||
|
||||
this._grid.addItem(appIcon.actor);
|
||||
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
|
||||
|
||||
appIcon._appInfo = appInfo;
|
||||
if (this._filterApp && !this._filterApp(appInfo))
|
||||
appIcon.actor.hide();
|
||||
|
||||
this._apps.push(appIcon);
|
||||
},
|
||||
|
||||
_ensureIconVisible: function(icon) {
|
||||
let adjustment = this.actor.vscroll.adjustment;
|
||||
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
|
||||
|
||||
let offset = 0;
|
||||
let vfade = this.actor.get_effect("vfade");
|
||||
if (vfade)
|
||||
offset = vfade.fade_offset;
|
||||
|
||||
// If this gets called as part of a right-click, the actor
|
||||
// will be needs_allocation, and so "icon.y" would return 0
|
||||
let box = icon.get_allocation_box();
|
||||
|
||||
if (box.y1 < value + offset)
|
||||
value = Math.max(0, box.y1 - offset);
|
||||
else if (box.y2 > value + pageSize - offset)
|
||||
value = Math.min(upper, box.y2 + offset - pageSize);
|
||||
else
|
||||
return;
|
||||
|
||||
Tweener.addTween(adjustment,
|
||||
{ value: value,
|
||||
time: SCROLL_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
setFilter: function(filter) {
|
||||
this._filterApp = filter;
|
||||
for (let i = 0; i < this._apps.length; i++)
|
||||
this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
|
||||
},
|
||||
|
||||
refresh: function(apps) {
|
||||
let ids = [];
|
||||
for (let i in apps)
|
||||
@ -125,6 +70,8 @@ AlphabeticalView.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(AlphabeticalView.prototype);
|
||||
|
||||
function ViewByCategories() {
|
||||
this._init();
|
||||
}
|
||||
@ -132,121 +79,59 @@ function ViewByCategories() {
|
||||
ViewByCategories.prototype = {
|
||||
_init: function() {
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this.actor = new St.BoxLayout({ style_class: 'all-app' });
|
||||
this.actor = new St.BoxLayout({ vertical: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._view = new AlphabeticalView();
|
||||
|
||||
// categories can be -1 (the All view) or 0...n-1, where n
|
||||
// is the number of sections
|
||||
// -2 is a flag to indicate that nothing is selected
|
||||
// (used only before the actor is mapped the first time)
|
||||
this._currentCategory = -2;
|
||||
this._filters = new St.BoxLayout({ vertical: true, reactive: true });
|
||||
this._filters.connect('scroll-event', Lang.bind(this, this._scrollFilter));
|
||||
|
||||
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
|
||||
this.actor.add(this._filters, { expand: false, y_fill: false, y_align: St.Align.START });
|
||||
|
||||
// Always select the "All" filter when switching to the app view
|
||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||
function() {
|
||||
if (this.actor.mapped && this._allFilter)
|
||||
this._selectCategory(-1);
|
||||
}));
|
||||
|
||||
this._sections = [];
|
||||
|
||||
// We need a dummy actor to catch the keyboard focus if the
|
||||
// user Ctrl-Alt-Tabs here before the deferred work creates
|
||||
// our real contents
|
||||
this._focusDummy = new St.Bin({ can_focus: true });
|
||||
this.actor.add(this._focusDummy);
|
||||
},
|
||||
|
||||
_scrollFilter: function(actor, event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
if (direction == Clutter.ScrollDirection.UP)
|
||||
this._selectCategory(Math.max(this._currentCategory - 1, -1))
|
||||
else if (direction == Clutter.ScrollDirection.DOWN)
|
||||
this._selectCategory(Math.min(this._currentCategory + 1, this._sections.length - 1));
|
||||
},
|
||||
|
||||
_selectCategory: function(num) {
|
||||
if (this._currentCategory == num) // nothing to do
|
||||
return;
|
||||
|
||||
this._currentCategory = num;
|
||||
|
||||
if (num != -1)
|
||||
this._allFilter.remove_style_pseudo_class('selected');
|
||||
else
|
||||
this._allFilter.add_style_pseudo_class('selected');
|
||||
|
||||
this._view.setFilter(Lang.bind(this, function(app) {
|
||||
if (num == -1)
|
||||
return true;
|
||||
return this._sections[num].name == app.get_section();
|
||||
}));
|
||||
|
||||
for (let i = 0; i < this._sections.length; i++) {
|
||||
if (i == num)
|
||||
this._sections[i].filterActor.add_style_pseudo_class('selected');
|
||||
else
|
||||
this._sections[i].filterActor.remove_style_pseudo_class('selected');
|
||||
}
|
||||
},
|
||||
|
||||
_addFilter: function(name, num) {
|
||||
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
||||
style_class: 'app-filter',
|
||||
x_align: St.Align.START,
|
||||
can_focus: true });
|
||||
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
this._selectCategory(num);
|
||||
}));
|
||||
|
||||
if (num != -1)
|
||||
this._sections[num] = { filterActor: button,
|
||||
name: name };
|
||||
else
|
||||
this._allFilter = button;
|
||||
},
|
||||
|
||||
_removeAll: function() {
|
||||
this._sections = [];
|
||||
this._filters.destroy_children();
|
||||
},
|
||||
|
||||
refresh: function(apps) {
|
||||
_updateSections: function(apps) {
|
||||
this._removeAll();
|
||||
|
||||
let sections = this._appSystem.get_sections();
|
||||
this._apps = apps;
|
||||
this._view.refresh(apps);
|
||||
|
||||
/* Translators: Filter to display all applications */
|
||||
this._addFilter(_("All"), -1);
|
||||
|
||||
if (!sections)
|
||||
return;
|
||||
for (let i = 0; i < sections.length; i++) {
|
||||
if (i) {
|
||||
let actor = new St.Bin({ style_class: 'app-section-divider' });
|
||||
let divider = new St.Bin({ style_class: 'app-section-divider-container',
|
||||
child: actor,
|
||||
x_fill: true });
|
||||
|
||||
for (let i = 0; i < sections.length; i++)
|
||||
this._addFilter(sections[i], i);
|
||||
this.actor.add(divider, { y_fill: false, expand: true });
|
||||
}
|
||||
let _apps = apps.filter(function(app) {
|
||||
return app.get_section() == sections[i];
|
||||
});
|
||||
this._sections[i] = { view: new AlphabeticalView(),
|
||||
apps: _apps,
|
||||
name: sections[i] };
|
||||
this._sections[i].view.connect('launching', Lang.bind(this, function() {
|
||||
this.emit('launching');
|
||||
}));
|
||||
this._sections[i].view.connect('drag-begin', Lang.bind(this, function() {
|
||||
this.emit('drag-begin');
|
||||
}));
|
||||
this.actor.add(this._sections[i].view.actor, { y_align: St.Align.START, expand: true });
|
||||
}
|
||||
},
|
||||
|
||||
this._selectCategory(-1);
|
||||
_removeAll: function() {
|
||||
this.actor.destroy_children();
|
||||
this._sections.forEach(function (section) { section.view.disconnectAll(); });
|
||||
|
||||
if (this._focusDummy) {
|
||||
let focused = this._focusDummy.has_key_focus();
|
||||
this._focusDummy.destroy();
|
||||
this._focusDummy = null;
|
||||
if (focused)
|
||||
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
this._sections = [];
|
||||
},
|
||||
|
||||
refresh: function(apps) {
|
||||
this._updateSections(apps);
|
||||
for (let i = 0; i < this._sections.length; i++) {
|
||||
this._sections[i].view.refresh(this._sections[i].apps);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(ViewByCategories.prototype);
|
||||
|
||||
/* This class represents a display containing a collection of application items.
|
||||
* The applications are sorted based on their name.
|
||||
*/
|
||||
@ -261,8 +146,17 @@ AllAppDisplay.prototype = {
|
||||
Main.queueDeferredWork(this._workId);
|
||||
}));
|
||||
|
||||
this._scrollView = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
vshadows: true });
|
||||
this.actor = new St.Bin({ style_class: 'all-app',
|
||||
y_align: St.Align.START,
|
||||
child: this._scrollView });
|
||||
|
||||
this._appView = new ViewByCategories();
|
||||
this.actor = new St.Bin({ child: this._appView.actor, x_fill: true, y_fill: true });
|
||||
this._scrollView.add_actor(this._appView.actor);
|
||||
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
|
||||
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
||||
},
|
||||
@ -275,6 +169,8 @@ AllAppDisplay.prototype = {
|
||||
this._appView.refresh(apps);
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(AllAppDisplay.prototype);
|
||||
|
||||
|
||||
function BaseAppSearchProvider() {
|
||||
this._init();
|
||||
@ -294,26 +190,17 @@ BaseAppSearchProvider.prototype = {
|
||||
return null;
|
||||
return { 'id': resultId,
|
||||
'name': app.get_name(),
|
||||
'createIcon': function(size) {
|
||||
return app.create_icon_texture(size);
|
||||
}
|
||||
};
|
||||
'icon': app.create_icon_texture(Search.RESULT_ICON_SIZE)};
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
|
||||
activateResult: function(id) {
|
||||
let app = this._appSys.get_app(id);
|
||||
app.activate(params.workspace ? params.workspace.index() : -1);
|
||||
app.activate();
|
||||
},
|
||||
|
||||
dragActivateResult: function(id, params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
|
||||
dragActivateResult: function(id) {
|
||||
let app = this._appSys.get_app(id);
|
||||
app.open_new_window(params.workspace ? params.workspace.get_index() : -1);
|
||||
app.open_new_window();
|
||||
}
|
||||
};
|
||||
|
||||
@ -340,6 +227,10 @@ AppSearchProvider.prototype = {
|
||||
let app = this._appSys.get_app(resultMeta['id']);
|
||||
let icon = new AppWellIcon(app);
|
||||
return icon.actor;
|
||||
},
|
||||
|
||||
expandSearch: function(terms) {
|
||||
log('TODO expand search');
|
||||
}
|
||||
};
|
||||
|
||||
@ -351,7 +242,7 @@ PrefsSearchProvider.prototype = {
|
||||
__proto__: BaseAppSearchProvider.prototype,
|
||||
|
||||
_init: function() {
|
||||
BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
|
||||
BaseAppSearchProvider.prototype._init.call(this, _("PREFERENCES"));
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
@ -360,24 +251,30 @@ PrefsSearchProvider.prototype = {
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._appSys.subsearch(true, previousResults, terms);
|
||||
},
|
||||
|
||||
expandSearch: function(terms) {
|
||||
let controlCenter = this._appSys.load_from_desktop_file('gnomecc.desktop');
|
||||
controlCenter.launch();
|
||||
Main.overview.hide();
|
||||
}
|
||||
};
|
||||
|
||||
function AppIcon(app, params) {
|
||||
this._init(app, params);
|
||||
function AppIcon(app) {
|
||||
this._init(app);
|
||||
}
|
||||
|
||||
AppIcon.prototype = {
|
||||
__proto__: IconGrid.BaseIcon.prototype,
|
||||
|
||||
_init : function(app, params) {
|
||||
_init : function(app) {
|
||||
this.app = app;
|
||||
|
||||
let label = this.app.get_name();
|
||||
|
||||
IconGrid.BaseIcon.prototype._init.call(this,
|
||||
label,
|
||||
params);
|
||||
{ setSizeManually: true });
|
||||
},
|
||||
|
||||
createIcon: function(iconSize) {
|
||||
@ -385,27 +282,23 @@ AppIcon.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function AppWellIcon(app, iconParams) {
|
||||
this._init(app, iconParams);
|
||||
function AppWellIcon(app) {
|
||||
this._init(app);
|
||||
}
|
||||
|
||||
AppWellIcon.prototype = {
|
||||
_init : function(app, iconParams) {
|
||||
_init : function(app) {
|
||||
this.app = app;
|
||||
this.actor = new St.Button({ style_class: 'app-well-app',
|
||||
reactive: true,
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
|
||||
can_focus: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor = new St.Clickable({ style_class: 'app-well-app',
|
||||
reactive: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this.icon = new AppIcon(app, iconParams);
|
||||
this.icon = new AppIcon(app);
|
||||
this.actor.set_child(this.icon.actor);
|
||||
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
|
||||
|
||||
this._menu = null;
|
||||
this._menuManager = new PopupMenu.PopupMenuManager(this);
|
||||
@ -416,15 +309,12 @@ AppWellIcon.prototype = {
|
||||
this._removeMenuTimeout();
|
||||
Main.overview.beginItemDrag(this);
|
||||
}));
|
||||
this._draggable.connect('drag-cancelled', Lang.bind(this,
|
||||
function () {
|
||||
Main.overview.cancelledItemDrag(this);
|
||||
}));
|
||||
this._draggable.connect('drag-end', Lang.bind(this,
|
||||
function () {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._menuTimeoutId = 0;
|
||||
@ -463,34 +353,29 @@ AppWellIcon.prototype = {
|
||||
Lang.bind(this, function() {
|
||||
this.popupMenu();
|
||||
}));
|
||||
} else if (button == 3) {
|
||||
this.popupMenu();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_onClicked: function(actor, button) {
|
||||
_onClicked: function(actor, event) {
|
||||
this._removeMenuTimeout();
|
||||
|
||||
let button = event.get_button();
|
||||
if (button == 1) {
|
||||
this._onActivate(Clutter.get_current_event());
|
||||
this._onActivate(event);
|
||||
} else if (button == 2) {
|
||||
// Last workspace is always empty
|
||||
let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
|
||||
launchWorkspace.activate(global.get_current_time());
|
||||
this.emit('launching');
|
||||
this.app.open_new_window(-1);
|
||||
Main.overview.hide();
|
||||
let newWorkspace = Main.overview.workspaces.addWorkspace();
|
||||
if (newWorkspace != null) {
|
||||
newWorkspace.activate(global.get_current_time());
|
||||
this.emit('launching');
|
||||
this.app.open_new_window();
|
||||
Main.overview.hide();
|
||||
}
|
||||
} else if (button == 3) {
|
||||
this.popupMenu();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_onKeyboardPopupMenu: function() {
|
||||
this.popupMenu();
|
||||
this._menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
getId: function() {
|
||||
return this.app.get_id();
|
||||
},
|
||||
@ -501,35 +386,67 @@ AppWellIcon.prototype = {
|
||||
|
||||
if (!this._menu) {
|
||||
this._menu = new AppIconMenu(this);
|
||||
this._menu.connect('highlight-window', Lang.bind(this, function (menu, window) {
|
||||
this.highlightWindow(window);
|
||||
}));
|
||||
this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
|
||||
this.activateWindow(window);
|
||||
}));
|
||||
this._menu.connect('popup', Lang.bind(this, function (menu, isPoppedUp) {
|
||||
if (!isPoppedUp)
|
||||
if (isPoppedUp) {
|
||||
this._onMenuPoppedUp();
|
||||
} else {
|
||||
this._onMenuPoppedDown();
|
||||
}
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function () { this._menu.close(); }));
|
||||
|
||||
this._menuManager.addMenu(this._menu);
|
||||
}
|
||||
|
||||
this.actor.set_hover(true);
|
||||
this.actor.show_tooltip();
|
||||
this._menu.popup();
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
highlightWindow: function(metaWindow) {
|
||||
if (this._didActivateWindow)
|
||||
return;
|
||||
if (!this._getRunning())
|
||||
return;
|
||||
Main.overview.getWorkspacesForWindow(metaWindow).setHighlightWindow(metaWindow);
|
||||
},
|
||||
|
||||
activateWindow: function(metaWindow) {
|
||||
if (metaWindow) {
|
||||
this._didActivateWindow = true;
|
||||
Main.activateWindow(metaWindow);
|
||||
} else {
|
||||
Main.overview.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_onMenuPoppedUp: function() {
|
||||
if (this._getRunning()) {
|
||||
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id());
|
||||
this._setWindowSelection = true;
|
||||
this._didActivateWindow = false;
|
||||
}
|
||||
},
|
||||
|
||||
_onMenuPoppedDown: function() {
|
||||
this.actor.sync_hover();
|
||||
|
||||
if (this._didActivateWindow)
|
||||
return;
|
||||
if (!this._setWindowSelection)
|
||||
return;
|
||||
|
||||
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(null);
|
||||
this._setWindowSelection = false;
|
||||
},
|
||||
|
||||
_getRunning: function() {
|
||||
return this.app.state != Shell.AppState.STOPPED;
|
||||
},
|
||||
|
||||
_onActivate: function (event) {
|
||||
@ -538,22 +455,24 @@ AppWellIcon.prototype = {
|
||||
|
||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK
|
||||
&& this.app.state == Shell.AppState.RUNNING) {
|
||||
this.app.open_new_window(-1);
|
||||
this.app.open_new_window();
|
||||
} else {
|
||||
this.app.activate(-1);
|
||||
this.app.activate();
|
||||
}
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
shellWorkspaceLaunch : function(params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
// called by this._menuManager when it has the grab
|
||||
menuEventFilter: function(event) {
|
||||
return this._menu.menuEventFilter(event);
|
||||
},
|
||||
|
||||
this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
|
||||
shellWorkspaceLaunch : function() {
|
||||
this.app.open_new_window();
|
||||
},
|
||||
|
||||
getDragActor: function() {
|
||||
return this.app.create_icon_texture(Main.overview.dash.iconSize);
|
||||
return this.app.create_icon_texture(this.icon.iconSize);
|
||||
},
|
||||
|
||||
// Returns the original actor that should align with the actor
|
||||
@ -572,17 +491,11 @@ AppIconMenu.prototype = {
|
||||
__proto__: PopupMenu.PopupMenu.prototype,
|
||||
|
||||
_init: function(source) {
|
||||
let side = St.Side.LEFT;
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
side = St.Side.RIGHT;
|
||||
|
||||
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
|
||||
|
||||
// We want to keep the item hovered while the menu is up
|
||||
this.blockSourceEvents = true;
|
||||
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, St.Align.MIDDLE, St.Side.LEFT, 0);
|
||||
|
||||
this._source = source;
|
||||
|
||||
this.connect('active-changed', Lang.bind(this, this._onActiveChanged));
|
||||
this.connect('activate', Lang.bind(this, this._onActivate));
|
||||
this.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
|
||||
|
||||
@ -622,12 +535,14 @@ AppIconMenu.prototype = {
|
||||
|
||||
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
|
||||
|
||||
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
|
||||
this._appendSeparator();
|
||||
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(_("New Window")) : null;
|
||||
|
||||
if (windows.length > 0)
|
||||
this._appendSeparator();
|
||||
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
|
||||
: _("Add to Favorites"));
|
||||
|
||||
this._highlightedItem = null;
|
||||
},
|
||||
|
||||
_appendSeparator: function () {
|
||||
@ -651,16 +566,74 @@ AppIconMenu.prototype = {
|
||||
if (open) {
|
||||
this.emit('popup', true);
|
||||
} else {
|
||||
this._updateHighlight(null);
|
||||
this.emit('popup', false);
|
||||
}
|
||||
},
|
||||
|
||||
// called by this._menuManager when it has the grab
|
||||
menuEventFilter: function(event) {
|
||||
let eventType = event.type();
|
||||
|
||||
// Check if the user is interacting with a window representation
|
||||
// rather than interacting with the menu
|
||||
|
||||
if (eventType == Clutter.EventType.BUTTON_RELEASE) {
|
||||
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
||||
if (metaWindow)
|
||||
this.emit('activate-window', metaWindow);
|
||||
} else if (eventType == Clutter.EventType.ENTER) {
|
||||
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
||||
if (metaWindow)
|
||||
this._selectMenuItemForWindow(metaWindow, true);
|
||||
} else if (eventType == Clutter.EventType.LEAVE) {
|
||||
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
||||
if (metaWindow)
|
||||
this._selectMenuItemForWindow(metaWindow, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_findMetaWindowForActor: function (actor) {
|
||||
if (actor._delegate instanceof Workspace.WindowClone)
|
||||
return actor._delegate.metaWindow;
|
||||
else if (actor.get_meta_window)
|
||||
return actor.get_meta_window();
|
||||
return null;
|
||||
},
|
||||
|
||||
_updateHighlight: function (item) {
|
||||
if (this._highlightedItem)
|
||||
this.emit('highlight-window', null);
|
||||
this._highlightedItem = item;
|
||||
if (this._highlightedItem) {
|
||||
let window = this._highlightedItem._window;
|
||||
if (window)
|
||||
this.emit('highlight-window', window);
|
||||
}
|
||||
},
|
||||
|
||||
_selectMenuItemForWindow: function (metaWindow, selected) {
|
||||
let items = this.getMenuItems();
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let item = items[i];
|
||||
let menuMetaWindow = item._window;
|
||||
if (menuMetaWindow == metaWindow)
|
||||
item.setActive(selected);
|
||||
}
|
||||
},
|
||||
|
||||
_onActiveChanged: function (menu, child) {
|
||||
this._updateHighlight(child);
|
||||
},
|
||||
|
||||
_onActivate: function (actor, child) {
|
||||
if (child._window) {
|
||||
let metaWindow = child._window;
|
||||
this.emit('activate-window', metaWindow);
|
||||
} else if (child == this._newWindowMenuItem) {
|
||||
this._source.app.open_new_window(-1);
|
||||
this._source.app.open_new_window();
|
||||
this.emit('activate-window', null);
|
||||
} else if (child == this._toggleFavoriteMenuItem) {
|
||||
let favs = AppFavorites.getAppFavorites();
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
@ -12,7 +11,7 @@ const POPUP_ANIMATION_TIME = 0.15;
|
||||
|
||||
/**
|
||||
* BoxPointer:
|
||||
* @side: side to draw the arrow on
|
||||
* @side: A St.Side type; currently only St.Side.TOP is implemented
|
||||
* @binProperties: Properties to set on contained bin
|
||||
*
|
||||
* An actor which displays a triangle "arrow" pointing to a given
|
||||
@ -41,80 +40,76 @@ BoxPointer.prototype = {
|
||||
this._border.connect('repaint', Lang.bind(this, this._drawBorder));
|
||||
this._container.add_actor(this._border);
|
||||
this.bin.raise(this._border);
|
||||
this._xOffset = 0;
|
||||
this._yOffset = 0;
|
||||
this._xPosition = 0;
|
||||
this._yPosition = 0;
|
||||
},
|
||||
|
||||
show: function(animate, onComplete) {
|
||||
animateAppear: function(onComplete) {
|
||||
let x = this.actor.x;
|
||||
let y = this.actor.y;
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let rise = themeNode.get_length('-arrow-rise');
|
||||
|
||||
this.opacity = 0;
|
||||
this.actor.opacity = 0;
|
||||
this.actor.show();
|
||||
|
||||
if (animate) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this.yOffset = -rise;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
this.yOffset = rise;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
this.xOffset = -rise;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
this.xOffset = rise;
|
||||
break;
|
||||
}
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this.actor.y -= rise;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
this.actor.y += rise;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
this.actor.x -= rise;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
this.actor.x += rise;
|
||||
break;
|
||||
}
|
||||
|
||||
Tweener.addTween(this, { opacity: 255,
|
||||
xOffset: 0,
|
||||
yOffset: 0,
|
||||
transition: "linear",
|
||||
onComplete: onComplete,
|
||||
time: POPUP_ANIMATION_TIME });
|
||||
Tweener.addTween(this.actor, { opacity: 255,
|
||||
x: x,
|
||||
y: y,
|
||||
transition: "linear",
|
||||
onComplete: onComplete,
|
||||
time: POPUP_ANIMATION_TIME });
|
||||
},
|
||||
|
||||
hide: function(animate, onComplete) {
|
||||
let xOffset = 0;
|
||||
let yOffset = 0;
|
||||
animateDisappear: function(onComplete) {
|
||||
let x = this.actor.x;
|
||||
let y = this.actor.y;
|
||||
let originalX = this.actor.x;
|
||||
let originalY = this.actor.y;
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let rise = themeNode.get_length('-arrow-rise');
|
||||
|
||||
if (animate) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
yOffset = rise;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
yOffset = -rise;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
xOffset = rise;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
xOffset = -rise;
|
||||
break;
|
||||
}
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
y += rise;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
y -= rise;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
x += rise;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
x -= rise;
|
||||
break;
|
||||
}
|
||||
|
||||
Tweener.addTween(this, { opacity: 0,
|
||||
xOffset: xOffset,
|
||||
yOffset: yOffset,
|
||||
transition: "linear",
|
||||
time: POPUP_ANIMATION_TIME,
|
||||
onComplete: Lang.bind(this, function () {
|
||||
this.actor.hide();
|
||||
this.xOffset = 0;
|
||||
this.yOffset = 0;
|
||||
if (onComplete)
|
||||
onComplete();
|
||||
})
|
||||
});
|
||||
Tweener.addTween(this.actor, { opacity: 0,
|
||||
x: x,
|
||||
y: y,
|
||||
transition: "linear",
|
||||
time: POPUP_ANIMATION_TIME,
|
||||
onComplete: Lang.bind(this, function () {
|
||||
this.actor.hide();
|
||||
this.actor.x = originalX;
|
||||
this.actor.y = originalY;
|
||||
if (onComplete)
|
||||
onComplete();
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
_adjustAllocationForArrow: function(isWidth, alloc) {
|
||||
@ -177,9 +172,6 @@ BoxPointer.prototype = {
|
||||
break;
|
||||
}
|
||||
this.bin.allocate(childBox, flags);
|
||||
|
||||
if (this._sourceActor && this._sourceActor.mapped)
|
||||
this._reposition(this._sourceActor, this._gap, this._alignment);
|
||||
},
|
||||
|
||||
_drawBorder: function(area) {
|
||||
@ -193,8 +185,10 @@ BoxPointer.prototype = {
|
||||
let halfBorder = borderWidth / 2;
|
||||
let halfBase = Math.floor(base/2);
|
||||
|
||||
let borderColor = themeNode.get_color('-arrow-border-color');
|
||||
let backgroundColor = themeNode.get_color('-arrow-background-color');
|
||||
let borderColor = new Clutter.Color();
|
||||
themeNode.get_color('-arrow-border-color', borderColor);
|
||||
let backgroundColor = new Clutter.Color();
|
||||
themeNode.get_color('-arrow-background-color', backgroundColor);
|
||||
|
||||
let [width, height] = area.get_surface_size();
|
||||
let [boxWidth, boxHeight] = [width, height];
|
||||
@ -214,88 +208,46 @@ BoxPointer.prototype = {
|
||||
cr.translate(rise, 0);
|
||||
}
|
||||
|
||||
let [x1, y1] = [halfBorder, halfBorder];
|
||||
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
|
||||
cr.moveTo(borderRadius, halfBorder);
|
||||
|
||||
cr.moveTo(x1 + borderRadius, y1);
|
||||
if (this._arrowSide == St.Side.TOP) {
|
||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
|
||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
} else {
|
||||
cr.lineTo(this._arrowOrigin - halfBase, y1);
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
cr.lineTo(this._arrowOrigin + halfBase, y1);
|
||||
}
|
||||
cr.lineTo(this._arrowOrigin - halfBase, halfBorder);
|
||||
cr.lineTo(this._arrowOrigin, halfBorder - rise);
|
||||
cr.lineTo(this._arrowOrigin + halfBase, halfBorder);
|
||||
}
|
||||
cr.lineTo(boxWidth - borderRadius, halfBorder);
|
||||
|
||||
cr.lineTo(x2 - borderRadius, y1);
|
||||
|
||||
// top-right corner
|
||||
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
||||
cr.arc(boxWidth - borderRadius - halfBorder, borderRadius + halfBorder, borderRadius,
|
||||
3*Math.PI/2, Math.PI*2);
|
||||
|
||||
if (this._arrowSide == St.Side.RIGHT) {
|
||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
} else {
|
||||
cr.lineTo(x2, this._arrowOrigin - halfBase);
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
cr.lineTo(x2, this._arrowOrigin + halfBase);
|
||||
}
|
||||
cr.lineTo(boxWidth - halfBorder, this._arrowOrigin - halfBase);
|
||||
cr.lineTo(boxWidth - halfBorder + rise, this._arrowOrigin);
|
||||
cr.lineTo(boxWidth - halfBorder, this._arrowOrigin + halfBase);
|
||||
}
|
||||
cr.lineTo(boxWidth - halfBorder, boxHeight - borderRadius);
|
||||
|
||||
cr.lineTo(x2, y2 - borderRadius);
|
||||
|
||||
// bottom-right corner
|
||||
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
||||
cr.arc(boxWidth - borderRadius - halfBorder, boxHeight - borderRadius - halfBorder, borderRadius,
|
||||
0, Math.PI/2);
|
||||
|
||||
if (this._arrowSide == St.Side.BOTTOM) {
|
||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
|
||||
} else {
|
||||
cr.lineTo(this._arrowOrigin + halfBase, y2);
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
cr.lineTo(this._arrowOrigin - halfBase, y2);
|
||||
}
|
||||
cr.lineTo(this._arrowOrigin + halfBase, boxHeight - halfBorder);
|
||||
cr.lineTo(this._arrowOrigin, boxHeight - halfBorder + rise);
|
||||
cr.lineTo(this._arrowOrigin - halfBase, boxHeight - halfBorder);
|
||||
}
|
||||
cr.lineTo(borderRadius, boxHeight - halfBorder);
|
||||
|
||||
cr.lineTo(x1 + borderRadius, y2);
|
||||
|
||||
// bottom-left corner
|
||||
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
||||
cr.arc(borderRadius + halfBorder, boxHeight - borderRadius - halfBorder, borderRadius,
|
||||
Math.PI/2, Math.PI);
|
||||
|
||||
if (this._arrowSide == St.Side.LEFT) {
|
||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
||||
} else {
|
||||
cr.lineTo(x1, this._arrowOrigin + halfBase);
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
cr.lineTo(x1, this._arrowOrigin - halfBase);
|
||||
}
|
||||
cr.lineTo(halfBorder, this._arrowOrigin + halfBase);
|
||||
cr.lineTo(halfBorder - rise, this._arrowOrigin);
|
||||
cr.lineTo(halfBorder, this._arrowOrigin - halfBase);
|
||||
}
|
||||
cr.lineTo(halfBorder, borderRadius);
|
||||
|
||||
cr.lineTo(x1, y1 + borderRadius);
|
||||
|
||||
// top-left corner
|
||||
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
||||
cr.arc(borderRadius + halfBorder, borderRadius + halfBorder, borderRadius,
|
||||
Math.PI, 3*Math.PI/2);
|
||||
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
@ -310,47 +262,32 @@ BoxPointer.prototype = {
|
||||
// so that we can query the correct size.
|
||||
this.actor.show();
|
||||
|
||||
this._sourceActor = sourceActor;
|
||||
this._gap = gap;
|
||||
this._alignment = alignment;
|
||||
|
||||
this._reposition(sourceActor, gap, alignment);
|
||||
},
|
||||
|
||||
_reposition: function(sourceActor, gap, alignment) {
|
||||
// Position correctly relative to the sourceActor
|
||||
let sourceNode = sourceActor.get_theme_node();
|
||||
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
||||
let sourceAllocation = Shell.util_get_transformed_allocation(sourceActor);
|
||||
let sourceCenterX = sourceAllocation.x1 + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) / 2;
|
||||
let sourceCenterY = sourceAllocation.y1 + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) / 2;
|
||||
let [sourceX, sourceY] = sourceActor.get_transformed_position();
|
||||
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
|
||||
|
||||
let [minWidth, minHeight, natWidth, natHeight] = this.actor.get_preferred_size();
|
||||
|
||||
// We also want to keep it onscreen, and separated from the
|
||||
// edge by the same distance as the main part of the box is
|
||||
// separated from its sourceActor
|
||||
let primary = global.get_primary_monitor();
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let borderWidth = themeNode.get_length('-arrow-border-width');
|
||||
let arrowBase = themeNode.get_length('-arrow-base');
|
||||
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
||||
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
||||
let halfMargin = margin / 2;
|
||||
let arrowRise = this.actor.get_theme_node().get_length('-arrow-rise');
|
||||
|
||||
let resX, resY;
|
||||
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
resY = sourceAllocation.y2 + gap;
|
||||
resY = sourceY + sourceHeight + gap;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
resY = sourceAllocation.y1 - natHeight - gap;
|
||||
resY = sourceY - natHeight - gap;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
resX = sourceAllocation.x2 + gap;
|
||||
resX = sourceX + sourceWidth + gap;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
resX = sourceAllocation.x1 - natWidth - gap;
|
||||
resX = sourceX - natWidth - gap;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -359,21 +296,42 @@ BoxPointer.prototype = {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
case St.Side.BOTTOM:
|
||||
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
|
||||
switch (alignment) {
|
||||
case St.Align.START:
|
||||
resX = sourceX;
|
||||
break;
|
||||
case St.Align.MIDDLE:
|
||||
resX = sourceX - Math.floor((natWidth - sourceWidth) / 2);
|
||||
break;
|
||||
case St.Align.END:
|
||||
resX = sourceX - (natWidth - sourceWidth);
|
||||
break;
|
||||
}
|
||||
|
||||
resX = Math.max(resX, primary.x + 10);
|
||||
resX = Math.min(resX, primary.x + primary.width - (10 + natWidth));
|
||||
this.setArrowOrigin(sourceCenterX - resX);
|
||||
resX = Math.min(resX, primary.x + primary.width - natWidth - arrowRise - gap);
|
||||
resX = Math.max(resX, primary.x);
|
||||
|
||||
this.setArrowOrigin((sourceX - resX) + Math.floor(sourceWidth / 2));
|
||||
break;
|
||||
|
||||
case St.Side.LEFT:
|
||||
case St.Side.RIGHT:
|
||||
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
|
||||
switch (alignment) {
|
||||
case St.Align.START:
|
||||
resY = sourceY;
|
||||
break;
|
||||
case St.Align.MIDDLE:
|
||||
resY = sourceY - Math.floor((natHeight - sourceHeight) / 2);
|
||||
break;
|
||||
case St.Align.END:
|
||||
resY = sourceY - (natHeight - sourceHeight);
|
||||
break;
|
||||
}
|
||||
|
||||
resY = Math.max(resY, primary.y + 10);
|
||||
resY = Math.min(resY, primary.y + primary.height - (10 + natHeight));
|
||||
resY = Math.min(resY, primary.y + primary.height - natHeight - arrowRise - gap);
|
||||
resY = Math.max(resY, primary.y);
|
||||
|
||||
this.setArrowOrigin(sourceCenterY - resY);
|
||||
this.setArrowOrigin((sourceY - resY) + Math.floor(sourceHeight / 2));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -384,9 +342,9 @@ BoxPointer.prototype = {
|
||||
parent = parent.get_parent();
|
||||
}
|
||||
|
||||
this._xPosition = Math.floor(x);
|
||||
this._yPosition = Math.floor(y);
|
||||
this._shiftActor();
|
||||
// Actually set the position
|
||||
this.actor.x = Math.floor(x);
|
||||
this.actor.y = Math.floor(y);
|
||||
},
|
||||
|
||||
// @origin: Coordinate specifying middle of the arrow, along
|
||||
@ -397,42 +355,5 @@ BoxPointer.prototype = {
|
||||
this._arrowOrigin = origin;
|
||||
this._border.queue_repaint();
|
||||
}
|
||||
},
|
||||
|
||||
_shiftActor : function() {
|
||||
// Since the position of the BoxPointer depends on the allocated size
|
||||
// of the BoxPointer and the position of the source actor, trying
|
||||
// to position the BoxPoiner via the x/y properties will result in
|
||||
// allocation loops and warnings. Instead we do the positioning via
|
||||
// the anchor point, which is independent of allocation, and leave
|
||||
// x == y == 0.
|
||||
this.actor.set_anchor_point(-(this._xPosition + this._xOffset),
|
||||
-(this._yPosition + this._yOffset));
|
||||
},
|
||||
|
||||
set xOffset(offset) {
|
||||
this._xOffset = offset;
|
||||
this._shiftActor();
|
||||
},
|
||||
|
||||
get xOffset() {
|
||||
return this._xOffset;
|
||||
},
|
||||
|
||||
set yOffset(offset) {
|
||||
this._yOffset = offset;
|
||||
this._shiftActor();
|
||||
},
|
||||
|
||||
get yOffset() {
|
||||
return this._yOffset;
|
||||
},
|
||||
|
||||
set opacity(opacity) {
|
||||
this.actor.opacity = opacity;
|
||||
},
|
||||
|
||||
get opacity() {
|
||||
return this.actor.opacity;
|
||||
}
|
||||
};
|
||||
|
@ -1,87 +1,22 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const DBus = imports.dbus;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Gettext_gtk30 = imports.gettext.domain('gtk30');
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
const C_ = Gettext.pgettext;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Gettext_gtk20 = imports.gettext.domain('gtk20');
|
||||
|
||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
|
||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||
|
||||
function _sameDay(dateA, dateB) {
|
||||
return (dateA.getDate() == dateB.getDate() &&
|
||||
dateA.getMonth() == dateB.getMonth() &&
|
||||
dateA.getYear() == dateB.getYear());
|
||||
}
|
||||
|
||||
function _sameYear(dateA, dateB) {
|
||||
return (dateA.getYear() == dateB.getYear());
|
||||
}
|
||||
|
||||
/* TODO: maybe needs config - right now we assume that Saturday and
|
||||
* Sunday are non-work days (not true in e.g. Israel, it's Sunday and
|
||||
* Monday there)
|
||||
*/
|
||||
function _isWorkDay(date) {
|
||||
return date.getDay() != 0 && date.getDay() != 6;
|
||||
}
|
||||
|
||||
function _getBeginningOfDay(date) {
|
||||
let ret = new Date(date.getTime());
|
||||
ret.setHours(0);
|
||||
ret.setMinutes(0);
|
||||
ret.setSeconds(0);
|
||||
ret.setMilliseconds(0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _getEndOfDay(date) {
|
||||
let ret = new Date(date.getTime());
|
||||
ret.setHours(23);
|
||||
ret.setMinutes(59);
|
||||
ret.setSeconds(59);
|
||||
ret.setMilliseconds(999);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _formatEventTime(event, clockFormat) {
|
||||
let ret;
|
||||
if (event.allDay) {
|
||||
/* Translators: Shown in calendar event list for all day events
|
||||
* Keep it short, best if you can use less then 10 characters
|
||||
*/
|
||||
ret = C_("event list time", "All Day");
|
||||
} else {
|
||||
switch (clockFormat) {
|
||||
case '24h':
|
||||
/* Translators: Shown in calendar event list, if 24h format */
|
||||
ret = event.date.toLocaleFormat(C_("event list time", "%H:%M"));
|
||||
break;
|
||||
|
||||
default:
|
||||
/* explicit fall-through */
|
||||
case '12h':
|
||||
/* Transators: Shown in calendar event list, if 12h format */
|
||||
ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _getCalendarWeekForDate(date) {
|
||||
// Based on the algorithms found here:
|
||||
// http://en.wikipedia.org/wiki/Talk:ISO_week_date
|
||||
@ -108,259 +43,12 @@ function _getDigitWidth(actor){
|
||||
return width;
|
||||
}
|
||||
|
||||
function _getCalendarDayAbbreviation(dayNumber) {
|
||||
let abbreviations = [
|
||||
/* Translators: Calendar grid abbreviation for Sunday.
|
||||
*
|
||||
* NOTE: These grid abbreviations are always shown together
|
||||
* and in order, e.g. "S M T W T F S".
|
||||
*/
|
||||
C_("grid sunday", "S"),
|
||||
/* Translators: Calendar grid abbreviation for Monday */
|
||||
C_("grid monday", "M"),
|
||||
/* Translators: Calendar grid abbreviation for Tuesday */
|
||||
C_("grid tuesday", "T"),
|
||||
/* Translators: Calendar grid abbreviation for Wednesday */
|
||||
C_("grid wednesday", "W"),
|
||||
/* Translators: Calendar grid abbreviation for Thursday */
|
||||
C_("grid thursday", "T"),
|
||||
/* Translators: Calendar grid abbreviation for Friday */
|
||||
C_("grid friday", "F"),
|
||||
/* Translators: Calendar grid abbreviation for Saturday */
|
||||
C_("grid saturday", "S")
|
||||
];
|
||||
return abbreviations[dayNumber];
|
||||
}
|
||||
|
||||
function _getEventDayAbbreviation(dayNumber) {
|
||||
let abbreviations = [
|
||||
/* Translators: Event list abbreviation for Sunday.
|
||||
*
|
||||
* NOTE: These list abbreviations are normally not shown together
|
||||
* so they need to be unique (e.g. Tuesday and Thursday cannot
|
||||
* both be 'T').
|
||||
*/
|
||||
C_("list sunday", "Su"),
|
||||
/* Translators: Event list abbreviation for Monday */
|
||||
C_("list monday", "M"),
|
||||
/* Translators: Event list abbreviation for Tuesday */
|
||||
C_("list tuesday", "T"),
|
||||
/* Translators: Event list abbreviation for Wednesday */
|
||||
C_("list wednesday", "W"),
|
||||
/* Translators: Event list abbreviation for Thursday */
|
||||
C_("list thursday", "Th"),
|
||||
/* Translators: Event list abbreviation for Friday */
|
||||
C_("list friday", "F"),
|
||||
/* Translators: Event list abbreviation for Saturday */
|
||||
C_("list saturday", "S")
|
||||
];
|
||||
return abbreviations[dayNumber];
|
||||
}
|
||||
|
||||
// Abstraction for an appointment/event in a calendar
|
||||
|
||||
function CalendarEvent(date, end, summary, allDay) {
|
||||
this._init(date, end, summary, allDay);
|
||||
}
|
||||
|
||||
CalendarEvent.prototype = {
|
||||
_init: function(date, end, summary, allDay) {
|
||||
this.date = date;
|
||||
this.end = end;
|
||||
this.summary = summary;
|
||||
this.allDay = allDay;
|
||||
}
|
||||
};
|
||||
|
||||
// Interface for appointments/events - e.g. the contents of a calendar
|
||||
//
|
||||
|
||||
// First, an implementation with no events
|
||||
function EmptyEventSource() {
|
||||
function Calendar() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
EmptyEventSource.prototype = {
|
||||
_init: function() {
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
},
|
||||
|
||||
getEvents: function(begin, end) {
|
||||
let result = [];
|
||||
return result;
|
||||
},
|
||||
|
||||
hasEvents: function(day) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(EmptyEventSource.prototype);
|
||||
|
||||
const CalendarServerIface = {
|
||||
name: 'org.gnome.Shell.CalendarServer',
|
||||
methods: [{ name: 'GetEvents',
|
||||
inSignature: 'xxb',
|
||||
outSignature: 'a(sssbxxa{sv})' }],
|
||||
signals: [{ name: 'Changed',
|
||||
inSignature: '' }]
|
||||
};
|
||||
|
||||
const CalendarServer = function () {
|
||||
this._init();
|
||||
};
|
||||
|
||||
CalendarServer.prototype = {
|
||||
_init: function() {
|
||||
DBus.session.proxifyObject(this, 'org.gnome.Shell.CalendarServer', '/org/gnome/Shell/CalendarServer');
|
||||
}
|
||||
};
|
||||
|
||||
DBus.proxifyPrototype(CalendarServer.prototype, CalendarServerIface);
|
||||
|
||||
// an implementation that reads data from a session bus service
|
||||
function DBusEventSource(owner) {
|
||||
this._init(owner);
|
||||
}
|
||||
|
||||
function _datesEqual(a, b) {
|
||||
if (a < b)
|
||||
return false;
|
||||
else if (a > b)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function _dateIntervalsOverlap(a0, a1, b0, b1)
|
||||
{
|
||||
if (a1 <= b0)
|
||||
return false;
|
||||
else if (b1 <= a0)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
DBusEventSource.prototype = {
|
||||
_init: function(owner) {
|
||||
this._resetCache();
|
||||
|
||||
this._dbusProxy = new CalendarServer(owner);
|
||||
this._dbusProxy.connect('Changed', Lang.bind(this, this._onChanged));
|
||||
|
||||
DBus.session.watch_name('org.gnome.Shell.CalendarServer',
|
||||
false, // do not launch a name-owner if none exists
|
||||
Lang.bind(this, this._onNameAppeared),
|
||||
Lang.bind(this, this._onNameVanished));
|
||||
},
|
||||
|
||||
_resetCache: function() {
|
||||
this._events = [];
|
||||
this._lastRequestBegin = null;
|
||||
this._lastRequestEnd = null;
|
||||
},
|
||||
|
||||
_onNameAppeared: function(owner) {
|
||||
this._resetCache();
|
||||
this._loadEvents(true);
|
||||
},
|
||||
|
||||
_onNameVanished: function(oldOwner) {
|
||||
this._resetCache();
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
_onChanged: function() {
|
||||
this._loadEvents(false);
|
||||
},
|
||||
|
||||
_onEventsReceived: function(appointments) {
|
||||
let newEvents = [];
|
||||
if (appointments != null) {
|
||||
for (let n = 0; n < appointments.length; n++) {
|
||||
let a = appointments[n];
|
||||
let date = new Date(a[4] * 1000);
|
||||
let end = new Date(a[5] * 1000);
|
||||
let summary = a[1];
|
||||
let allDay = a[3];
|
||||
let event = new CalendarEvent(date, end, summary, allDay);
|
||||
newEvents.push(event);
|
||||
}
|
||||
newEvents.sort(function(event1, event2) {
|
||||
return event1.date.getTime() - event2.date.getTime();
|
||||
});
|
||||
}
|
||||
|
||||
this._events = newEvents;
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
_loadEvents: function(forceReload) {
|
||||
if (this._curRequestBegin && this._curRequestEnd){
|
||||
let callFlags = 0;
|
||||
if (forceReload)
|
||||
callFlags |= DBus.CALL_FLAG_START;
|
||||
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
|
||||
this._curRequestEnd.getTime() / 1000,
|
||||
forceReload,
|
||||
Lang.bind(this, this._onEventsReceived),
|
||||
callFlags);
|
||||
}
|
||||
},
|
||||
|
||||
requestRange: function(begin, end, forceReload) {
|
||||
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
||||
this._lastRequestBegin = begin;
|
||||
this._lastRequestEnd = end;
|
||||
this._curRequestBegin = begin;
|
||||
this._curRequestEnd = end;
|
||||
this._loadEvents(forceReload);
|
||||
}
|
||||
},
|
||||
|
||||
getEvents: function(begin, end) {
|
||||
let result = [];
|
||||
for(let n = 0; n < this._events.length; n++) {
|
||||
let event = this._events[n];
|
||||
if (_dateIntervalsOverlap (event.date, event.end, begin, end)) {
|
||||
result.push(event);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
hasEvents: function(day) {
|
||||
let dayBegin = _getBeginningOfDay(day);
|
||||
let dayEnd = _getEndOfDay(day);
|
||||
|
||||
let events = this.getEvents(dayBegin, dayEnd);
|
||||
|
||||
if (events.length == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(DBusEventSource.prototype);
|
||||
|
||||
// Calendar:
|
||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
||||
function Calendar(eventSource) {
|
||||
this._init(eventSource);
|
||||
}
|
||||
|
||||
Calendar.prototype = {
|
||||
_init: function(eventSource) {
|
||||
this._eventSource = eventSource;
|
||||
|
||||
this._eventSource.connect('changed', Lang.bind(this,
|
||||
function() {
|
||||
this._update(false);
|
||||
}));
|
||||
|
||||
_init: function() {
|
||||
// FIXME: This is actually the fallback method for GTK+ for the week start;
|
||||
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
|
||||
// should add a C function so we can do the full handling.
|
||||
@ -372,7 +60,7 @@ Calendar.prototype = {
|
||||
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
||||
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
||||
|
||||
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
|
||||
let weekStartString = Gettext_gtk20.gettext('calendar:week_start:0');
|
||||
if (weekStartString.indexOf('calendar:week_start:') == 0) {
|
||||
this._weekStart = parseInt(weekStartString.substring(20));
|
||||
}
|
||||
@ -383,8 +71,7 @@ Calendar.prototype = {
|
||||
}
|
||||
|
||||
// Find the ordering for month/year in the calendar heading
|
||||
this._headerFormatWithoutYear = '%B';
|
||||
switch (Gettext_gtk30.gettext('calendar:MY')) {
|
||||
switch (Gettext_gtk20.gettext('calendar:MY')) {
|
||||
case 'calendar:MY':
|
||||
this._headerFormat = '%B %Y';
|
||||
break;
|
||||
@ -398,7 +85,7 @@ Calendar.prototype = {
|
||||
}
|
||||
|
||||
// Start off with the current date
|
||||
this._selectedDate = new Date();
|
||||
this.date = new Date();
|
||||
|
||||
this.actor = new St.Table({ homogeneous: false,
|
||||
style_class: 'calendar',
|
||||
@ -408,17 +95,14 @@ Calendar.prototype = {
|
||||
Lang.bind(this, this._onScroll));
|
||||
|
||||
this._buildHeader ();
|
||||
this._update();
|
||||
},
|
||||
|
||||
// Sets the calendar to show a specific date
|
||||
setDate: function(date, forceReload) {
|
||||
if (!_sameDay(date, this._selectedDate)) {
|
||||
this._selectedDate = date;
|
||||
this._update(forceReload);
|
||||
this.emit('selected-date-changed', new Date(this._selectedDate));
|
||||
} else {
|
||||
if (forceReload)
|
||||
this._update(forceReload);
|
||||
setDate: function(date) {
|
||||
if (!_sameDay(date, this.date)) {
|
||||
this.date = date;
|
||||
this._update();
|
||||
}
|
||||
},
|
||||
|
||||
@ -432,36 +116,45 @@ Calendar.prototype = {
|
||||
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
||||
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
|
||||
let [backlabel, forwardlabel] = ['<', '>'];
|
||||
if (St.Widget.get_default_direction () == St.TextDirection.RTL) {
|
||||
[backlabel, forwardlabel] = [forwardlabel, backlabel];
|
||||
}
|
||||
|
||||
let back = new St.Button({ style_class: 'calendar-change-month-back' });
|
||||
let back = new St.Button({ label: backlabel, style_class: 'calendar-change-month' });
|
||||
this._topBox.add(back);
|
||||
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
|
||||
back.connect('clicked', Lang.bind(this, this._prevMonth));
|
||||
|
||||
this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
|
||||
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||
this._dateLabel = new St.Label();
|
||||
this._topBox.add(this._dateLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||
|
||||
let forward = new St.Button({ style_class: 'calendar-change-month-forward' });
|
||||
let forward = new St.Button({ label: forwardlabel, style_class: 'calendar-change-month' });
|
||||
this._topBox.add(forward);
|
||||
forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
|
||||
forward.connect('clicked', Lang.bind(this, this._nextMonth));
|
||||
|
||||
// Add weekday labels...
|
||||
//
|
||||
// We need to figure out the abbreviated localized names for the days of the week;
|
||||
// we do this by just getting the next 7 days starting from right now and then putting
|
||||
// them in the right cell in the table. It doesn't matter if we add them in order
|
||||
let iter = new Date(this._selectedDate);
|
||||
let iter = new Date(this.date);
|
||||
iter.setSeconds(0); // Leap second protection. Hah!
|
||||
iter.setHours(12);
|
||||
|
||||
if (this._useWeekdate) {
|
||||
this._weekdateHeader = new St.Label();
|
||||
this.actor.add(this._weekdateHeader,
|
||||
{ row: 1,
|
||||
col: 0,
|
||||
x_fill: false, x_align: St.Align.MIDDLE });
|
||||
this._setWeekdateHeaderWidth();
|
||||
} else {
|
||||
this._weekdateHeader = null;
|
||||
}
|
||||
|
||||
for (let i = 0; i < 7; i++) {
|
||||
// Could use iter.toLocaleFormat('%a') but that normally gives three characters
|
||||
// and we want, ideally, a single character for e.g. S M T W T F S
|
||||
let customDayAbbrev = _getCalendarDayAbbreviation(iter.getDay());
|
||||
let label = new St.Label({ style_class: 'calendar-day-base calendar-day-heading',
|
||||
text: customDayAbbrev });
|
||||
this.actor.add(label,
|
||||
this.actor.add(new St.Label({ text: iter.toLocaleFormat('%a') }),
|
||||
{ row: 1,
|
||||
col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
|
||||
x_fill: false, x_align: St.Align.MIDDLE });
|
||||
x_fill: false, x_align: St.Align.END });
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
}
|
||||
|
||||
@ -485,72 +178,43 @@ Calendar.prototype = {
|
||||
switch (event.get_scroll_direction()) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
case Clutter.ScrollDirection.LEFT:
|
||||
this._onPrevMonthButtonClicked();
|
||||
this._prevMonth();
|
||||
break;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
case Clutter.ScrollDirection.RIGHT:
|
||||
this._onNextMonthButtonClicked();
|
||||
this._nextMonth();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_onPrevMonthButtonClicked: function() {
|
||||
let newDate = new Date(this._selectedDate);
|
||||
let oldMonth = newDate.getMonth();
|
||||
if (oldMonth == 0) {
|
||||
newDate.setMonth(11);
|
||||
newDate.setFullYear(newDate.getFullYear() - 1);
|
||||
if (newDate.getMonth() != 11) {
|
||||
let day = 32 - new Date(newDate.getFullYear() - 1, 11, 32).getDate();
|
||||
newDate = new Date(newDate.getFullYear() - 1, 11, day);
|
||||
}
|
||||
_prevMonth: function() {
|
||||
if (this.date.getMonth() == 0) {
|
||||
this.date.setMonth(11);
|
||||
this.date.setFullYear(this.date.getFullYear() - 1);
|
||||
} else {
|
||||
this.date.setMonth(this.date.getMonth() - 1);
|
||||
}
|
||||
else {
|
||||
newDate.setMonth(oldMonth - 1);
|
||||
if (newDate.getMonth() != oldMonth - 1) {
|
||||
let day = 32 - new Date(newDate.getFullYear(), oldMonth - 1, 32).getDate();
|
||||
newDate = new Date(newDate.getFullYear(), oldMonth - 1, day);
|
||||
}
|
||||
}
|
||||
|
||||
this.setDate(newDate, false);
|
||||
this._update();
|
||||
},
|
||||
|
||||
_onNextMonthButtonClicked: function() {
|
||||
let newDate = new Date(this._selectedDate);
|
||||
let oldMonth = newDate.getMonth();
|
||||
if (oldMonth == 11) {
|
||||
newDate.setMonth(0);
|
||||
newDate.setFullYear(newDate.getFullYear() + 1);
|
||||
if (newDate.getMonth() != 0) {
|
||||
let day = 32 - new Date(newDate.getFullYear() + 1, 0, 32).getDate();
|
||||
newDate = new Date(newDate.getFullYear() + 1, 0, day);
|
||||
}
|
||||
_nextMonth: function() {
|
||||
if (this.date.getMonth() == 11) {
|
||||
this.date.setMonth(0);
|
||||
this.date.setFullYear(this.date.getFullYear() + 1);
|
||||
} else {
|
||||
this.date.setMonth(this.date.getMonth() + 1);
|
||||
}
|
||||
else {
|
||||
newDate.setMonth(oldMonth + 1);
|
||||
if (newDate.getMonth() != oldMonth + 1) {
|
||||
let day = 32 - new Date(newDate.getFullYear(), oldMonth + 1, 32).getDate();
|
||||
newDate = new Date(newDate.getFullYear(), oldMonth + 1, day);
|
||||
}
|
||||
}
|
||||
|
||||
this.setDate(newDate, false);
|
||||
this._update();
|
||||
},
|
||||
|
||||
_onSettingsChange: function() {
|
||||
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
||||
this._buildHeader();
|
||||
this._update(false);
|
||||
this._update();
|
||||
},
|
||||
|
||||
_update: function(forceReload) {
|
||||
let now = new Date();
|
||||
|
||||
if (_sameYear(this._selectedDate, now))
|
||||
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
|
||||
else
|
||||
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
|
||||
_update: function() {
|
||||
this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat);
|
||||
|
||||
// Remove everything but the topBox and the weekday labels
|
||||
let children = this.actor.get_children();
|
||||
@ -558,217 +222,45 @@ Calendar.prototype = {
|
||||
children[i].destroy();
|
||||
|
||||
// Start at the beginning of the week before the start of the month
|
||||
let beginDate = new Date(this._selectedDate);
|
||||
beginDate.setDate(1);
|
||||
beginDate.setSeconds(0);
|
||||
beginDate.setHours(12);
|
||||
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
|
||||
beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
|
||||
let iter = new Date(this.date);
|
||||
iter.setDate(1);
|
||||
iter.setSeconds(0);
|
||||
iter.setHours(12);
|
||||
let daysToWeekStart = (7 + iter.getDay() - this._weekStart) % 7;
|
||||
iter.setTime(iter.getTime() - daysToWeekStart * MSECS_IN_DAY);
|
||||
|
||||
let now = new Date();
|
||||
|
||||
let iter = new Date(beginDate);
|
||||
let row = 2;
|
||||
while (true) {
|
||||
let button = new St.Button({ label: iter.getDate().toString() });
|
||||
|
||||
let iterStr = iter.toUTCString();
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
let newlySelectedDate = new Date(iterStr);
|
||||
this.setDate(newlySelectedDate, false);
|
||||
}));
|
||||
|
||||
let hasEvents = this._eventSource.hasEvents(iter);
|
||||
let styleClass = 'calendar-day-base calendar-day';
|
||||
if (_isWorkDay(iter))
|
||||
styleClass += ' calendar-work-day'
|
||||
else
|
||||
styleClass += ' calendar-nonwork-day'
|
||||
|
||||
// Hack used in lieu of border-collapse - see gnome-shell.css
|
||||
if (row == 2)
|
||||
styleClass = 'calendar-day-top ' + styleClass;
|
||||
if (iter.getDay() == this._weekStart)
|
||||
styleClass = 'calendar-day-left ' + styleClass;
|
||||
|
||||
let label = new St.Label({ text: iter.getDate().toString() });
|
||||
if (_sameDay(now, iter))
|
||||
styleClass += ' calendar-today';
|
||||
else if (iter.getMonth() != this._selectedDate.getMonth())
|
||||
styleClass += ' calendar-other-month-day';
|
||||
|
||||
if (_sameDay(this._selectedDate, iter))
|
||||
button.add_style_pseudo_class('active');
|
||||
|
||||
if (hasEvents)
|
||||
styleClass += ' calendar-day-with-events'
|
||||
|
||||
button.style_class = styleClass;
|
||||
label.style_class = 'calendar-day calendar-today';
|
||||
else if (iter.getMonth() != this.date.getMonth())
|
||||
label.style_class = 'calendar-day calendar-other-month-day';
|
||||
else
|
||||
label.style_class = 'calendar-day';
|
||||
|
||||
let offsetCols = this._useWeekdate ? 1 : 0;
|
||||
this.actor.add(button,
|
||||
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
|
||||
this.actor.add(label,
|
||||
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
|
||||
x_fill: false, x_align: St.Align.END });
|
||||
|
||||
if (this._useWeekdate && iter.getDay() == 4) {
|
||||
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
|
||||
style_class: 'calendar-day-base calendar-week-number'});
|
||||
style_class: 'calendar-day calendar-calendarweek'});
|
||||
this.actor.add(label,
|
||||
{ row: row, col: 0, y_align: St.Align.MIDDLE });
|
||||
{ row: row, col: 0,
|
||||
x_fill: false, x_align: St.Align.MIDDLE });
|
||||
}
|
||||
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
if (iter.getDay() == this._weekStart) {
|
||||
// We stop on the first "first day of the week" after the month we are displaying
|
||||
if (iter.getMonth() > this._selectedDate.getMonth() || iter.getYear() > this._selectedDate.getYear())
|
||||
if (iter.getMonth() > this.date.getMonth() || iter.getYear() > this.date.getYear())
|
||||
break;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
// Signal to the event source that we are interested in events
|
||||
// only from this date range
|
||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(Calendar.prototype);
|
||||
|
||||
function EventsList(eventSource) {
|
||||
this._init(eventSource);
|
||||
}
|
||||
|
||||
EventsList.prototype = {
|
||||
_init: function(eventSource) {
|
||||
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
|
||||
this._date = new Date();
|
||||
this._eventSource = eventSource;
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
||||
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
|
||||
if (weekStartString.indexOf('calendar:week_start:') == 0) {
|
||||
this._weekStart = parseInt(weekStartString.substring(20));
|
||||
}
|
||||
|
||||
if (isNaN(this._weekStart) ||
|
||||
this._weekStart < 0 ||
|
||||
this._weekStart > 6) {
|
||||
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
|
||||
this._weekStart = 0;
|
||||
}
|
||||
|
||||
this._update();
|
||||
},
|
||||
|
||||
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
|
||||
if (includeDayName) {
|
||||
dayNameBox.add(new St.Label( { style_class: 'events-day-dayname',
|
||||
text: day } ),
|
||||
{ x_fill: true } );
|
||||
}
|
||||
timeBox.add(new St.Label( { style_class: 'events-day-time',
|
||||
text: time} ),
|
||||
{ x_fill: true } );
|
||||
eventTitleBox.add(new St.Label( { style_class: 'events-day-task',
|
||||
text: desc} ));
|
||||
},
|
||||
|
||||
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
|
||||
let events = this._eventSource.getEvents(begin, end);
|
||||
|
||||
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
|
||||
|
||||
if (events.length == 0 && !showNothingScheduled)
|
||||
return;
|
||||
|
||||
let vbox = new St.BoxLayout( {vertical: true} );
|
||||
this.actor.add(vbox);
|
||||
|
||||
vbox.add(new St.Label({ style_class: 'events-day-header', text: header }));
|
||||
let box = new St.BoxLayout({style_class: 'events-header-hbox'});
|
||||
let dayNameBox = new St.BoxLayout({ vertical: true, style_class: 'events-day-name-box' });
|
||||
let timeBox = new St.BoxLayout({ vertical: true, style_class: 'events-time-box' });
|
||||
let eventTitleBox = new St.BoxLayout({ vertical: true, style_class: 'events-event-box' });
|
||||
box.add(dayNameBox, {x_fill: false});
|
||||
box.add(timeBox, {x_fill: false});
|
||||
box.add(eventTitleBox, {expand: true});
|
||||
vbox.add(box);
|
||||
|
||||
for (let n = 0; n < events.length; n++) {
|
||||
let event = events[n];
|
||||
let dayString = _getEventDayAbbreviation(event.date.getDay());
|
||||
let timeString = _formatEventTime(event, clockFormat);
|
||||
let summaryString = event.summary;
|
||||
this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
|
||||
}
|
||||
|
||||
if (events.length == 0 && showNothingScheduled) {
|
||||
let now = new Date();
|
||||
/* Translators: Text to show if there are no events */
|
||||
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
|
||||
let timeString = _formatEventTime(nothingEvent, clockFormat);
|
||||
this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
|
||||
}
|
||||
},
|
||||
|
||||
_showOtherDay: function(day) {
|
||||
this.actor.destroy_children();
|
||||
|
||||
let dayBegin = _getBeginningOfDay(day);
|
||||
let dayEnd = _getEndOfDay(day);
|
||||
|
||||
let dayString;
|
||||
let now = new Date();
|
||||
if (_sameYear(day, now))
|
||||
/* Translators: Shown on calendar heading when selected day occurs on current year */
|
||||
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d"));
|
||||
else
|
||||
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
||||
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
|
||||
this._addPeriod(dayString, dayBegin, dayEnd, false, true);
|
||||
},
|
||||
|
||||
_showToday: function() {
|
||||
this.actor.destroy_children();
|
||||
|
||||
let now = new Date();
|
||||
let dayBegin = _getBeginningOfDay(now);
|
||||
let dayEnd = _getEndOfDay(now);
|
||||
this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
|
||||
|
||||
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
|
||||
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
||||
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
|
||||
|
||||
if (dayEnd.getDay() <= 4 + this._weekStart) {
|
||||
/* If now is within the first 5 days we show "This week" and
|
||||
* include events up until and including Saturday/Sunday
|
||||
* (depending on whether a week starts on Sunday/Monday).
|
||||
*/
|
||||
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||
let thisWeekEnd = new Date(dayEnd.getTime() + (6 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
|
||||
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
|
||||
} else {
|
||||
/* otherwise it's one of the two last days of the week ... show
|
||||
* "Next week" and include events up until and including *next*
|
||||
* Saturday/Sunday
|
||||
*/
|
||||
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||
let nextWeekEnd = new Date(dayEnd.getTime() + (13 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
|
||||
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
|
||||
}
|
||||
},
|
||||
|
||||
// Sets the event list to show events from a specific date
|
||||
setDate: function(date) {
|
||||
if (!_sameDay(date, this._date)) {
|
||||
this._date = date;
|
||||
this._update();
|
||||
}
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
let today = new Date();
|
||||
if (_sameDay (this._date, today)) {
|
||||
this._showToday();
|
||||
} else {
|
||||
this._showOtherDay(this._date);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
214
js/ui/chrome.js
@ -13,6 +13,12 @@ const Params = imports.misc.params;
|
||||
// normal mode (ie, outside the Overview), that surrounds the main
|
||||
// workspace content.
|
||||
|
||||
const Visibility = {
|
||||
FULL: 1,
|
||||
FULLSCREEN: 2,
|
||||
OVERVIEW: 3
|
||||
};
|
||||
|
||||
const defaultParams = {
|
||||
visibleInOverview: false,
|
||||
visibleInFullscreen: false,
|
||||
@ -31,13 +37,12 @@ Chrome.prototype = {
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocated));
|
||||
|
||||
this._monitors = [];
|
||||
this._inFullscreen = false;
|
||||
this._inOverview = false;
|
||||
this.visibility = Visibility.FULL;
|
||||
|
||||
this._trackedActors = [];
|
||||
|
||||
global.screen.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
global.screen.connect('restacked',
|
||||
Lang.bind(this, this._windowsRestacked));
|
||||
|
||||
@ -50,8 +55,6 @@ Chrome.prototype = {
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._overviewHidden));
|
||||
|
||||
this._updateMonitors();
|
||||
this._updateFullscreen();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
@ -191,12 +194,24 @@ Chrome.prototype = {
|
||||
let actorData = this._trackedActors[i];
|
||||
if (this._inOverview && !actorData.visibleInOverview)
|
||||
this.actor.set_skip_paint(actorData.actor, true);
|
||||
else if (!this._inOverview && !actorData.visibleInFullscreen &&
|
||||
this._findMonitorForActor(actorData.actor).inFullscreen)
|
||||
else if (!this._inOverview && this._inFullscreen && !actorData.visibleInFullscreen)
|
||||
this.actor.set_skip_paint(actorData.actor, true);
|
||||
else
|
||||
this.actor.set_skip_paint(actorData.actor, false);
|
||||
}
|
||||
|
||||
let newVisibility;
|
||||
if (this._inOverview)
|
||||
newVisibility = Visibility.OVERVIEW;
|
||||
else if (this._inFullscreen)
|
||||
newVisibility = Visibility.FULLSCREEN;
|
||||
else
|
||||
newVisibility = Visibility.FULL;
|
||||
|
||||
if (newVisibility != this.visibility) {
|
||||
this.visibility = newVisibility;
|
||||
this.emit('visibility-changed', this.visibility);
|
||||
}
|
||||
},
|
||||
|
||||
_overviewShowing: function() {
|
||||
@ -211,77 +226,15 @@ Chrome.prototype = {
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_updateMonitors: function() {
|
||||
let monitors = global.get_monitors();
|
||||
let primary = global.get_primary_monitor();
|
||||
this._monitors = monitors;
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
let monitor = monitors[i];
|
||||
if (monitor.x == primary.x &&
|
||||
monitor.y == primary.y &&
|
||||
monitor.width == primary.width &&
|
||||
monitor.height == primary.height)
|
||||
this._primaryMonitor = monitor;
|
||||
}
|
||||
},
|
||||
|
||||
_findMonitorForRect: function(x, y, w, h) {
|
||||
// First look at what monitor the center of the rectangle is at
|
||||
let cx = x + w/2;
|
||||
let cy = y + h/2;
|
||||
for (let i = 0; i < this._monitors.length; i++) {
|
||||
let monitor = this._monitors[i];
|
||||
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
|
||||
cy >= monitor.y && cy < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
}
|
||||
// If the center is not on a monitor, return the first overlapping monitor
|
||||
for (let i = 0; i < this._monitors.length; i++) {
|
||||
let monitor = this._monitors[i];
|
||||
if (x + w > monitor.x && x < monitor.x + monitor.width &&
|
||||
y + h > monitor.y && y < monitor.y + monitor.height)
|
||||
return monitor;
|
||||
}
|
||||
// otherwise on no monitor
|
||||
return null;
|
||||
},
|
||||
|
||||
_findMonitorForWindow: function(window) {
|
||||
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
|
||||
},
|
||||
|
||||
// This call guarantees that we return some monitor to simplify usage of it
|
||||
// In practice all tracked actors should be visible on some monitor anyway
|
||||
_findMonitorForActor: function(actor) {
|
||||
let [x, y] = actor.get_transformed_position();
|
||||
let [w, h] = actor.get_transformed_size();
|
||||
let monitor = this._findMonitorForRect(x, y, w, h);
|
||||
if (monitor)
|
||||
return monitor;
|
||||
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
this._updateMonitors();
|
||||
|
||||
// Update everything that depends on monitor positions
|
||||
this._updateFullscreen();
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_queueUpdateRegions: function() {
|
||||
if (!this._updateRegionIdle)
|
||||
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
|
||||
Meta.PRIORITY_BEFORE_REDRAW);
|
||||
},
|
||||
|
||||
_updateFullscreen: function() {
|
||||
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
|
||||
|
||||
// Reset all monitors to not fullscreen
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
this._monitors[i].inFullscreen = false;
|
||||
_windowsRestacked: function() {
|
||||
let windows = global.get_window_actors();
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
// The chrome layer should be visible unless there is a window
|
||||
// with layer FULLSCREEN, or a window with layer
|
||||
@ -295,51 +248,42 @@ Chrome.prototype = {
|
||||
|
||||
// @windows is sorted bottom to top.
|
||||
|
||||
let wasInFullscreen = this._inFullscreen;
|
||||
this._inFullscreen = false;
|
||||
for (let i = windows.length - 1; i > -1; i--) {
|
||||
let window = windows[i];
|
||||
let layer = window.get_meta_window().get_layer();
|
||||
let layer = windows[i].get_meta_window().get_layer();
|
||||
|
||||
// There are 3 cases we check here for:
|
||||
// 1.) Monitor sized window
|
||||
// 2.) Window with a position somewhere on the primary screen having the _NET_WM_FULLSCREEN flag set
|
||||
// 3.) Window that is partly off screen (tries to hide its decorations) which might have negative coords
|
||||
// We check for 1.) and 2.) by checking if the upper right corner is on the primary monitor, but avoid the case
|
||||
// where it overlaps with the secondary screen (like window.x + window.width == primary.x + primary.width)
|
||||
// For 3.) we just ignore negative values as they don't really make sense
|
||||
|
||||
if (layer == Meta.StackLayer.FULLSCREEN) {
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
if (monitor)
|
||||
monitor.inFullscreen = true;
|
||||
if (Math.max(windows[i].x, 0) >= primary.x && Math.max(windows[i].x, 0) < primary.x + primary.width &&
|
||||
Math.max(windows[i].y, 0) >= primary.y && Math.max(windows[i].y, 0) < primary.y + primary.height) {
|
||||
this._inFullscreen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
|
||||
let monitor = this._findMonitorForWindow(window);
|
||||
if (monitor &&
|
||||
window.x <= monitor.x &&
|
||||
window.x + window.width >= monitor.x + monitor.width &&
|
||||
window.y <= monitor.y &&
|
||||
window.y + window.height >= monitor.y + monitor.height)
|
||||
monitor.inFullscreen = true;
|
||||
if (windows[i].x <= primary.x &&
|
||||
windows[i].x + windows[i].width >= primary.x + primary.width &&
|
||||
windows[i].y <= primary.y &&
|
||||
windows[i].y + windows[i].height >= primary.y + primary.height) {
|
||||
this._inFullscreen = true;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_windowsRestacked: function() {
|
||||
let wasInFullscreen = [];
|
||||
for (let i = 0; i < this._monitors.length; i++)
|
||||
wasInFullscreen[i] = this._monitors[i].inFullscreen;
|
||||
|
||||
this._updateFullscreen();
|
||||
|
||||
let changed = false;
|
||||
for (let i = 0; i < wasInFullscreen.length; i++) {
|
||||
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
if (this._inFullscreen != wasInFullscreen) {
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
}
|
||||
|
||||
// Figure out where the pointer is in case we lost track of
|
||||
// it during a grab. (In particular, if a trayicon popup menu
|
||||
// is dismissed, see if we need to close the message tray.)
|
||||
global.sync_pointer();
|
||||
},
|
||||
|
||||
_updateRegions: function() {
|
||||
@ -368,75 +312,41 @@ Chrome.prototype = {
|
||||
if (!actorData.affectsStruts)
|
||||
continue;
|
||||
|
||||
// Limit struts to the size of the screen
|
||||
let x1 = Math.max(x, 0);
|
||||
let x2 = Math.min(x + w, global.screen_width);
|
||||
let y1 = Math.max(y, 0);
|
||||
let y2 = Math.min(y + h, global.screen_height);
|
||||
|
||||
// NetWM struts are not really powerful enought to handle
|
||||
// a multi-monitor scenario, they only describe what happens
|
||||
// around the outer sides of the full display region. However
|
||||
// it can describe a partial region along each side, so
|
||||
// we can support having the struts only affect the
|
||||
// primary monitor. This should be enough as we only have
|
||||
// chrome affecting the struts on the primary monitor so
|
||||
// far.
|
||||
//
|
||||
// Metacity wants to know what side of the screen the
|
||||
// strut is considered to be attached to. If the actor is
|
||||
// only touching one edge, or is touching the entire
|
||||
// border of the primary monitor, then it's obvious which
|
||||
// side to call it. If it's in a corner, we pick a side
|
||||
// width/height of one edge, then it's obvious which side
|
||||
// to call it. If it's in a corner, we pick a side
|
||||
// arbitrarily. If it doesn't touch any edges, or it spans
|
||||
// the width/height across the middle of the screen, then
|
||||
// we don't create a strut for it at all.
|
||||
let side;
|
||||
let primary = this._primaryMonitor;
|
||||
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
|
||||
if (y1 <= primary.y)
|
||||
if (w >= global.screen_width) {
|
||||
if (y <= 0)
|
||||
side = Meta.Side.TOP;
|
||||
else if (y2 >= primary.y + primary.height)
|
||||
else if (y + h >= global.screen_height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
|
||||
if (x1 <= 0)
|
||||
} else if (h >= global.screen_height) {
|
||||
if (x <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (x2 >= global.screen_width)
|
||||
else if (x + w >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else
|
||||
continue;
|
||||
} else if (x1 <= 0)
|
||||
} else if (x <= 0)
|
||||
side = Meta.Side.LEFT;
|
||||
else if (y1 <= 0)
|
||||
else if (y <= 0)
|
||||
side = Meta.Side.TOP;
|
||||
else if (x2 >= global.screen_width)
|
||||
else if (x + w >= global.screen_width)
|
||||
side = Meta.Side.RIGHT;
|
||||
else if (y2 >= global.screen_height)
|
||||
else if (y + h >= global.screen_height)
|
||||
side = Meta.Side.BOTTOM;
|
||||
else
|
||||
continue;
|
||||
|
||||
// Ensure that the strut rects goes all the way to the screen edge,
|
||||
// as this really what mutter expects.
|
||||
switch (side) {
|
||||
case Meta.Side.TOP:
|
||||
y1 = 0;
|
||||
break;
|
||||
case Meta.Side.BOTTOM:
|
||||
y2 = global.screen_height;
|
||||
break;
|
||||
case Meta.Side.LEFT:
|
||||
x1 = 0;
|
||||
break;
|
||||
case Meta.Side.RIGHT:
|
||||
x2 = global.screen_width;
|
||||
break;
|
||||
}
|
||||
|
||||
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
|
||||
let strut = new Meta.Strut({ rect: strutRect, side: side });
|
||||
let strut = new Meta.Strut({ rect: rect, side: side });
|
||||
struts.push(strut);
|
||||
}
|
||||
|
||||
|
@ -1,328 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const AltTab = imports.ui.altTab;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const POPUP_APPICON_SIZE = 96;
|
||||
const POPUP_FADE_TIME = 0.1; // seconds
|
||||
|
||||
const SortGroup = {
|
||||
TOP: 0,
|
||||
MIDDLE: 1,
|
||||
BOTTOM: 2
|
||||
};
|
||||
|
||||
function CtrlAltTabManager() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
CtrlAltTabManager.prototype = {
|
||||
_init: function() {
|
||||
this._items = [];
|
||||
this._focusManager = St.FocusManager.get_for_stage(global.stage);
|
||||
},
|
||||
|
||||
addGroup: function(root, name, icon, params) {
|
||||
let item = Params.parse(params, { sortGroup: SortGroup.MIDDLE,
|
||||
proxy: root,
|
||||
focusCallback: null });
|
||||
|
||||
item.root = root;
|
||||
item.name = name;
|
||||
item.iconName = icon;
|
||||
|
||||
this._items.push(item);
|
||||
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
|
||||
this._focusManager.add_group(root);
|
||||
},
|
||||
|
||||
removeGroup: function(root) {
|
||||
this._focusManager.remove_group(root);
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
if (this._items[i].root == root) {
|
||||
this._items.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
focusGroup: function(item) {
|
||||
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
|
||||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
|
||||
if (item.window)
|
||||
Main.activateWindow(item.window);
|
||||
else if (item.focusCallback)
|
||||
item.focusCallback();
|
||||
else
|
||||
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
// Sort the items into a consistent order; panel first, tray last,
|
||||
// and everything else in between, sorted by X coordinate, so that
|
||||
// they will have the same left-to-right ordering in the
|
||||
// Ctrl-Alt-Tab dialog as they do onscreen.
|
||||
_sortItems: function(a, b) {
|
||||
if (a.sortGroup != b.sortGroup)
|
||||
return a.sortGroup - b.sortGroup;
|
||||
|
||||
let y;
|
||||
if (a.x == undefined) {
|
||||
if (a.window)
|
||||
a.x = a.window.get_compositor_private().x;
|
||||
else
|
||||
[a.x, y] = a.proxy.get_transformed_position();
|
||||
}
|
||||
if (b.x == undefined) {
|
||||
if (b.window)
|
||||
b.x = b.window.get_compositor_private().x;
|
||||
else
|
||||
[b.x, y] = b.proxy.get_transformed_position();
|
||||
}
|
||||
|
||||
return a.x - b.x;
|
||||
},
|
||||
|
||||
popup: function(backwards) {
|
||||
// Start with the set of focus groups that are currently mapped
|
||||
let items = this._items.filter(function (item) { return item.proxy.mapped; });
|
||||
|
||||
// And add the windows metacity would show in its Ctrl-Alt-Tab list
|
||||
if (!Main.overview.visible) {
|
||||
let screen = global.screen;
|
||||
let display = screen.get_display();
|
||||
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
|
||||
let windowTracker = Shell.WindowTracker.get_default();
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let icon;
|
||||
let app = windowTracker.get_window_app(windows[i]);
|
||||
if (app)
|
||||
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
||||
else
|
||||
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
||||
items.push({ window: windows[i],
|
||||
name: windows[i].title,
|
||||
iconActor: icon,
|
||||
sortGroup: SortGroup.MIDDLE });
|
||||
}
|
||||
}
|
||||
|
||||
if (!items.length)
|
||||
return;
|
||||
|
||||
items.sort(Lang.bind(this, this._sortItems));
|
||||
new CtrlAltTabPopup().show(items, backwards);
|
||||
}
|
||||
};
|
||||
|
||||
function mod(a, b) {
|
||||
return (a + b) % b;
|
||||
}
|
||||
|
||||
function CtrlAltTabPopup() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
CtrlAltTabPopup.prototype = {
|
||||
_init : function() {
|
||||
this.actor = new Shell.GenericContainer({ name: 'ctrlAltTabPopup',
|
||||
reactive: true });
|
||||
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._haveModal = false;
|
||||
this._selection = 0;
|
||||
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
alloc.min_size = primary.width;
|
||||
alloc.natural_size = primary.width;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
alloc.min_size = primary.height;
|
||||
alloc.natural_size = primary.height;
|
||||
},
|
||||
|
||||
_allocate: function (actor, box, flags) {
|
||||
let childBox = new Clutter.ActorBox();
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
|
||||
let vPadding = this.actor.get_theme_node().get_vertical_padding();
|
||||
let hPadding = this.actor.get_theme_node().get_horizontal_padding();
|
||||
|
||||
let [childMinHeight, childNaturalHeight] = this._switcher.actor.get_preferred_height(primary.width - hPadding);
|
||||
let [childMinWidth, childNaturalWidth] = this._switcher.actor.get_preferred_width(childNaturalHeight);
|
||||
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
|
||||
childBox.x2 = Math.min(primary.width - hPadding, childBox.x1 + childNaturalWidth);
|
||||
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
|
||||
childBox.y2 = childBox.y1 + childNaturalHeight;
|
||||
this._switcher.actor.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
show : function(items, startBackwards) {
|
||||
if (!Main.pushModal(this.actor))
|
||||
return false;
|
||||
this._haveModal = true;
|
||||
|
||||
this._keyPressEventId = this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
|
||||
this._keyReleaseEventId = this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
|
||||
|
||||
this._items = items;
|
||||
this._switcher = new CtrlAltTabSwitcher(items);
|
||||
this.actor.add_actor(this._switcher.actor);
|
||||
|
||||
if (startBackwards)
|
||||
this._selection = this._items.length - 1;
|
||||
this._select(this._selection);
|
||||
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
|
||||
this._finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
this.actor.opacity = 0;
|
||||
this.actor.show();
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: POPUP_FADE_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_next : function() {
|
||||
return mod(this._selection + 1, this._items.length);
|
||||
},
|
||||
|
||||
_previous : function() {
|
||||
return mod(this._selection - 1, this._items.length);
|
||||
},
|
||||
|
||||
_keyPressEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
|
||||
if (shift && keysym == Clutter.KEY_Tab)
|
||||
keysym = Clutter.ISO_Left_Tab;
|
||||
|
||||
if (keysym == Clutter.KEY_Escape)
|
||||
this.destroy();
|
||||
else if (keysym == Clutter.KEY_Tab)
|
||||
this._select(this._next());
|
||||
else if (keysym == Clutter.KEY_ISO_Left_Tab)
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.KEY_Left)
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.KEY_Right)
|
||||
this._select(this._next());
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_keyReleaseEvent : function(actor, event) {
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
let state = mods & Clutter.ModifierType.MOD1_MASK;
|
||||
|
||||
if (state == 0)
|
||||
this._finish();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_finish : function() {
|
||||
this.destroy();
|
||||
|
||||
Main.ctrlAltTabManager.focusGroup(this._items[this._selection]);
|
||||
},
|
||||
|
||||
_popModal: function() {
|
||||
if (this._haveModal) {
|
||||
Main.popModal(this.actor);
|
||||
this._haveModal = false;
|
||||
}
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
this._popModal();
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: POPUP_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.actor.destroy();
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
this._popModal();
|
||||
if (this._keyPressEventId)
|
||||
this.actor.disconnect(this._keyPressEventId);
|
||||
if (this._keyReleaseEventId)
|
||||
this.actor.disconnect(this._keyReleaseEventId);
|
||||
},
|
||||
|
||||
_select : function(num) {
|
||||
this._selection = num;
|
||||
this._switcher.highlight(num);
|
||||
}
|
||||
};
|
||||
|
||||
function CtrlAltTabSwitcher(items) {
|
||||
this._init(items);
|
||||
}
|
||||
|
||||
CtrlAltTabSwitcher.prototype = {
|
||||
__proto__ : AltTab.SwitcherList.prototype,
|
||||
|
||||
_init : function(items) {
|
||||
AltTab.SwitcherList.prototype._init.call(this, true);
|
||||
|
||||
for (let i = 0; i < items.length; i++)
|
||||
this._addIcon(items[i]);
|
||||
},
|
||||
|
||||
_addIcon : function(item) {
|
||||
let box = new St.BoxLayout({ style_class: 'alt-tab-app',
|
||||
vertical: true });
|
||||
|
||||
let icon = item.iconActor;
|
||||
if (!icon) {
|
||||
icon = new St.Icon({ icon_name: item.iconName,
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: POPUP_APPICON_SIZE });
|
||||
}
|
||||
box.add(icon, { x_fill: false, y_fill: false } );
|
||||
|
||||
let text = new St.Label({ text: item.name });
|
||||
box.add(text, { x_fill: false });
|
||||
|
||||
this.addItem(box);
|
||||
}
|
||||
};
|
553
js/ui/dash.js
@ -1,6 +1,5 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Signals = imports.signals;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -14,177 +13,22 @@ const AppFavorites = imports.ui.appFavorites;
|
||||
const DND = imports.ui.dnd;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Workspace = imports.ui.workspace;
|
||||
|
||||
const DASH_ANIMATION_TIME = 0.2;
|
||||
|
||||
// A container like StBin, but taking the child's scale into account
|
||||
// when requesting a size
|
||||
function DashItemContainer() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DashItemContainer.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new Shell.GenericContainer({ style_class: 'dash-item-container' });
|
||||
this.actor.connect('get-preferred-width',
|
||||
Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height',
|
||||
Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate',
|
||||
Lang.bind(this, this._allocate));
|
||||
this.actor._delegate = this;
|
||||
|
||||
this.child = null;
|
||||
this._childScale = 1;
|
||||
this._childOpacity = 255;
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
if (this.child == null)
|
||||
return;
|
||||
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] =
|
||||
this.child.get_preferred_size();
|
||||
let [childScaleX, childScaleY] = this.child.get_scale();
|
||||
|
||||
let childWidth = Math.min(natChildWidth * childScaleX, availWidth);
|
||||
let childHeight = Math.min(natChildHeight * childScaleY, availHeight);
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.x1 = (availWidth - childWidth) / 2;
|
||||
childBox.y1 = (availHeight - childHeight) / 2;
|
||||
childBox.x2 = childBox.x1 + childWidth;
|
||||
childBox.y2 = childBox.y1 + childHeight;
|
||||
|
||||
this.child.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
alloc.min_size = 0;
|
||||
alloc.natural_size = 0;
|
||||
|
||||
if (this.child == null)
|
||||
return;
|
||||
|
||||
let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
|
||||
alloc.min_size += minHeight * this.child.scale_y;
|
||||
alloc.natural_size += natHeight * this.child.scale_y;
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
alloc.min_size = 0;
|
||||
alloc.natural_size = 0;
|
||||
|
||||
if (this.child == null)
|
||||
return;
|
||||
|
||||
let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
|
||||
alloc.min_size = minWidth * this.child.scale_y;
|
||||
alloc.natural_size = natWidth * this.child.scale_y;
|
||||
},
|
||||
|
||||
setChild: function(actor) {
|
||||
if (this.child == actor)
|
||||
return;
|
||||
|
||||
this.actor.destroy_children();
|
||||
|
||||
this.child = actor;
|
||||
this.actor.add_actor(this.child);
|
||||
},
|
||||
|
||||
animateIn: function() {
|
||||
if (this.child == null)
|
||||
return;
|
||||
|
||||
this.childScale = 0;
|
||||
this.childOpacity = 0;
|
||||
Tweener.addTween(this,
|
||||
{ childScale: 1.0,
|
||||
childOpacity: 255,
|
||||
time: DASH_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
|
||||
animateOutAndDestroy: function() {
|
||||
if (this.child == null) {
|
||||
this.actor.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
this.childScale = 1.0;
|
||||
Tweener.addTween(this,
|
||||
{ childScale: 0.0,
|
||||
childOpacity: 0,
|
||||
time: DASH_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.actor.destroy();
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
set childScale(scale) {
|
||||
this._childScale = scale;
|
||||
|
||||
if (this.child == null)
|
||||
return;
|
||||
|
||||
this.child.set_scale_with_gravity(scale, scale,
|
||||
Clutter.Gravity.CENTER);
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get childScale() {
|
||||
return this._childScale;
|
||||
},
|
||||
|
||||
set childOpacity(opacity) {
|
||||
this._childOpacity = opacity;
|
||||
|
||||
if (this.child == null)
|
||||
return;
|
||||
|
||||
this.child.set_opacity(opacity);
|
||||
this.actor.queue_redraw();
|
||||
},
|
||||
|
||||
get childOpacity() {
|
||||
return this._childOpacity;
|
||||
}
|
||||
};
|
||||
|
||||
function RemoveFavoriteIcon() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
RemoveFavoriteIcon.prototype = {
|
||||
__proto__: DashItemContainer.prototype,
|
||||
|
||||
_init: function() {
|
||||
DashItemContainer.prototype._init.call(this);
|
||||
|
||||
this._iconBin = new St.Bin({ style_class: 'remove-favorite' });
|
||||
this.actor = new St.Bin({ style_class: 'remove-favorite' });
|
||||
this._iconActor = null;
|
||||
this.icon = new IconGrid.BaseIcon(_("Remove"),
|
||||
{ setSizeManually: true,
|
||||
showLabel: false,
|
||||
createIcon: Lang.bind(this, this._createIcon) });
|
||||
this._iconBin.set_child(this.icon.actor);
|
||||
this._iconBin._delegate = this;
|
||||
|
||||
this.setChild(this._iconBin);
|
||||
this.hiding = false;
|
||||
},
|
||||
|
||||
animateOutAndDestroy: function() {
|
||||
DashItemContainer.prototype.animateOutAndDestroy.call(this);
|
||||
this.hiding = true;
|
||||
this.actor.set_child(this.icon.actor);
|
||||
this.actor._delegate = this;
|
||||
},
|
||||
|
||||
_createIcon: function(size) {
|
||||
@ -195,7 +39,7 @@ RemoveFavoriteIcon.prototype = {
|
||||
},
|
||||
|
||||
setHover: function(hovered) {
|
||||
this._iconBin.set_hover(hovered);
|
||||
this.actor.set_hover(hovered);
|
||||
if (this._iconActor)
|
||||
this._iconActor.set_hover(hovered);
|
||||
},
|
||||
@ -210,7 +54,7 @@ RemoveFavoriteIcon.prototype = {
|
||||
if (source instanceof AppDisplay.AppWellIcon) {
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
app = appSystem.get_app(source.getId());
|
||||
} else if (source.metaWindow) {
|
||||
} else if (source instanceof Workspace.WindowClone) {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
app = tracker.get_window_app(source.metaWindow);
|
||||
}
|
||||
@ -228,35 +72,24 @@ RemoveFavoriteIcon.prototype = {
|
||||
};
|
||||
|
||||
|
||||
function DragPlaceholderItem() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DragPlaceholderItem.prototype = {
|
||||
__proto__: DashItemContainer.prototype,
|
||||
|
||||
_init: function() {
|
||||
DashItemContainer.prototype._init.call(this);
|
||||
this.setChild(new St.Bin({ style_class: 'dash-placeholder' }));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function Dash() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
Dash.prototype = {
|
||||
_init : function() {
|
||||
this._placeholderText = null;
|
||||
this._menus = [];
|
||||
this._menuDisplays = [];
|
||||
this._maxHeight = -1;
|
||||
this.iconSize = 64;
|
||||
this._shownInitially = false;
|
||||
this._iconSize = 48;
|
||||
|
||||
this._dragPlaceholder = null;
|
||||
this._dragPlaceholderPos = -1;
|
||||
this._animatingPlaceholdersCount = 0;
|
||||
this._favRemoveTarget = null;
|
||||
|
||||
this._favorites = [];
|
||||
|
||||
this._box = new St.BoxLayout({ name: 'dash',
|
||||
vertical: true,
|
||||
clip_to_allocation: true });
|
||||
@ -278,53 +111,38 @@ Dash.prototype = {
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
||||
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
|
||||
this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
|
||||
},
|
||||
|
||||
Main.overview.connect('item-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
Main.overview.connect('item-drag-end',
|
||||
Lang.bind(this, this._onDragEnd));
|
||||
Main.overview.connect('item-drag-cancelled',
|
||||
Lang.bind(this, this._onDragCancelled));
|
||||
Main.overview.connect('window-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
Main.overview.connect('window-drag-cancelled',
|
||||
Lang.bind(this, this._onDragCancelled));
|
||||
Main.overview.connect('window-drag-end',
|
||||
Lang.bind(this, this._onDragEnd));
|
||||
show: function() {
|
||||
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
this._itemDragEndId = Main.overview.connect('item-drag-end',
|
||||
Lang.bind(this, this._onDragEnd));
|
||||
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
this._windowDragEndId = Main.overview.connect('window-drag-end',
|
||||
Lang.bind(this, this._onDragEnd));
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
Main.overview.disconnect(this._itemDragBeginId);
|
||||
Main.overview.disconnect(this._itemDragEndId);
|
||||
Main.overview.disconnect(this._windowDragBeginId);
|
||||
Main.overview.disconnect(this._windowDragEndId);
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
this._dragCancelled = false;
|
||||
this._dragMonitor = {
|
||||
dragMotion: Lang.bind(this, this._onDragMotion)
|
||||
};
|
||||
DND.addDragMonitor(this._dragMonitor);
|
||||
},
|
||||
|
||||
_onDragCancelled: function() {
|
||||
this._dragCancelled = true;
|
||||
this._endDrag();
|
||||
},
|
||||
|
||||
_onDragEnd: function() {
|
||||
if (this._dragCancelled)
|
||||
return;
|
||||
|
||||
this._endDrag();
|
||||
},
|
||||
|
||||
_endDrag: function() {
|
||||
this._clearDragPlaceholder();
|
||||
if (this._favRemoveTarget) {
|
||||
this._favRemoveTarget.actor.hide();
|
||||
this._adjustIconSize();
|
||||
this._favRemoveTarget.actor.show();
|
||||
|
||||
this._favRemoveTarget.animateOutAndDestroy();
|
||||
this._favRemoveTarget.actor.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
this._favRemoveTarget = null;
|
||||
}));
|
||||
this._favRemoveTarget.actor.destroy();
|
||||
this._favRemoveTarget = null;
|
||||
}
|
||||
DND.removeMonitor(this._dragMonitor);
|
||||
},
|
||||
@ -333,7 +151,7 @@ Dash.prototype = {
|
||||
let app = null;
|
||||
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
|
||||
app = this._appSystem.get_app(dragEvent.source.getId());
|
||||
else if (dragEvent.source.metaWindow)
|
||||
else if (dragEvent.source instanceof Workspace.WindowClone)
|
||||
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
|
||||
else
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
@ -346,10 +164,8 @@ Dash.prototype = {
|
||||
|
||||
if (srcIsFavorite && this._favRemoveTarget == null) {
|
||||
this._favRemoveTarget = new RemoveFavoriteIcon();
|
||||
this._favRemoveTarget.icon.setIconSize(this.iconSize);
|
||||
this._favRemoveTarget.icon.setIconSize(this._iconSize);
|
||||
this._box.add(this._favRemoveTarget.actor);
|
||||
this._adjustIconSize();
|
||||
this._favRemoveTarget.animateIn();
|
||||
}
|
||||
|
||||
let favRemoveHovered = false;
|
||||
@ -377,10 +193,8 @@ Dash.prototype = {
|
||||
Main.queueDeferredWork(this._workId);
|
||||
},
|
||||
|
||||
_createAppItem: function(app) {
|
||||
let display = new AppDisplay.AppWellIcon(app,
|
||||
{ setSizeManually: true,
|
||||
showLabel: false });
|
||||
_addApp: function(app) {
|
||||
let display = new AppDisplay.AppWellIcon(app);
|
||||
display._draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
display.actor.opacity = 50;
|
||||
@ -389,227 +203,63 @@ Dash.prototype = {
|
||||
Lang.bind(this, function() {
|
||||
display.actor.opacity = 255;
|
||||
}));
|
||||
display.actor.set_tooltip_text(app.get_name());
|
||||
|
||||
let item = new DashItemContainer();
|
||||
item.setChild(display.actor);
|
||||
|
||||
display.icon.setIconSize(this.iconSize);
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
_adjustIconSize: function() {
|
||||
let children = this._box.get_children();
|
||||
if (children.length == 0) {
|
||||
this._box.add_style_pseudo_class('empty');
|
||||
return;
|
||||
}
|
||||
|
||||
this._box.remove_style_pseudo_class('empty');
|
||||
|
||||
if (this._maxHeight == -1)
|
||||
return;
|
||||
|
||||
let iconChildren = children.filter(function(actor) {
|
||||
return actor.visible &&
|
||||
actor._delegate.child &&
|
||||
actor._delegate.child._delegate &&
|
||||
actor._delegate.child._delegate.icon;
|
||||
});
|
||||
|
||||
// Compute the amount of extra space (or missing space) we have
|
||||
// per icon with the current icon size
|
||||
let [minHeight, natHeight] = this.actor.get_preferred_height(-1);
|
||||
let diff = (this._maxHeight - natHeight) / iconChildren.length;
|
||||
|
||||
let iconSizes = [ 16, 22, 24, 32, 48, 64 ];
|
||||
|
||||
let newIconSize = 16;
|
||||
for (let i = 0; i < iconSizes.length; i++) {
|
||||
if (iconSizes[i] < this.iconSize + diff)
|
||||
newIconSize = iconSizes[i];
|
||||
}
|
||||
|
||||
if (newIconSize == this.iconSize)
|
||||
return;
|
||||
|
||||
let oldIconSize = this.iconSize;
|
||||
this.iconSize = newIconSize;
|
||||
|
||||
let scale = oldIconSize / newIconSize;
|
||||
for (let i = 0; i < iconChildren.length; i++) {
|
||||
let icon = iconChildren[i]._delegate.child._delegate.icon;
|
||||
|
||||
// Set the new size immediately, to keep the icons' sizes
|
||||
// in sync with this.iconSize
|
||||
icon.setIconSize(this.iconSize);
|
||||
|
||||
// Don't animate the icon size change when the overview
|
||||
// is not visible or when initially filling the dash
|
||||
if (!Main.overview.visible || !this._shownInitially)
|
||||
continue;
|
||||
|
||||
let [targetWidth, targetHeight] = icon.icon.get_size();
|
||||
|
||||
// Scale the icon's texture to the previous size and
|
||||
// tween to the new size
|
||||
icon.icon.set_size(icon.icon.width * scale,
|
||||
icon.icon.height * scale);
|
||||
|
||||
Tweener.addTween(icon.icon,
|
||||
{ width: targetWidth,
|
||||
height: targetHeight,
|
||||
time: DASH_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
this._box.add(display.actor);
|
||||
},
|
||||
|
||||
_redisplay: function () {
|
||||
this._box.hide();
|
||||
this._box.remove_all();
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
||||
|
||||
/* hardcode here pending some design about how exactly desktop contexts behave */
|
||||
let contextId = '';
|
||||
|
||||
let running = this._tracker.get_running_apps(contextId);
|
||||
let runningIds = this._appIdListToHash(running);
|
||||
|
||||
let children = this._box.get_children().filter(function(actor) {
|
||||
return actor._delegate.child &&
|
||||
actor._delegate.child._delegate &&
|
||||
actor._delegate.child._delegate.app;
|
||||
});
|
||||
// Apps currently in the dash
|
||||
let oldApps = children.map(function(actor) {
|
||||
return actor._delegate.child._delegate.app;
|
||||
});
|
||||
// Apps supposed to be in the dash
|
||||
let newApps = [];
|
||||
|
||||
for (let id in favorites)
|
||||
newApps.push(favorites[id]);
|
||||
for (let id in favorites) {
|
||||
let app = favorites[id];
|
||||
this._addApp(app);
|
||||
}
|
||||
|
||||
for (let i = 0; i < running.length; i++) {
|
||||
let app = running[i];
|
||||
if (app.get_id() in favorites)
|
||||
continue;
|
||||
newApps.push(app);
|
||||
this._addApp(app);
|
||||
}
|
||||
if (this._placeholderText) {
|
||||
this._placeholderText.destroy();
|
||||
this._placeholderText = null;
|
||||
}
|
||||
|
||||
// Figure out the actual changes to the list of items; we iterate
|
||||
// over both the list of items currently in the dash and the list
|
||||
// of items expected there, and collect additions and removals.
|
||||
// Moves are both an addition and a removal, where the order of
|
||||
// the operations depends on whether we encounter the position
|
||||
// where the item has been added first or the one from where it
|
||||
// was removed.
|
||||
// There is an assumption that only one item is moved at a given
|
||||
// time; when moving several items at once, everything will still
|
||||
// end up at the right position, but there might be additional
|
||||
// additions/removals (e.g. it might remove all the launchers
|
||||
// and add them back in the new order even if a smaller set of
|
||||
// additions and removals is possible).
|
||||
// If above assumptions turns out to be a problem, we might need
|
||||
// to use a more sophisticated algorithm, e.g. Longest Common
|
||||
// Subsequence as used by diff.
|
||||
let addedItems = [];
|
||||
let removedActors = [];
|
||||
let children = this._box.get_children();
|
||||
if (children.length == 0) {
|
||||
this._placeholderText = new St.Label({ text: _("Drag here to add favorites") });
|
||||
this._box.add_actor(this._placeholderText);
|
||||
} else if (this._maxHeight > -1) {
|
||||
let iconSizes = [ 48, 32, 24, 22, 16 ];
|
||||
|
||||
let newIndex = 0;
|
||||
let oldIndex = 0;
|
||||
while (newIndex < newApps.length || oldIndex < oldApps.length) {
|
||||
// No change at oldIndex/newIndex
|
||||
if (oldApps[oldIndex] == newApps[newIndex]) {
|
||||
oldIndex++;
|
||||
newIndex++;
|
||||
continue;
|
||||
}
|
||||
for (let i = 0; i < iconSizes.length; i++) {
|
||||
let minHeight, natHeight;
|
||||
|
||||
// App removed at oldIndex
|
||||
if (oldApps[oldIndex] &&
|
||||
newApps.indexOf(oldApps[oldIndex]) == -1) {
|
||||
removedActors.push(children[oldIndex]);
|
||||
oldIndex++;
|
||||
continue;
|
||||
}
|
||||
this._iconSize = iconSizes[i];
|
||||
for (let j = 0; j < children.length; j++)
|
||||
children[j]._delegate.icon.setIconSize(this._iconSize);
|
||||
|
||||
// App added at newIndex
|
||||
if (newApps[newIndex] &&
|
||||
oldApps.indexOf(newApps[newIndex]) == -1) {
|
||||
addedItems.push({ app: newApps[newIndex],
|
||||
item: this._createAppItem(newApps[newIndex]),
|
||||
pos: newIndex });
|
||||
newIndex++;
|
||||
continue;
|
||||
}
|
||||
[minHeight, natHeight] = this.actor.get_preferred_height(-1);
|
||||
|
||||
// App moved
|
||||
let insertHere = newApps[newIndex + 1] &&
|
||||
newApps[newIndex + 1] == oldApps[oldIndex];
|
||||
let alreadyRemoved = removedActors.reduce(function(result, actor) {
|
||||
let removedApp = actor._delegate.child._delegate.app;
|
||||
return result || removedApp == newApps[newIndex];
|
||||
}, false);
|
||||
|
||||
if (insertHere || alreadyRemoved) {
|
||||
let newItem = this._createAppItem(newApps[newIndex]);
|
||||
addedItems.push({ app: newApps[newIndex],
|
||||
item: newItem,
|
||||
pos: newIndex + removedActors.length });
|
||||
newIndex++;
|
||||
} else {
|
||||
removedActors.push(children[oldIndex]);
|
||||
oldIndex++;
|
||||
if (natHeight <= this._maxHeight)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < addedItems.length; i++)
|
||||
this._box.insert_actor(addedItems[i].item.actor,
|
||||
addedItems[i].pos);
|
||||
|
||||
// Hide removed actors to not take them into account
|
||||
// when adjusting the icon size ...
|
||||
for (let i = 0; i < removedActors.length; i++)
|
||||
removedActors[i].hide();
|
||||
|
||||
// ... and do the same for the remove target if necessary
|
||||
if (this._favRemoveTarget && this._favRemoveTarget.hiding)
|
||||
this._favRemoveTarget.actor.hide();
|
||||
|
||||
this._adjustIconSize();
|
||||
|
||||
if (this._favRemoveTarget && this._favRemoveTarget.hiding)
|
||||
this._favRemoveTarget.actor.show();
|
||||
|
||||
// Skip animations on first run when adding the initial set
|
||||
// of items, to avoid all items zooming in at once
|
||||
if (!this._shownInitially) {
|
||||
this._shownInitially = true;
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < removedActors.length; i++) {
|
||||
removedActors[i].show();
|
||||
let item = removedActors[i]._delegate;
|
||||
|
||||
// Don't animate item removal when the overview is hidden
|
||||
if (Main.overview.visible)
|
||||
item.animateOutAndDestroy();
|
||||
else
|
||||
item.actor.destroy();
|
||||
}
|
||||
|
||||
// Don't animate item addition when the overview is hidden
|
||||
if (!Main.overview.visible)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < addedItems.length; i++)
|
||||
addedItems[i].item.animateIn();
|
||||
this._box.show();
|
||||
},
|
||||
|
||||
_clearDragPlaceholder: function() {
|
||||
if (this._dragPlaceholder) {
|
||||
this._dragPlaceholder.animateOutAndDestroy();
|
||||
this._dragPlaceholder.destroy();
|
||||
this._dragPlaceholder = null;
|
||||
this._dragPlaceholderPos = -1;
|
||||
}
|
||||
@ -619,79 +269,40 @@ Dash.prototype = {
|
||||
let app = null;
|
||||
if (source instanceof AppDisplay.AppWellIcon)
|
||||
app = this._appSystem.get_app(source.getId());
|
||||
else if (source.metaWindow)
|
||||
else if (source instanceof Workspace.WindowClone)
|
||||
app = this._tracker.get_window_app(source.metaWindow);
|
||||
|
||||
// Don't allow favoriting of transient apps
|
||||
if (app == null || app.is_transient())
|
||||
return DND.DragMotionResult.NO_DROP;
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavorites();
|
||||
let numFavorites = favorites.length;
|
||||
|
||||
let favPos = favorites.indexOf(app);
|
||||
|
||||
let children = this._box.get_children();
|
||||
let numChildren = children.length;
|
||||
let numFavorites = AppFavorites.getAppFavorites().getFavorites().length;
|
||||
let numChildren = this._box.get_children().length;
|
||||
let boxHeight = this._box.height;
|
||||
|
||||
// Keep the placeholder out of the index calculation; assuming that
|
||||
// the remove target has the same size as "normal" items, we don't
|
||||
// need to do the same adjustment there.
|
||||
if (this._dragPlaceholder) {
|
||||
boxHeight -= this._dragPlaceholder.actor.height;
|
||||
boxHeight -= this._dragPlaceholder.height;
|
||||
numChildren--;
|
||||
}
|
||||
|
||||
let pos = Math.round(y * numChildren / boxHeight);
|
||||
|
||||
if (pos != this._dragPlaceholderPos && pos <= numFavorites) {
|
||||
if (this._animatingPlaceholdersCount > 0) {
|
||||
let appChildren = children.filter(function(actor) {
|
||||
return actor._delegate &&
|
||||
actor._delegate.child &&
|
||||
actor._delegate.child._delegate &&
|
||||
actor._delegate.child._delegate.app;
|
||||
});
|
||||
this._dragPlaceholderPos = children.indexOf(appChildren[pos]);
|
||||
} else {
|
||||
this._dragPlaceholderPos = pos;
|
||||
}
|
||||
|
||||
// Don't allow positioning before or after self
|
||||
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
|
||||
if (this._dragPlaceholder) {
|
||||
this._dragPlaceholder.animateOutAndDestroy();
|
||||
this._animatingPlaceholdersCount++;
|
||||
this._dragPlaceholder.actor.connect('destroy',
|
||||
Lang.bind(this, function() {
|
||||
this._animatingPlaceholdersCount--;
|
||||
}));
|
||||
}
|
||||
this._dragPlaceholder = null;
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
}
|
||||
|
||||
// If the placeholder already exists, we just move
|
||||
// it, but if we are adding it, expand its size in
|
||||
// an animation
|
||||
let fadeIn;
|
||||
if (this._dragPlaceholder) {
|
||||
this._dragPlaceholder.actor.destroy();
|
||||
fadeIn = false;
|
||||
} else {
|
||||
fadeIn = true;
|
||||
}
|
||||
|
||||
this._dragPlaceholder = new DragPlaceholderItem();
|
||||
this._box.insert_actor(this._dragPlaceholder.actor,
|
||||
this._dragPlaceholderPos);
|
||||
if (fadeIn)
|
||||
this._dragPlaceholder.animateIn();
|
||||
this._dragPlaceholderPos = pos;
|
||||
if (this._dragPlaceholder)
|
||||
this._dragPlaceholder.destroy();
|
||||
this._dragPlaceholder = new St.Bin({ style_class: 'dash-placeholder' });
|
||||
this._box.insert_actor(this._dragPlaceholder, pos);
|
||||
}
|
||||
|
||||
let srcIsFavorite = (favPos != -1);
|
||||
let id = app.get_id();
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
||||
|
||||
let srcIsFavorite = (id in favorites);
|
||||
|
||||
if (srcIsFavorite)
|
||||
return DND.DragMotionResult.MOVE_DROP;
|
||||
@ -704,7 +315,7 @@ Dash.prototype = {
|
||||
let app = null;
|
||||
if (source instanceof AppDisplay.AppWellIcon) {
|
||||
app = this._appSystem.get_app(source.getId());
|
||||
} else if (source.metaWindow) {
|
||||
} else if (source instanceof Workspace.WindowClone) {
|
||||
app = this._tracker.get_window_app(source.metaWindow);
|
||||
}
|
||||
|
||||
@ -722,11 +333,7 @@ Dash.prototype = {
|
||||
let favPos = 0;
|
||||
let children = this._box.get_children();
|
||||
for (let i = 0; i < this._dragPlaceholderPos; i++) {
|
||||
if (this._dragPlaceholder &&
|
||||
children[i] == this._dragPlaceholder.actor)
|
||||
continue;
|
||||
|
||||
let childId = children[i]._delegate.child._delegate.app.get_id();
|
||||
let childId = children[i]._delegate.app.get_id();
|
||||
if (childId == id)
|
||||
continue;
|
||||
if (childId in favorites)
|
||||
|
@ -1,212 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Cairo = imports.cairo;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Util = imports.misc.util;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Calendar = imports.ui.calendar;
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||
|
||||
// in org.gnome.shell.clock
|
||||
const CLOCK_SHOW_DATE_KEY = 'show-date';
|
||||
const CLOCK_SHOW_SECONDS_KEY = 'show-seconds';
|
||||
|
||||
function _onVertSepRepaint (area)
|
||||
{
|
||||
let cr = area.get_context();
|
||||
let themeNode = area.get_theme_node();
|
||||
let [width, height] = area.get_surface_size();
|
||||
let stippleColor = themeNode.get_color('-stipple-color');
|
||||
let stippleWidth = themeNode.get_length('-stipple-width');
|
||||
let x = Math.floor(width/2) + 0.5;
|
||||
cr.moveTo(x, 0);
|
||||
cr.lineTo(x, height);
|
||||
Clutter.cairo_set_source_color(cr, stippleColor);
|
||||
cr.setDash([1, 3], 1); // Hard-code for now
|
||||
cr.setLineWidth(stippleWidth);
|
||||
cr.stroke();
|
||||
};
|
||||
|
||||
function DateMenuButton() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DateMenuButton.prototype = {
|
||||
__proto__: PanelMenu.Button.prototype,
|
||||
|
||||
_init: function() {
|
||||
let item;
|
||||
let hbox;
|
||||
let vbox;
|
||||
|
||||
this._eventSource = new Calendar.DBusEventSource();
|
||||
|
||||
let menuAlignment = 0.25;
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
menuAlignment = 1.0 - menuAlignment;
|
||||
PanelMenu.Button.prototype._init.call(this, menuAlignment);
|
||||
|
||||
this._clock = new St.Label();
|
||||
this.actor.set_child(this._clock);
|
||||
|
||||
hbox = new St.BoxLayout({name: 'calendarArea'});
|
||||
this.menu.addActor(hbox);
|
||||
|
||||
// Fill up the first column
|
||||
|
||||
vbox = new St.BoxLayout({vertical: true});
|
||||
hbox.add(vbox);
|
||||
|
||||
// Date
|
||||
this._date = new St.Label();
|
||||
this._date.style_class = 'datemenu-date-label';
|
||||
vbox.add(this._date);
|
||||
|
||||
this._eventList = new Calendar.EventsList(this._eventSource);
|
||||
|
||||
// Calendar
|
||||
this._calendar = new Calendar.Calendar(this._eventSource);
|
||||
this._calendar.connect('selected-date-changed',
|
||||
Lang.bind(this, function(calendar, date) {
|
||||
this._eventList.setDate(date);
|
||||
}));
|
||||
vbox.add(this._calendar.actor);
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
item.setColumnWidths(1);
|
||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
item.actor.can_focus = false;
|
||||
vbox.add(item.actor);
|
||||
|
||||
// Add vertical separator
|
||||
|
||||
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
pseudo_class: 'highlighted' });
|
||||
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||
hbox.add(item);
|
||||
|
||||
// Fill up the second column
|
||||
|
||||
vbox = new St.BoxLayout({vertical: true});
|
||||
hbox.add(vbox, { expand: true });
|
||||
|
||||
// Event list
|
||||
vbox.add(this._eventList.actor, { expand: true });
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
item.actor.can_focus = false;
|
||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
// Whenever the menu is opened, select today
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
if (isOpen) {
|
||||
let now = new Date();
|
||||
/* Passing true to setDate() forces events to be reloaded. We
|
||||
* want this behavior, because
|
||||
*
|
||||
* o It will cause activation of the calendar server which is
|
||||
* useful if it has crashed
|
||||
*
|
||||
* o It will cause the calendar server to reload events which
|
||||
* is useful if dynamic updates are not supported or not
|
||||
* properly working
|
||||
*
|
||||
* Since this only happens when the menu is opened, the cost
|
||||
* isn't very big.
|
||||
*/
|
||||
this._calendar.setDate(now, true);
|
||||
// No need to update this._eventList as ::selected-date-changed
|
||||
// signal will fire
|
||||
}
|
||||
}));
|
||||
|
||||
// Done with hbox for calendar and event list
|
||||
|
||||
// Track changes to clock settings
|
||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||
|
||||
// Start the clock
|
||||
this._updateClockAndDate();
|
||||
},
|
||||
|
||||
_updateClockAndDate: function() {
|
||||
let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
|
||||
let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
|
||||
let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
|
||||
|
||||
let clockFormat;
|
||||
let dateFormat;
|
||||
|
||||
switch (format) {
|
||||
case '24h':
|
||||
if (showDate)
|
||||
/* Translators: This is the time format with date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
|
||||
: _("%a %b %e, %R");
|
||||
else
|
||||
/* Translators: This is the time format without date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %R:%S")
|
||||
: _("%a %R");
|
||||
break;
|
||||
case '12h':
|
||||
default:
|
||||
if (showDate)
|
||||
/* Translators: This is a time format with date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
|
||||
: _("%a %b %e, %l:%M %p");
|
||||
else
|
||||
/* Translators: This is a time format without date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
|
||||
: _("%a %l:%M %p");
|
||||
break;
|
||||
}
|
||||
|
||||
let displayDate = new Date();
|
||||
|
||||
this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
|
||||
|
||||
/* Translators: This is the date format to use when the calendar popup is
|
||||
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
*/
|
||||
dateFormat = _("%A %B %e, %Y");
|
||||
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
||||
|
||||
Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate));
|
||||
return false;
|
||||
},
|
||||
|
||||
_onPreferencesActivate: function() {
|
||||
this.menu.close();
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-datetime-panel.desktop');
|
||||
app.activate(-1);
|
||||
},
|
||||
|
||||
_onOpenCalendarActivate: function() {
|
||||
this.menu.close();
|
||||
// TODO: pass the selected day
|
||||
Util.spawn(['evolution', '-c', 'calendar']);
|
||||
}
|
||||
};
|
133
js/ui/dnd.js
@ -26,9 +26,9 @@ const DragMotionResult = {
|
||||
};
|
||||
|
||||
const DRAG_CURSOR_MAP = {
|
||||
0: Shell.Cursor.DND_UNSUPPORTED_TARGET,
|
||||
1: Shell.Cursor.DND_COPY,
|
||||
2: Shell.Cursor.DND_MOVE
|
||||
0: Shell.Cursor.UNSUPPORTED_TARGET,
|
||||
1: Shell.Cursor.COPY,
|
||||
2: Shell.Cursor.MOVE
|
||||
};
|
||||
|
||||
const DragDropResult = {
|
||||
@ -87,10 +87,6 @@ _Draggable.prototype = {
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, function() {
|
||||
this._actorDestroyed = true;
|
||||
// If the drag actor is destroyed and we were going to fix
|
||||
// up its hover state, fix up the parent hover state instead
|
||||
if (this.actor == this._firstLeaveActor)
|
||||
this._firstLeaveActor = this._dragOrigParent;
|
||||
if (this._dragInProgress)
|
||||
this._cancelDrag(global.get_current_time());
|
||||
this.disconnectAll();
|
||||
@ -104,14 +100,6 @@ _Draggable.prototype = {
|
||||
this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
|
||||
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
|
||||
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
|
||||
|
||||
// During the drag, we eat enter/leave events so that actors don't prelight or show
|
||||
// tooltips. But we remember the actors that we first left/last entered so we can
|
||||
// fix up the hover state after the drag ends.
|
||||
this._firstLeaveActor = null;
|
||||
this._lastEnterActor = null;
|
||||
|
||||
this._eventsGrabbed = false;
|
||||
},
|
||||
|
||||
_onButtonPress : function (actor, event) {
|
||||
@ -122,11 +110,11 @@ _Draggable.prototype = {
|
||||
return false;
|
||||
|
||||
this._buttonDown = true;
|
||||
// special case St.Button: grabbing the pointer would mess up the
|
||||
// special case St.Clickable: grabbing the pointer would mess up the
|
||||
// internal state, so we start the drag manually on hover change
|
||||
if (this.actor instanceof St.Button)
|
||||
if (this.actor instanceof St.Clickable)
|
||||
this.actor.connect('notify::hover',
|
||||
Lang.bind(this, this._onButtonHoverChanged));
|
||||
Lang.bind(this, this._onClickableHoverChanged));
|
||||
else
|
||||
this._grabActor();
|
||||
|
||||
@ -137,8 +125,8 @@ _Draggable.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
_onButtonHoverChanged: function(button) {
|
||||
if (button.hover || !button.pressed)
|
||||
_onClickableHoverChanged: function(button) {
|
||||
if (button.hover || !button.held)
|
||||
return;
|
||||
|
||||
button.fake_release();
|
||||
@ -154,26 +142,18 @@ _Draggable.prototype = {
|
||||
|
||||
_ungrabActor: function() {
|
||||
Clutter.ungrab_pointer();
|
||||
if (!this._onEventId)
|
||||
return;
|
||||
this.actor.disconnect(this._onEventId);
|
||||
this._onEventId = null;
|
||||
},
|
||||
|
||||
_grabEvents: function() {
|
||||
if (!this._eventsGrabbed) {
|
||||
Clutter.grab_pointer(_getEventHandlerActor());
|
||||
Clutter.grab_keyboard(_getEventHandlerActor());
|
||||
this._eventsGrabbed = true;
|
||||
}
|
||||
Clutter.grab_pointer(_getEventHandlerActor());
|
||||
Clutter.grab_keyboard(_getEventHandlerActor());
|
||||
},
|
||||
|
||||
_ungrabEvents: function() {
|
||||
if (this._eventsGrabbed) {
|
||||
Clutter.ungrab_pointer();
|
||||
Clutter.ungrab_keyboard();
|
||||
this._eventsGrabbed = false;
|
||||
}
|
||||
Clutter.ungrab_pointer();
|
||||
Clutter.ungrab_keyboard();
|
||||
},
|
||||
|
||||
_onEvent: function(actor, event) {
|
||||
@ -210,11 +190,6 @@ _Draggable.prototype = {
|
||||
this._cancelDrag(event.get_time());
|
||||
return true;
|
||||
}
|
||||
} else if (event.type() == Clutter.EventType.LEAVE) {
|
||||
if (this._firstLeaveActor == null)
|
||||
this._firstLeaveActor = event.get_source();
|
||||
} else if (event.type() == Clutter.EventType.ENTER) {
|
||||
this._lastEnterActor = event.get_source();
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -238,7 +213,7 @@ _Draggable.prototype = {
|
||||
if (this._onEventId)
|
||||
this._ungrabActor();
|
||||
this._grabEvents();
|
||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
||||
global.set_cursor(Shell.Cursor.IN_DRAG);
|
||||
|
||||
this._dragX = this._dragStartX = stageX;
|
||||
this._dragY = this._dragStartY = stageY;
|
||||
@ -399,7 +374,7 @@ _Draggable.prototype = {
|
||||
}
|
||||
target = target.get_parent();
|
||||
}
|
||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
||||
global.set_cursor(Shell.Cursor.IN_DRAG);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -465,51 +440,25 @@ _Draggable.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
// Get position of the drag actor's source if the source is still around,
|
||||
// or return the original location if the actor itself was being dragged
|
||||
// or the source is no longer around.
|
||||
_getRestoreLocation: function() {
|
||||
let x, y, scale;
|
||||
let locX = this._snapBackX;
|
||||
let locY = this._snapBackY;
|
||||
|
||||
if (this._dragActorSource && this._dragActorSource.visible) {
|
||||
// Snap the clone back to its source
|
||||
[x, y] = this._dragActorSource.get_transformed_position();
|
||||
let [sourceScaledWidth, sourceScaledHeight] = this._dragActorSource.get_transformed_size();
|
||||
scale = this._dragActor.width / sourceScaledWidth;
|
||||
} else if (this._dragOrigParent) {
|
||||
// Snap the actor back to its original position within
|
||||
// its parent, adjusting for the fact that the parent
|
||||
// may have been moved or scaled
|
||||
let [parentX, parentY] = this._dragOrigParent.get_transformed_position();
|
||||
let [parentWidth, parentHeight] = this._dragOrigParent.get_size();
|
||||
let [parentScaledWidth, parentScaledHeight] = this._dragOrigParent.get_transformed_size();
|
||||
let parentScale = 1.0;
|
||||
if (parentWidth != 0)
|
||||
parentScale = parentScaledWidth / parentWidth;
|
||||
|
||||
x = parentX + parentScale * this._dragOrigX;
|
||||
y = parentY + parentScale * this._dragOrigY;
|
||||
scale = this._dragOrigScale * parentScale;
|
||||
} else {
|
||||
// Snap back actor to its original stage position
|
||||
x = this._snapBackX;
|
||||
y = this._snapBackY;
|
||||
scale = this._snapBackScale;
|
||||
}
|
||||
|
||||
return [x, y, scale];
|
||||
if (this._dragActorSource && this._dragActorSource.visible)
|
||||
[locX, locY] = this._dragActorSource.get_transformed_position();
|
||||
return [locX, locY];
|
||||
},
|
||||
|
||||
_cancelDrag: function(eventTime) {
|
||||
this.emit('drag-cancelled', eventTime);
|
||||
this._dragInProgress = false;
|
||||
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
|
||||
let [snapBackX, snapBackY] = this._getRestoreLocation();
|
||||
|
||||
if (this._actorDestroyed) {
|
||||
global.unset_cursor();
|
||||
if (!this._buttonDown)
|
||||
this._dragComplete();
|
||||
this.emit('drag-end', eventTime, false);
|
||||
if (!this._dragOrigParent)
|
||||
this._dragActor.destroy();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -518,8 +467,8 @@ _Draggable.prototype = {
|
||||
Tweener.addTween(this._dragActor,
|
||||
{ x: snapBackX,
|
||||
y: snapBackY,
|
||||
scale_x: snapBackScale,
|
||||
scale_y: snapBackScale,
|
||||
scale_x: this._snapBackScale,
|
||||
scale_y: this._snapBackScale,
|
||||
opacity: this._dragOrigOpacity,
|
||||
time: SNAP_BACK_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
@ -531,11 +480,11 @@ _Draggable.prototype = {
|
||||
|
||||
_restoreDragActor: function(eventTime) {
|
||||
this._dragInProgress = false;
|
||||
[restoreX, restoreY, restoreScale] = this._getRestoreLocation();
|
||||
[restoreX, restoreY] = this._getRestoreLocation();
|
||||
|
||||
// fade the actor back in at its original location
|
||||
this._dragActor.set_position(restoreX, restoreY);
|
||||
this._dragActor.set_scale(restoreScale, restoreScale);
|
||||
this._dragActor.set_scale(this._snapBackScale, this._snapBackScale);
|
||||
this._dragActor.opacity = 0;
|
||||
|
||||
this._animationInProgress = true;
|
||||
@ -565,36 +514,12 @@ _Draggable.prototype = {
|
||||
this._dragComplete();
|
||||
},
|
||||
|
||||
// Actor is an actor we have entered or left during the drag; call
|
||||
// st_widget_sync_hover on all StWidget ancestors
|
||||
_syncHover: function(actor) {
|
||||
while (actor) {
|
||||
let parent = actor.get_parent();
|
||||
if (actor instanceof St.Widget)
|
||||
actor.sync_hover();
|
||||
|
||||
actor = parent;
|
||||
}
|
||||
},
|
||||
|
||||
_dragComplete: function() {
|
||||
if (!this._actorDestroyed)
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, false);
|
||||
|
||||
this._ungrabEvents();
|
||||
|
||||
if (this._firstLeaveActor) {
|
||||
this._syncHover(this._firstLeaveActor);
|
||||
this._firstLeaveActor = null;
|
||||
}
|
||||
|
||||
if (this._lastEnterActor) {
|
||||
this._syncHover(this._lastEnterActor);
|
||||
this._lastEnterActor = null;
|
||||
}
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, false);
|
||||
|
||||
this._dragActor = undefined;
|
||||
currentDraggable = null;
|
||||
this._ungrabEvents();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,12 +1,487 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const DocInfo = imports.misc.docInfo;
|
||||
const Params = imports.misc.params;
|
||||
const DND = imports.ui.dnd;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
const Main = imports.ui.main;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const MAX_DASH_DOCS = 50;
|
||||
const DASH_DOCS_ICON_SIZE = 16;
|
||||
|
||||
const DEFAULT_SPACING = 4;
|
||||
|
||||
/* This class represents a single display item containing information about a document.
|
||||
* We take the current number of seconds in the constructor to avoid looking up the current
|
||||
* time for every item when they are created in a batch.
|
||||
*
|
||||
* docInfo - DocInfo object containing information about the document
|
||||
* currentSeconds - current number of seconds since the epoch
|
||||
*/
|
||||
function DocDisplayItem(docInfo, currentSecs) {
|
||||
this._init(docInfo, currentSecs);
|
||||
}
|
||||
|
||||
DocDisplayItem.prototype = {
|
||||
__proto__: GenericDisplay.GenericDisplayItem.prototype,
|
||||
|
||||
_init : function(docInfo, currentSecs) {
|
||||
GenericDisplay.GenericDisplayItem.prototype._init.call(this);
|
||||
this._docInfo = docInfo;
|
||||
|
||||
this._setItemInfo(docInfo.name, '');
|
||||
|
||||
this._timeoutTime = -1;
|
||||
this._resetTimeDisplay(currentSecs);
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
|
||||
getUpdateTimeoutTime: function() {
|
||||
return this._timeoutTime;
|
||||
},
|
||||
|
||||
// Update any relative-time based displays for this item.
|
||||
redisplay: function(currentSecs) {
|
||||
this._resetTimeDisplay(currentSecs);
|
||||
},
|
||||
|
||||
//// Public method overrides ////
|
||||
|
||||
// Opens a document represented by this display item.
|
||||
launch : function() {
|
||||
this._docInfo.launch();
|
||||
},
|
||||
|
||||
//// Protected method overrides ////
|
||||
|
||||
// Returns an icon for the item.
|
||||
_createIcon : function() {
|
||||
return this._docInfo.createIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
|
||||
},
|
||||
|
||||
// Returns a preview icon for the item.
|
||||
_createPreviewIcon : function() {
|
||||
return this._docInfo.createIcon(GenericDisplay.PREVIEW_ICON_SIZE);
|
||||
},
|
||||
|
||||
// Creates and returns a large preview icon, but only if this._docInfo is an image file
|
||||
// and we were able to generate a pixbuf from it successfully.
|
||||
_createLargePreviewIcon : function() {
|
||||
if (this._docInfo.mimeType == null || this._docInfo.mimeType.indexOf('image/') != 0)
|
||||
return null;
|
||||
|
||||
try {
|
||||
return St.TextureCache.get_default().load_uri_sync(St.TextureCachePolicy.NONE,
|
||||
this._docInfo.uri, -1, -1);
|
||||
} catch (e) {
|
||||
// An exception will be raised when the image format isn't know
|
||||
/* FIXME: http://bugzilla.gnome.org/show_bug.cgi?id=591480: should
|
||||
* only ignore GDK_PIXBUF_ERROR_UNKNOWN_TYPE. */
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
//// Drag and Drop ////
|
||||
|
||||
shellWorkspaceLaunch: function() {
|
||||
this.launch();
|
||||
},
|
||||
|
||||
//// Private Methods ////
|
||||
|
||||
// Updates the last visited time displayed in the description text for the item.
|
||||
_resetTimeDisplay: function(currentSecs) {
|
||||
let lastSecs = this._docInfo.timestamp;
|
||||
let timeDelta = currentSecs - lastSecs;
|
||||
let [text, nextUpdate] = global.format_time_relative_pretty(timeDelta);
|
||||
this._timeoutTime = currentSecs + nextUpdate;
|
||||
this._setDescriptionText(text);
|
||||
}
|
||||
};
|
||||
|
||||
/* This class represents a display containing a collection of document items.
|
||||
* The documents are sorted by how recently they were last visited.
|
||||
*/
|
||||
function DocDisplay() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DocDisplay.prototype = {
|
||||
__proto__: GenericDisplay.GenericDisplay.prototype,
|
||||
|
||||
_init : function() {
|
||||
GenericDisplay.GenericDisplay.prototype._init.call(this);
|
||||
// We keep a single timeout callback for updating last visited times
|
||||
// for all the items in the display. This avoids creating individual
|
||||
// callbacks for each item in the display. So proper time updates
|
||||
// for individual items and item details depend on the item being
|
||||
// associated with one of the displays.
|
||||
this._updateTimeoutTargetTime = -1;
|
||||
this._updateTimeoutId = 0;
|
||||
|
||||
this._docManager = DocInfo.getDocManager();
|
||||
this._docsStale = true;
|
||||
this._docManager.connect('changed', Lang.bind(this, function(mgr, userData) {
|
||||
this._docsStale = true;
|
||||
// Changes in local recent files should not happen when we are in the Overview mode,
|
||||
// but redisplaying right away is cool when we use Zephyr.
|
||||
// Also, we might be displaying remote documents, like Google Docs, in the future
|
||||
// which might be edited by someone else.
|
||||
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
|
||||
}));
|
||||
|
||||
this.connect('destroy', Lang.bind(this, function (o) {
|
||||
if (this._updateTimeoutId > 0)
|
||||
Mainloop.source_remove(this._updateTimeoutId);
|
||||
}));
|
||||
},
|
||||
|
||||
//// Protected method overrides ////
|
||||
|
||||
// Gets the list of recent items from the recent items manager.
|
||||
_refreshCache : function() {
|
||||
if (!this._docsStale)
|
||||
return true;
|
||||
this._allItems = {};
|
||||
Lang.copyProperties(this._docManager.getInfosByUri(), this._allItems);
|
||||
this._docsStale = false;
|
||||
return false;
|
||||
},
|
||||
|
||||
// Sets the list of the displayed items based on how recently they were last visited.
|
||||
_setDefaultList : function() {
|
||||
// It seems to be an implementation detail of the Mozilla JavaScript that object
|
||||
// properties are returned during the iteration in the same order in which they were
|
||||
// defined, but it is not a guarantee according to this
|
||||
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Statements/for...in
|
||||
// While this._allItems associative array seems to always be ordered by last added,
|
||||
// as the results of this._recentManager.get_items() based on which it is constructed are,
|
||||
// we should do the sorting manually because we want the order to be based on last visited.
|
||||
//
|
||||
// This function is called each time the search string is set back to '' or we display
|
||||
// the Overview, so we are doing the sorting over the same items multiple times if the list
|
||||
// of recent items didn't change. We could store an additional array of doc ids and sort
|
||||
// them once when they are returned by this._recentManager.get_items() to avoid having to do
|
||||
// this sorting each time, but the sorting seems to be very fast anyway, so there is no need
|
||||
// to introduce an additional class variable.
|
||||
this._matchedItems = {};
|
||||
this._matchedItemKeys = [];
|
||||
let docIdsToRemove = [];
|
||||
for (docId in this._allItems) {
|
||||
this._matchedItems[docId] = 1;
|
||||
this._matchedItemKeys.push(docId);
|
||||
}
|
||||
|
||||
for (docId in docIdsToRemove) {
|
||||
delete this._allItems[docId];
|
||||
}
|
||||
|
||||
this._matchedItemKeys.sort(Lang.bind(this, this._compareItems));
|
||||
},
|
||||
|
||||
// Compares items associated with the item ids based on how recently the items
|
||||
// were last visited.
|
||||
// Returns an integer value indicating the result of the comparison.
|
||||
_compareItems : function(itemIdA, itemIdB) {
|
||||
let docA = this._allItems[itemIdA];
|
||||
let docB = this._allItems[itemIdB];
|
||||
|
||||
return docB.timestamp - docA.timestamp;
|
||||
},
|
||||
|
||||
// Checks if the item info can be a match for the search string by checking
|
||||
// the name of the document. Item info is expected to be GtkRecentInfo.
|
||||
// Returns a boolean flag indicating if itemInfo is a match.
|
||||
_isInfoMatching : function(itemInfo, search) {
|
||||
if (!itemInfo.exists())
|
||||
return false;
|
||||
|
||||
if (search == null || search == '')
|
||||
return true;
|
||||
|
||||
let name = itemInfo.name.toLowerCase();
|
||||
if (name.indexOf(search) >= 0)
|
||||
return true;
|
||||
// TODO: we can also check doc URIs, so that
|
||||
// if you search for a directory name, we display recent files from it
|
||||
return false;
|
||||
},
|
||||
|
||||
// Creates a DocDisplayItem based on itemInfo, which is expected to be a DocInfo object.
|
||||
_createDisplayItem: function(itemInfo) {
|
||||
let currentSecs = new Date().getTime() / 1000;
|
||||
let docDisplayItem = new DocDisplayItem(itemInfo, currentSecs);
|
||||
this._updateTimeoutCallback(docDisplayItem, currentSecs);
|
||||
return docDisplayItem;
|
||||
},
|
||||
|
||||
//// Private Methods ////
|
||||
|
||||
// A callback function that redisplays the items, updating their descriptions,
|
||||
// and sets up a new timeout callback.
|
||||
_docTimeout: function () {
|
||||
let currentSecs = new Date().getTime() / 1000;
|
||||
this._updateTimeoutId = 0;
|
||||
this._updateTimeoutTargetTime = -1;
|
||||
for (let docId in this._displayedItems) {
|
||||
let docDisplayItem = this._displayedItems[docId];
|
||||
docDisplayItem.redisplay(currentSecs);
|
||||
this._updateTimeoutCallback(docDisplayItem, currentSecs);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Updates the timeout callback if the timeout time for the docDisplayItem
|
||||
// is earlier than the target time for the current timeout callback.
|
||||
_updateTimeoutCallback: function (docDisplayItem, currentSecs) {
|
||||
let timeoutTime = docDisplayItem.getUpdateTimeoutTime();
|
||||
if (this._updateTimeoutTargetTime < 0 || timeoutTime < this._updateTimeoutTargetTime) {
|
||||
if (this._updateTimeoutId > 0)
|
||||
Mainloop.source_remove(this._updateTimeoutId);
|
||||
this._updateTimeoutId = Mainloop.timeout_add_seconds(timeoutTime - currentSecs, Lang.bind(this, this._docTimeout));
|
||||
this._updateTimeoutTargetTime = timeoutTime;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(DocDisplay.prototype);
|
||||
|
||||
function DashDocDisplayItem(docInfo) {
|
||||
this._init(docInfo);
|
||||
}
|
||||
|
||||
DashDocDisplayItem.prototype = {
|
||||
_init: function(docInfo) {
|
||||
this._info = docInfo;
|
||||
this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
|
||||
|
||||
this.actor = new St.Clickable({ style_class: 'recent-docs-item',
|
||||
reactive: true,
|
||||
x_align: St.Align.START });
|
||||
|
||||
let box = new St.BoxLayout({ style_class: 'recent-docs-item-box' });
|
||||
this.actor.set_child(box);
|
||||
|
||||
box.add(this._icon);
|
||||
|
||||
let text = new St.Label({ text: docInfo.name });
|
||||
box.add(text);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, function () {
|
||||
docInfo.launch();
|
||||
Main.overview.hide();
|
||||
}));
|
||||
|
||||
this.actor._delegate = this;
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.beginItemDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
},
|
||||
|
||||
getUri: function() {
|
||||
return this._info.uri;
|
||||
},
|
||||
|
||||
getDragActorSource: function() {
|
||||
return this._icon;
|
||||
},
|
||||
|
||||
getDragActor: function(stageX, stageY) {
|
||||
this.dragActor = this._info.createIcon(DASH_DOCS_ICON_SIZE);
|
||||
return this.dragActor;
|
||||
},
|
||||
|
||||
//// Drag and drop functions ////
|
||||
|
||||
shellWorkspaceLaunch: function () {
|
||||
this._info.launch();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class used to display two column recent documents in the dash
|
||||
*/
|
||||
function DashDocDisplay() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DashDocDisplay.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new Shell.GenericContainer();
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
||||
|
||||
this._actorsByUri = {};
|
||||
|
||||
this._docManager = DocInfo.getDocManager();
|
||||
this._docManager.connect('changed', Lang.bind(this, this._onDocsChanged));
|
||||
this._pendingDocsChange = true;
|
||||
this._checkDocExistence = false;
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
let children = actor.get_children();
|
||||
|
||||
// We use two columns maximum. Just take the min and natural size of the
|
||||
// first two items, even though strictly speaking it's not correct; we'd
|
||||
// need to calculate how many items we could fit for the height, then
|
||||
// take the biggest preferred width for each column.
|
||||
// In practice the dash gets a fixed width anyways.
|
||||
|
||||
// If we have one child, add its minimum and natural size
|
||||
if (children.length > 0) {
|
||||
let [minSize, naturalSize] = children[0].get_preferred_width(forHeight);
|
||||
alloc.min_size += minSize;
|
||||
alloc.natural_size += naturalSize;
|
||||
}
|
||||
// If we have two, add its size, plus DEFAULT_SPACING
|
||||
if (children.length > 1) {
|
||||
let [minSize, naturalSize] = children[1].get_preferred_width(forHeight);
|
||||
alloc.min_size += DEFAULT_SPACING + minSize;
|
||||
alloc.natural_size += DEFAULT_SPACING + naturalSize;
|
||||
}
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
let children = actor.get_children();
|
||||
|
||||
// The width of an item is our allocated width, minus spacing, divided in half.
|
||||
this._itemWidth = Math.floor((forWidth - DEFAULT_SPACING) / 2);
|
||||
|
||||
let maxNatural = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let child = children[i];
|
||||
let [minSize, naturalSize] = child.get_preferred_height(this._itemWidth);
|
||||
maxNatural = Math.max(maxNatural, naturalSize);
|
||||
}
|
||||
|
||||
this._itemHeight = maxNatural;
|
||||
|
||||
let firstColumnChildren = Math.ceil(children.length / 2);
|
||||
alloc.natural_size = (firstColumnChildren * maxNatural +
|
||||
(firstColumnChildren - 1) * DEFAULT_SPACING);
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
let width = box.x2 - box.x1;
|
||||
let height = box.y2 - box.y1;
|
||||
|
||||
// Make sure this._itemWidth/Height have been computed, even
|
||||
// if the parent actor didn't check our size before allocating.
|
||||
// (Not clear if that is required or not as a Clutter
|
||||
// invariant; this is safe and cheap because of caching.)
|
||||
actor.get_preferred_height(width);
|
||||
|
||||
let children = actor.get_children();
|
||||
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
let columnIndex = 0;
|
||||
let i = 0;
|
||||
// Loop over the children, going vertically down first. When we run
|
||||
// out of vertical space (our y variable is bigger than box.y2), switch
|
||||
// to the second column.
|
||||
while (i < children.length) {
|
||||
let child = children[i];
|
||||
|
||||
if (y + this._itemHeight > box.y2) {
|
||||
// Is this the second column, or we're in
|
||||
// the first column and can't even fit one
|
||||
// item? In that case, break.
|
||||
if (columnIndex == 1 || i == 0) {
|
||||
break;
|
||||
}
|
||||
// Set x to the halfway point.
|
||||
columnIndex += 1;
|
||||
x = x + this._itemWidth + DEFAULT_SPACING;
|
||||
// And y is back to the top.
|
||||
y = 0;
|
||||
// Retry this same item, now that we're in the second column.
|
||||
// By looping back to the top here, we re-test the size
|
||||
// again for the second column.
|
||||
continue;
|
||||
}
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.x1 = x;
|
||||
childBox.y1 = y;
|
||||
childBox.x2 = childBox.x1 + this._itemWidth;
|
||||
childBox.y2 = y + this._itemHeight;
|
||||
|
||||
y = childBox.y2 + DEFAULT_SPACING;
|
||||
|
||||
child.allocate(childBox, flags);
|
||||
this.actor.set_skip_paint(child, false);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (this._checkDocExistence) {
|
||||
// Now we know how many docs we are displaying, queue a check to see if any of them
|
||||
// have been deleted. If they are deleted, then we'll get a 'changed' signal; since
|
||||
// we'll now be displaying items we weren't previously, we'll check again to see
|
||||
// if they were deleted, and so forth and so on.
|
||||
// TODO: We should change this to ask for as many as we can fit in the given space:
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=603522#c23
|
||||
this._docManager.queueExistenceCheck(i);
|
||||
this._checkDocExistence = false;
|
||||
}
|
||||
|
||||
for (; i < children.length; i++)
|
||||
this.actor.set_skip_paint(children[i], true);
|
||||
},
|
||||
|
||||
_onDocsChanged: function() {
|
||||
this._checkDocExistence = true;
|
||||
Main.queueDeferredWork(this._workId);
|
||||
},
|
||||
|
||||
_redisplay: function() {
|
||||
// Should be kept alive by the _actorsByUri
|
||||
this.actor.remove_all();
|
||||
let docs = this._docManager.getTimestampOrderedInfos();
|
||||
for (let i = 0; i < docs.length && i < MAX_DASH_DOCS; i++) {
|
||||
let doc = docs[i];
|
||||
let display = this._actorsByUri[doc.uri];
|
||||
if (display) {
|
||||
this.actor.add_actor(display.actor);
|
||||
} else {
|
||||
let display = new DashDocDisplayItem(doc);
|
||||
this.actor.add_actor(display.actor);
|
||||
this._actorsByUri[doc.uri] = display;
|
||||
}
|
||||
}
|
||||
// Any unparented actors must have been deleted
|
||||
for (let uri in this._actorsByUri) {
|
||||
let display = this._actorsByUri[uri];
|
||||
if (display.actor.get_parent() == null) {
|
||||
display.actor.destroy();
|
||||
delete this._actorsByUri[uri];
|
||||
}
|
||||
}
|
||||
this.emit('changed');
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(DashDocDisplay.prototype);
|
||||
|
||||
function DocSearchProvider() {
|
||||
this._init();
|
||||
@ -26,18 +501,12 @@ DocSearchProvider.prototype = {
|
||||
return null;
|
||||
return { 'id': resultId,
|
||||
'name': docInfo.name,
|
||||
'createIcon': function(size) {
|
||||
return docInfo.createIcon(size);
|
||||
}
|
||||
};
|
||||
'icon': docInfo.createIcon(Search.RESULT_ICON_SIZE)};
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
|
||||
activateResult: function(id) {
|
||||
let docInfo = this._docManager.lookupByUri(id);
|
||||
docInfo.launch(params.workspace ? params.workspace.index() : -1);
|
||||
docInfo.launch();
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
@ -46,5 +515,9 @@ DocSearchProvider.prototype = {
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._docManager.subsearch(previousResults, terms);
|
||||
},
|
||||
|
||||
expandSearch: function(terms) {
|
||||
log('TODO expand docs search');
|
||||
}
|
||||
};
|
||||
|
@ -1,536 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
*
|
||||
* Copyright 2010 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, 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.
|
||||
*/
|
||||
|
||||
const DBus = imports.dbus;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Pango = imports.gi.Pango;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
let _endSessionDialog = null;
|
||||
|
||||
const _ITEM_ICON_SIZE = 48;
|
||||
const _DIALOG_ICON_SIZE = 32;
|
||||
|
||||
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
|
||||
|
||||
const EndSessionDialogIface = {
|
||||
name: 'org.gnome.SessionManager.EndSessionDialog',
|
||||
methods: [{ name: 'Open',
|
||||
inSignature: 'uuuao',
|
||||
outSignature: ''
|
||||
}
|
||||
],
|
||||
signals: [{ name: 'Canceled',
|
||||
inSignature: '',
|
||||
}],
|
||||
properties: []
|
||||
};
|
||||
|
||||
const logoutDialogContent = {
|
||||
subjectWithUser: _("Log Out %s"),
|
||||
subject: _("Log Out"),
|
||||
inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
|
||||
uninhibitedDescriptionWithUser: _("%s will be logged out automatically in %d seconds."),
|
||||
uninhibitedDescription: _("You will be logged out automatically in %d seconds."),
|
||||
endDescription: _("Logging out of the system."),
|
||||
confirmButtons: [{ signal: 'ConfirmedLogout',
|
||||
label: _("Log Out") }],
|
||||
iconStyleClass: 'end-session-dialog-logout-icon'
|
||||
};
|
||||
|
||||
const shutdownDialogContent = {
|
||||
subject: _("Power Off"),
|
||||
inhibitedDescription: _("Click Power Off to quit these applications and power off the system."),
|
||||
uninhibitedDescription: _("The system will power off automatically in %d seconds."),
|
||||
endDescription: _("Powering off the system."),
|
||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||
label: _("Restart") },
|
||||
{ signal: 'ConfirmedShutdown',
|
||||
label: _("Power Off") }],
|
||||
iconName: 'system-shutdown',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||
};
|
||||
|
||||
const restartDialogContent = {
|
||||
subject: _("Restart"),
|
||||
inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
|
||||
uninhibitedDescription: _("The system will restart automatically in %d seconds."),
|
||||
endDescription: _("Restarting the system."),
|
||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||
label: _("Restart") }],
|
||||
iconName: 'system-shutdown',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||
};
|
||||
|
||||
const DialogContent = {
|
||||
0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent,
|
||||
1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent,
|
||||
2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent
|
||||
};
|
||||
|
||||
function findAppFromInhibitor(inhibitor) {
|
||||
let desktopFile = inhibitor.app_id;
|
||||
|
||||
if (!GLib.str_has_suffix(desktopFile, '.desktop'))
|
||||
desktopFile += '.desktop';
|
||||
|
||||
let candidateDesktopFiles = [];
|
||||
|
||||
candidateDesktopFiles.push(desktopFile);
|
||||
candidateDesktopFiles.push('gnome-' + desktopFile);
|
||||
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
let app = null;
|
||||
for (let i = 0; i < candidateDesktopFiles.length; i++) {
|
||||
try {
|
||||
app = appSystem.get_app(candidateDesktopFiles[i]);
|
||||
|
||||
if (app)
|
||||
break;
|
||||
} catch(e) {
|
||||
// ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
function ListItem(app, reason) {
|
||||
this._init(app, reason);
|
||||
}
|
||||
|
||||
ListItem.prototype = {
|
||||
_init: function(app, reason) {
|
||||
this._app = app;
|
||||
this._reason = reason;
|
||||
|
||||
if (this._reason == null)
|
||||
this._reason = '';
|
||||
|
||||
let layout = new St.BoxLayout({ vertical: false});
|
||||
|
||||
this.actor = new St.Button({ style_class: 'end-session-dialog-app-list-item',
|
||||
can_focus: true,
|
||||
child: layout,
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
|
||||
this._icon = this._app.create_icon_texture(_ITEM_ICON_SIZE);
|
||||
|
||||
let iconBin = new St.Bin({ style_class: 'end-session-dialog-app-list-item-icon',
|
||||
child: this._icon });
|
||||
layout.add(iconBin);
|
||||
|
||||
let textLayout = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item-text-box',
|
||||
vertical: true });
|
||||
layout.add(textLayout);
|
||||
|
||||
this._nameLabel = new St.Label({ text: this._app.get_name(),
|
||||
style_class: 'end-session-dialog-app-list-item-name' });
|
||||
textLayout.add(this._nameLabel,
|
||||
{ expand: false,
|
||||
x_fill: true });
|
||||
|
||||
this._descriptionLabel = new St.Label({ text: this._reason,
|
||||
style_class: 'end-session-dialog-app-list-item-description' });
|
||||
textLayout.add(this._descriptionLabel,
|
||||
{ expand: true,
|
||||
x_fill: true });
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
this.emit('activate');
|
||||
this._app.activate(-1);
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ListItem.prototype);
|
||||
|
||||
// The logout timer only shows updates every 10 seconds
|
||||
// until the last 10 seconds, then it shows updates every
|
||||
// second. This function takes a given time and returns
|
||||
// what we should show to the user for that time.
|
||||
function _roundSecondsToInterval(totalSeconds, secondsLeft, interval) {
|
||||
let time;
|
||||
|
||||
time = Math.ceil(secondsLeft);
|
||||
|
||||
// Final count down is in decrements of 1
|
||||
if (time <= interval)
|
||||
return time;
|
||||
|
||||
// Round up higher than last displayable time interval
|
||||
time += interval - 1;
|
||||
|
||||
// Then round down to that time interval
|
||||
if (time > totalSeconds)
|
||||
time = Math.ceil(totalSeconds);
|
||||
else
|
||||
time -= time % interval;
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
function _setLabelText(label, text) {
|
||||
if (text) {
|
||||
label.set_text(text);
|
||||
label.show();
|
||||
} else {
|
||||
label.set_text('');
|
||||
label.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function EndSessionDialog() {
|
||||
if (_endSessionDialog == null) {
|
||||
this._init();
|
||||
DBus.session.exportObject('/org/gnome/SessionManager/EndSessionDialog',
|
||||
this);
|
||||
_endSessionDialog = this;
|
||||
}
|
||||
|
||||
return _endSessionDialog;
|
||||
}
|
||||
|
||||
function init() {
|
||||
// This always returns the same singleton object
|
||||
// By instantiating it initially, we register the
|
||||
// bus object, etc.
|
||||
let dialog = new EndSessionDialog();
|
||||
}
|
||||
|
||||
EndSessionDialog.prototype = {
|
||||
__proto__: ModalDialog.ModalDialog.prototype,
|
||||
|
||||
_init: function() {
|
||||
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' });
|
||||
|
||||
this._user = Gdm.UserManager.ref_default().get_user(GLib.get_user_name());
|
||||
|
||||
this._secondsLeft = 0;
|
||||
this._totalSecondsToStayOpen = 0;
|
||||
this._inhibitors = [];
|
||||
|
||||
this.connect('destroy',
|
||||
Lang.bind(this, this._onDestroy));
|
||||
this.connect('opened',
|
||||
Lang.bind(this, this._onOpened));
|
||||
|
||||
this._userLoadedId = this._user.connect('notify::is_loaded',
|
||||
Lang.bind(this, this._updateContent));
|
||||
|
||||
this._userChangedId = this._user.connect('changed',
|
||||
Lang.bind(this, this._updateContent));
|
||||
|
||||
let mainContentLayout = new St.BoxLayout({ vertical: false });
|
||||
this.contentLayout.add(mainContentLayout,
|
||||
{ x_fill: true,
|
||||
y_fill: false });
|
||||
|
||||
this._iconBin = new St.Bin();
|
||||
mainContentLayout.add(this._iconBin,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let messageLayout = new St.BoxLayout({ vertical: true });
|
||||
mainContentLayout.add(messageLayout,
|
||||
{ y_align: St.Align.START });
|
||||
|
||||
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
|
||||
|
||||
messageLayout.add(this._subjectLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
|
||||
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._descriptionLabel.clutter_text.line_wrap = true;
|
||||
|
||||
messageLayout.add(this._descriptionLabel,
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list'});
|
||||
scrollView.set_policy(Gtk.PolicyType.NEVER,
|
||||
Gtk.PolicyType.AUTOMATIC);
|
||||
this.contentLayout.add(scrollView,
|
||||
{ x_fill: true,
|
||||
y_fill: true });
|
||||
scrollView.hide();
|
||||
|
||||
this._applicationList = new St.BoxLayout({ vertical: true });
|
||||
scrollView.add_actor(this._applicationList,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
this._applicationList.connect('actor-added',
|
||||
Lang.bind(this, function() {
|
||||
if (this._applicationList.get_children().length == 1)
|
||||
scrollView.show();
|
||||
}));
|
||||
|
||||
this._applicationList.connect('actor-removed',
|
||||
Lang.bind(this, function() {
|
||||
if (this._applicationList.get_children().length == 0)
|
||||
scrollView.hide();
|
||||
}));
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._user.disconnect(this._userLoadedId);
|
||||
this._user.disconnect(this._userChangedId);
|
||||
},
|
||||
|
||||
_setIconFromFile: function(iconFile, styleClass) {
|
||||
if (styleClass)
|
||||
this._iconBin.set_style_class_name(styleClass);
|
||||
this._iconBin.set_style(null);
|
||||
|
||||
this._iconBin.child = null;
|
||||
if (iconFile) {
|
||||
this._iconBin.show();
|
||||
this._iconBin.set_style('background-image: url("' + iconFile + '");');
|
||||
} else {
|
||||
this._iconBin.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_setIconFromName: function(iconName, styleClass) {
|
||||
if (styleClass)
|
||||
this._iconBin.set_style_class_name(styleClass);
|
||||
this._iconBin.set_style(null);
|
||||
|
||||
if (iconName != null) {
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
|
||||
iconName,
|
||||
St.IconType.SYMBOLIC,
|
||||
_DIALOG_ICON_SIZE);
|
||||
|
||||
this._iconBin.child = icon;
|
||||
this._iconBin.show();
|
||||
} else {
|
||||
this._iconBin.child = null;
|
||||
this._iconBin.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_updateContent: function() {
|
||||
if (this.state != ModalDialog.State.OPENING &&
|
||||
this.state != ModalDialog.State.OPENED)
|
||||
return;
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
|
||||
let subject = dialogContent.subject;
|
||||
let description;
|
||||
|
||||
if (this._user.is_loaded && !dialogContent.iconName) {
|
||||
let iconFile = this._user.get_icon_file();
|
||||
|
||||
this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
|
||||
} else if (dialogContent.iconName) {
|
||||
this._setIconFromName(dialogContent.iconName,
|
||||
dialogContent.iconStyleClass);
|
||||
}
|
||||
|
||||
if (this._inhibitors.length > 0) {
|
||||
this._stopTimer();
|
||||
description = dialogContent.inhibitedDescription;
|
||||
} else if (this._secondsLeft > 0 && this._inhibitors.length == 0) {
|
||||
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
|
||||
this._secondsLeft,
|
||||
10);
|
||||
|
||||
if (this._user.is_loaded) {
|
||||
let realName = this._user.get_real_name();
|
||||
|
||||
if (realName != null) {
|
||||
if (dialogContent.subjectWithUser)
|
||||
subject = dialogContent.subjectWithUser.format(realName);
|
||||
|
||||
if (dialogContent.uninhibitedDescriptionWithUser)
|
||||
description = dialogContent.uninhibitedDescriptionWithUser.format(realName, displayTime);
|
||||
else
|
||||
description = dialogContent.uninhibitedDescription.format(displayTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (!description)
|
||||
description = dialogContent.uninhibitedDescription.format(displayTime);
|
||||
} else {
|
||||
description = dialogContent.endDescription;
|
||||
}
|
||||
|
||||
_setLabelText(this._subjectLabel, subject);
|
||||
_setLabelText(this._descriptionLabel, description);
|
||||
},
|
||||
|
||||
_updateButtons: function() {
|
||||
if (this.state != ModalDialog.State.OPENING &&
|
||||
this.state != ModalDialog.State.OPENED)
|
||||
return;
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
let buttons = [{ action: Lang.bind(this, this.cancel),
|
||||
label: _("Cancel"),
|
||||
key: Clutter.Escape }];
|
||||
|
||||
for (let i = 0; i < dialogContent.confirmButtons.length; i++) {
|
||||
let signal = dialogContent.confirmButtons[i].signal;
|
||||
let label = dialogContent.confirmButtons[i].label;
|
||||
buttons.push({ action: Lang.bind(this, function() {
|
||||
this._confirm(signal);
|
||||
}),
|
||||
label: label });
|
||||
}
|
||||
|
||||
this.setButtons(buttons);
|
||||
},
|
||||
|
||||
close: function() {
|
||||
ModalDialog.ModalDialog.prototype.close.call(this);
|
||||
DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
|
||||
'org.gnome.SessionManager.EndSessionDialog',
|
||||
'Closed', '', []);
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this._stopTimer();
|
||||
DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
|
||||
'org.gnome.SessionManager.EndSessionDialog',
|
||||
'Canceled', '', []);
|
||||
this.close(global.get_current_time());
|
||||
},
|
||||
|
||||
_confirm: function(signal) {
|
||||
this._fadeOutDialog();
|
||||
this._stopTimer();
|
||||
DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
|
||||
'org.gnome.SessionManager.EndSessionDialog',
|
||||
signal, '', []);
|
||||
},
|
||||
|
||||
_onOpened: function() {
|
||||
if (this._inhibitors.length == 0)
|
||||
this._startTimer();
|
||||
},
|
||||
|
||||
_startTimer: function() {
|
||||
this._secondsLeft = this._totalSecondsToStayOpen;
|
||||
Tweener.addTween(this,
|
||||
{ _secondsLeft: 0,
|
||||
time: this._secondsLeft,
|
||||
transition: 'linear',
|
||||
onUpdate: Lang.bind(this, this._updateContent),
|
||||
onComplete: Lang.bind(this, function() {
|
||||
let dialogContent = DialogContent[this._type];
|
||||
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
||||
this._confirm(button.signal);
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
_stopTimer: function() {
|
||||
Tweener.removeTweens(this);
|
||||
this._secondsLeft = 0;
|
||||
},
|
||||
|
||||
_onInhibitorLoaded: function(inhibitor) {
|
||||
if (this._inhibitors.indexOf(inhibitor) < 0) {
|
||||
// Stale inhibitor
|
||||
return;
|
||||
}
|
||||
|
||||
let app = findAppFromInhibitor(inhibitor);
|
||||
|
||||
if (app) {
|
||||
let item = new ListItem(app, inhibitor.reason);
|
||||
item.connect('activate',
|
||||
Lang.bind(this, function() {
|
||||
this.close(global.get_current_time());
|
||||
}));
|
||||
this._applicationList.add(item.actor, { x_fill: true });
|
||||
this._stopTimer();
|
||||
} else {
|
||||
// inhibiting app is a service, not an application
|
||||
this._inhibitors.splice(this._inhibitors.indexOf(inhibitor), 1);
|
||||
}
|
||||
|
||||
this._updateContent();
|
||||
},
|
||||
|
||||
OpenAsync: function(type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths, callback) {
|
||||
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
|
||||
this._inhibitors = [];
|
||||
this._applicationList.destroy_children();
|
||||
this._type = type;
|
||||
|
||||
if (!(this._type in DialogContent))
|
||||
throw new DBus.DBusError('org.gnome.Shell.ModalDialog.TypeError',
|
||||
"Unknown dialog type requested");
|
||||
|
||||
for (let i = 0; i < inhibitorObjectPaths.length; i++) {
|
||||
let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i]);
|
||||
|
||||
inhibitor.connect('is-loaded',
|
||||
Lang.bind(this, function() {
|
||||
this._onInhibitorLoaded(inhibitor);
|
||||
}));
|
||||
this._inhibitors.push(inhibitor);
|
||||
}
|
||||
|
||||
if (!this.open(timestamp))
|
||||
throw new DBus.DBusError('org.gnome.Shell.ModalDialog.GrabError',
|
||||
"Cannot grab pointer and keyboard");
|
||||
|
||||
this._updateButtons();
|
||||
this._updateContent();
|
||||
|
||||
let signalId = this.connect('opened',
|
||||
Lang.bind(this, function() {
|
||||
callback();
|
||||
this.disconnect(signalId);
|
||||
}));
|
||||
}
|
||||
};
|
||||
DBus.conformExport(EndSessionDialog.prototype, EndSessionDialogIface);
|
@ -4,7 +4,7 @@ const Clutter = imports.gi.Clutter;;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Gettext_gtk30 = imports.gettext.domain('gtk30');
|
||||
const Gettext_gtk20 = imports.gettext.domain('gtk20');
|
||||
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
@ -17,7 +17,7 @@ function _patchContainerClass(containerClass) {
|
||||
// This one is a straightforward mapping of the C method
|
||||
containerClass.prototype.child_set = function(actor, props) {
|
||||
let meta = this.get_child_meta(actor);
|
||||
for (let prop in props)
|
||||
for (prop in props)
|
||||
meta[prop] = props[prop];
|
||||
};
|
||||
|
||||
@ -64,13 +64,8 @@ function init() {
|
||||
Tweener.init();
|
||||
String.prototype.format = Format.format;
|
||||
|
||||
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783
|
||||
Date.prototype.toLocaleFormat = function(format) {
|
||||
return Shell.util_format_date(format, this.getTime());
|
||||
};
|
||||
|
||||
// Set the default direction for St widgets (this needs to be done before any use of St)
|
||||
if (Gettext_gtk30.gettext('default:LTR') == 'default:RTL') {
|
||||
if (Gettext_gtk20.gettext('default:LTR') == 'default:RTL') {
|
||||
St.Widget.set_default_direction(St.TextDirection.RTL);
|
||||
}
|
||||
|
||||
@ -93,6 +88,8 @@ function init() {
|
||||
|
||||
_blockMethod('Clutter.Event.get_state', 'Shell.get_event_state',
|
||||
'gjs\'s handling of Clutter.ModifierType is broken. See bug 597292.');
|
||||
_blockMethod('Gdk.Display.get_device_state', 'global.get_pointer',
|
||||
'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.');
|
||||
_blockMethod('Gdk.Window.get_device_position', 'global.get_pointer',
|
||||
'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.');
|
||||
|
||||
|
@ -4,8 +4,6 @@ const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
|
||||
const ExtensionState = {
|
||||
ENABLED: 1,
|
||||
DISABLED: 2,
|
||||
@ -27,36 +25,6 @@ var disabledExtensions;
|
||||
// GFile for user extensions
|
||||
var userExtensionsDir = null;
|
||||
|
||||
/**
|
||||
* versionCheck:
|
||||
* @required: an array of versions we're compatible with
|
||||
* @current: the version we have
|
||||
*
|
||||
* Check if a component is compatible for an extension.
|
||||
* @required is an array, and at least one version must match.
|
||||
* @current must be in the format <major>.<minor>.<point>.<micro>
|
||||
* <micro> is always ignored
|
||||
* <point> is ignored if <minor> is even (so you can target the
|
||||
* whole stable release)
|
||||
* <minor> and <major> must match
|
||||
* Each target version must be at least <major> and <minor>
|
||||
*/
|
||||
function versionCheck(required, current) {
|
||||
let currentArray = current.split('.');
|
||||
let major = currentArray[0];
|
||||
let minor = currentArray[1];
|
||||
let point = currentArray[2];
|
||||
for (let i = 0; i < required.length; i++) {
|
||||
let requiredArray = required[i].split('.');
|
||||
if (requiredArray[0] == major &&
|
||||
requiredArray[1] == minor &&
|
||||
(requiredArray[2] == point ||
|
||||
(requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function loadExtension(dir, enabled, type) {
|
||||
let info;
|
||||
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
|
||||
@ -75,8 +43,8 @@ function loadExtension(dir, enabled, type) {
|
||||
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
|
||||
return;
|
||||
}
|
||||
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
|
||||
for (let i = 0; i < requiredProperties.length; i++) {
|
||||
let requiredProperties = ['uuid', 'name', 'description'];
|
||||
for (let i = 0; i < requiredProperties; i++) {
|
||||
let prop = requiredProperties[i];
|
||||
if (!meta[prop]) {
|
||||
global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
|
||||
@ -100,12 +68,6 @@ function loadExtension(dir, enabled, type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
|
||||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
|
||||
global.logError(baseErrorString + 'extension is not compatible with current GNOME Shell and/or GJS version');
|
||||
return;
|
||||
}
|
||||
|
||||
extensionMeta[meta.uuid] = meta;
|
||||
extensionMeta[meta.uuid].type = type;
|
||||
extensionMeta[meta.uuid].path = dir.get_path();
|
||||
@ -150,7 +112,7 @@ function loadExtension(dir, enabled, type) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
extensionModule.main(meta);
|
||||
extensionModule.main();
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
@ -174,15 +136,8 @@ function init() {
|
||||
}
|
||||
|
||||
function _loadExtensionsIn(dir, type) {
|
||||
let fileEnum;
|
||||
let fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||
let file, info;
|
||||
try {
|
||||
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch (e) {
|
||||
global.logError('' + e);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((info = fileEnum.next_file(null)) != null) {
|
||||
let fileType = info.get_file_type();
|
||||
if (fileType != Gio.FileType.DIRECTORY)
|
||||
|
693
js/ui/genericDisplay.js
Normal file
@ -0,0 +1,693 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const RedisplayFlags = { NONE: 0,
|
||||
FULL: 1 << 1,
|
||||
SUBSEARCH: 1 << 2,
|
||||
IMMEDIATE: 1 << 3 };
|
||||
|
||||
// Used by subclasses
|
||||
const ITEM_DISPLAY_ICON_SIZE = 48;
|
||||
const PREVIEW_ICON_SIZE = 96;
|
||||
|
||||
/* This is a virtual class that represents a single display item containing
|
||||
* a name, a description, and an icon. It allows selecting an item and represents
|
||||
* it by highlighting it with a different background color than the default.
|
||||
*/
|
||||
function GenericDisplayItem() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
GenericDisplayItem.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ style_class: 'generic-display-item',
|
||||
reactive: true });
|
||||
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('button-release-event',
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
// Activates the item by launching it
|
||||
this.emit('activate');
|
||||
return true;
|
||||
}));
|
||||
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.beginItemDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
|
||||
this._iconBin = new St.Bin();
|
||||
this.actor.add(this._iconBin);
|
||||
|
||||
this._infoText = new St.BoxLayout({ style_class: 'generic-display-item-text',
|
||||
vertical: true });
|
||||
this.actor.add(this._infoText, { expand: true, y_fill: false });
|
||||
|
||||
this._name = null;
|
||||
this._description = null;
|
||||
this._icon = null;
|
||||
|
||||
this._initialLoadComplete = false;
|
||||
|
||||
// An array of details description actors that we create over time for the item.
|
||||
// It is used for updating the description text inside the details actor when
|
||||
// the description text for the item is updated.
|
||||
this._detailsDescriptions = [];
|
||||
},
|
||||
|
||||
//// Draggable object interface ////
|
||||
|
||||
// Returns a cloned texture of the item's icon to represent the item as it
|
||||
// is being dragged.
|
||||
getDragActor: function(stageX, stageY) {
|
||||
return this._createIcon();
|
||||
},
|
||||
|
||||
// Returns the item icon, a separate copy of which is used to
|
||||
// represent the item as it is being dragged. This is used to
|
||||
// determine a snap-back location for the drag icon if it does
|
||||
// not get accepted by any drop target.
|
||||
getDragActorSource: function() {
|
||||
return this._icon;
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
|
||||
// Highlights the item by setting a different background color than the default
|
||||
// if isSelected is true, removes the highlighting otherwise.
|
||||
markSelected: function(isSelected) {
|
||||
if (isSelected)
|
||||
this.actor.add_style_pseudo_class('selected');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('selected');
|
||||
},
|
||||
|
||||
/*
|
||||
* Returns an actor containing item details. In the future details can have more information than what
|
||||
* the preview pop-up has and be item-type specific.
|
||||
*/
|
||||
createDetailsActor: function() {
|
||||
|
||||
let details = new St.BoxLayout({ style_class: 'generic-display-container',
|
||||
vertical: true });
|
||||
|
||||
let mainDetails = new St.BoxLayout({ style_class: 'generic-display-container' });
|
||||
|
||||
// Inner box with name and description
|
||||
let textDetails = new St.BoxLayout({ style_class: 'generic-display-details',
|
||||
vertical: true });
|
||||
let detailsName = new St.Label({ style_class: 'generic-display-details-name',
|
||||
text: this._name.text });
|
||||
textDetails.add(detailsName);
|
||||
|
||||
let detailsDescription = new St.Label({ text: this._description.text });
|
||||
textDetails.add(detailsDescription);
|
||||
|
||||
this._detailsDescriptions.push(detailsDescription);
|
||||
|
||||
mainDetails.add(textDetails, { expand: true });
|
||||
|
||||
let previewIcon = this._createPreviewIcon();
|
||||
let largePreviewIcon = this._createLargePreviewIcon();
|
||||
|
||||
if (previewIcon != null && largePreviewIcon == null) {
|
||||
mainDetails.insert_actor(previewIcon, 0);
|
||||
}
|
||||
|
||||
details.add(mainDetails);
|
||||
|
||||
if (largePreviewIcon != null) {
|
||||
details.add(largePreviewIcon);
|
||||
}
|
||||
|
||||
return details;
|
||||
},
|
||||
|
||||
// Destroys the item.
|
||||
destroy: function() {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
//// Pure virtual public methods ////
|
||||
|
||||
// Performes an action associated with launching this item, such as opening a file or an application.
|
||||
launch: function() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
//// Protected methods ////
|
||||
|
||||
/*
|
||||
* Creates the graphical elements for the item based on the item information.
|
||||
*
|
||||
* nameText - name of the item
|
||||
* descriptionText - short description of the item
|
||||
*/
|
||||
_setItemInfo: function(nameText, descriptionText) {
|
||||
if (this._name != null) {
|
||||
// this also removes this._name from the parent container,
|
||||
// so we don't need to call this.actor.remove_actor(this._name) directly
|
||||
this._name.destroy();
|
||||
this._name = null;
|
||||
}
|
||||
if (this._description != null) {
|
||||
this._description.destroy();
|
||||
this._description = null;
|
||||
}
|
||||
if (this._icon != null) {
|
||||
// though we get the icon from elsewhere, we assume its ownership here,
|
||||
// and therefore should be responsible for distroying it
|
||||
this._icon.destroy();
|
||||
this._icon = null;
|
||||
}
|
||||
|
||||
this._icon = this._createIcon();
|
||||
this._iconBin.set_child(this._icon);
|
||||
|
||||
this._name = new St.Label({ style_class: 'generic-display-item-name',
|
||||
text: nameText });
|
||||
this._infoText.add(this._name);
|
||||
|
||||
this._description = new St.Label({ style_class: 'generic-display-item-description',
|
||||
text: descriptionText ? descriptionText : '' });
|
||||
this._infoText.add(this._description);
|
||||
},
|
||||
|
||||
// Sets the description text for the item, including the description text
|
||||
// in the details actors that have been created for the item.
|
||||
_setDescriptionText: function(text) {
|
||||
this._description.text = text;
|
||||
for (let i = 0; i < this._detailsDescriptions.length; i++) {
|
||||
let detailsDescription = this._detailsDescriptions[i];
|
||||
if (detailsDescription != null) {
|
||||
detailsDescription.text = text;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//// Virtual protected methods ////
|
||||
|
||||
// Creates and returns a large preview icon, but only if we have a detailed image.
|
||||
_createLargePreviewIcon : function() {
|
||||
return null;
|
||||
},
|
||||
|
||||
//// Pure virtual protected methods ////
|
||||
|
||||
// Returns an icon for the item.
|
||||
_createIcon: function() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
// Returns a preview icon for the item.
|
||||
_createPreviewIcon: function() {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
//// Private methods ////
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(GenericDisplayItem.prototype);
|
||||
|
||||
/* This is a virtual class that represents a display containing a collection of items
|
||||
* that can be filtered with a search string.
|
||||
*/
|
||||
function GenericDisplay() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
GenericDisplay.prototype = {
|
||||
_init : function() {
|
||||
this._search = '';
|
||||
this._expanded = false;
|
||||
|
||||
this.actor = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
vshadows: true });
|
||||
this._list = new St.BoxLayout({ style_class: 'generic-display-container',
|
||||
vertical: true });
|
||||
this.actor.add_actor(this._list);
|
||||
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
|
||||
this._pendingRedisplay = RedisplayFlags.NONE;
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify));
|
||||
|
||||
// map<itemId, Object> where Object represents the item info
|
||||
this._allItems = {};
|
||||
// set<itemId>
|
||||
this._matchedItems = {};
|
||||
// sorted array of items matched by search
|
||||
this._matchedItemKeys = [];
|
||||
// map<itemId, GenericDisplayItem>
|
||||
this._displayedItems = {};
|
||||
this._openDetailIndex = -1;
|
||||
this._selectedIndex = -1;
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
|
||||
// Sets the search string and displays the matching items.
|
||||
setSearch: function(text) {
|
||||
let lowertext = text.toLowerCase();
|
||||
if (lowertext == this._search) {
|
||||
return;
|
||||
}
|
||||
let flags = RedisplayFlags.IMMEDIATE;
|
||||
if (this._search != '') {
|
||||
// Because we combine search terms with OR, we have to be sure that no new term
|
||||
// was introduced before deciding that the new search results will be a subset of
|
||||
// the existing search results.
|
||||
if (lowertext.indexOf(this._search) == 0 &&
|
||||
lowertext.split(/\s+/).length == this._search.split(/\s+/).length) {
|
||||
flags |= RedisplayFlags.SUBSEARCH;
|
||||
}
|
||||
}
|
||||
this._search = lowertext;
|
||||
this._redisplay(flags);
|
||||
},
|
||||
|
||||
// Launches the item that is currently selected, closing the Overview
|
||||
activateSelected: function() {
|
||||
if (this._selectedIndex != -1) {
|
||||
let selected = this._findDisplayedByIndex(this._selectedIndex);
|
||||
selected.launch();
|
||||
this.unsetSelected();
|
||||
Main.overview.hide();
|
||||
}
|
||||
},
|
||||
|
||||
// Moves the selection one up. If the selection was already on the top item, it's moved
|
||||
// to the bottom one. Returns true if the selection actually moved up, false if it wrapped
|
||||
// around to the bottom.
|
||||
selectUp: function() {
|
||||
let count = this._getVisibleCount();
|
||||
let selectedUp = true;
|
||||
let prev = this._selectedIndex - 1;
|
||||
if (this._selectedIndex <= 0) {
|
||||
prev = count - 1;
|
||||
selectedUp = false;
|
||||
}
|
||||
this._selectIndex(prev);
|
||||
return selectedUp;
|
||||
},
|
||||
|
||||
// Moves the selection one down. If the selection was already on the bottom item, it's moved
|
||||
// to the top one. Returns true if the selection actually moved down, false if it wrapped
|
||||
// around to the top.
|
||||
selectDown: function() {
|
||||
let count = this._getVisibleCount();
|
||||
let selectedDown = true;
|
||||
let next = this._selectedIndex + 1;
|
||||
if (this._selectedIndex == count - 1) {
|
||||
next = 0;
|
||||
selectedDown = false;
|
||||
}
|
||||
this._selectIndex(next);
|
||||
return selectedDown;
|
||||
},
|
||||
|
||||
// Selects the first item among the displayed items.
|
||||
selectFirstItem: function() {
|
||||
if (this.hasItems())
|
||||
this._selectIndex(0);
|
||||
},
|
||||
|
||||
// Selects the last item among the displayed items.
|
||||
selectLastItem: function() {
|
||||
let count = this._getVisibleCount();
|
||||
if (this.hasItems())
|
||||
this._selectIndex(count - 1);
|
||||
},
|
||||
|
||||
// Returns true if the display has some item selected.
|
||||
hasSelected: function() {
|
||||
return this._selectedIndex != -1;
|
||||
},
|
||||
|
||||
// Removes selection if some display item is selected.
|
||||
unsetSelected: function() {
|
||||
this._selectIndex(-1);
|
||||
},
|
||||
|
||||
// Returns true if the display has any displayed items.
|
||||
hasItems: function() {
|
||||
// TODO: figure out why this._list.displayedCount is returning a
|
||||
// positive number when this._mathedItems.length is 0
|
||||
// This can be triggered if a search string is entered for which there are no matches.
|
||||
// log('this._mathedItems.length: ' + this._matchedItems.length + ' this._list.displayedCount ' + this._list.displayedCount);
|
||||
return this._matchedItemKeys.length > 0;
|
||||
},
|
||||
|
||||
getMatchedItemsCount: function() {
|
||||
return this._matchedItemKeys.length;
|
||||
},
|
||||
|
||||
// Load the initial state
|
||||
load: function() {
|
||||
this._redisplay(RedisplayFlags.FULL);
|
||||
},
|
||||
|
||||
// Should be called when the display is closed
|
||||
resetState: function() {
|
||||
this._filterReset();
|
||||
this._openDetailIndex = -1;
|
||||
this.actor.get_vscroll_bar().get_adjustment().value = 0;
|
||||
},
|
||||
|
||||
// Returns an actor which acts as a sidebar; this is used for
|
||||
// the applications category view
|
||||
getNavigationArea: function () {
|
||||
return null;
|
||||
},
|
||||
|
||||
createDetailsForIndex: function(index) {
|
||||
let item = this._findDisplayedByIndex(index);
|
||||
return item.createDetailsActor();
|
||||
},
|
||||
|
||||
//// Protected methods ////
|
||||
|
||||
_recreateDisplayItems: function() {
|
||||
this._removeAllDisplayItems();
|
||||
this._setDefaultList();
|
||||
for (let itemId in this._allItems) {
|
||||
this._addDisplayItem(itemId);
|
||||
}
|
||||
},
|
||||
|
||||
// Creates a display item based on the information associated with itemId
|
||||
// and adds it to the list of displayed items, but does not yet display it.
|
||||
_addDisplayItem : function(itemId) {
|
||||
if (this._displayedItems.hasOwnProperty(itemId)) {
|
||||
log('Tried adding a display item for ' + itemId + ', but an item with this item id is already among displayed items.');
|
||||
return;
|
||||
}
|
||||
|
||||
let itemInfo = this._allItems[itemId];
|
||||
let displayItem = this._createDisplayItem(itemInfo);
|
||||
|
||||
displayItem.connect('activate',
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
// update the selection
|
||||
this._selectIndex(this._list.get_children().indexOf(displayItem.actor));
|
||||
this.activateSelected();
|
||||
}));
|
||||
|
||||
displayItem.connect('show-details',
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
let index = this._list.get_children().indexOf(displayItem.actor);
|
||||
/* Close the details pane if already open */
|
||||
if (index == this._openDetailIndex) {
|
||||
this._openDetailIndex = -1;
|
||||
this.emit('show-details', -1);
|
||||
} else {
|
||||
this._openDetailIndex = index;
|
||||
this.emit('show-details', index);
|
||||
}
|
||||
}));
|
||||
this._displayedItems[itemId] = displayItem;
|
||||
},
|
||||
|
||||
// Removes an item identifed by the itemId from the displayed items.
|
||||
_removeDisplayItem: function(itemId) {
|
||||
let children = this._list.get_children();
|
||||
let count = children.length;
|
||||
let displayItem = this._displayedItems[itemId];
|
||||
let displayItemIndex = children.indexOf(displayItem.actor);
|
||||
|
||||
if (this.hasSelected() && count == 1) {
|
||||
this.unsetSelected();
|
||||
} else if (this.hasSelected() && displayItemIndex < this._selectedIndex) {
|
||||
this.selectUp();
|
||||
}
|
||||
|
||||
displayItem.destroy();
|
||||
|
||||
delete this._displayedItems[itemId];
|
||||
},
|
||||
|
||||
// Removes all displayed items.
|
||||
_removeAllDisplayItems: function() {
|
||||
this.unsetSelected();
|
||||
for (itemId in this._displayedItems)
|
||||
this._removeDisplayItem(itemId);
|
||||
},
|
||||
|
||||
// Return true if there's an active search or other constraint
|
||||
// on the list
|
||||
_filterActive: function() {
|
||||
return this._search != '';
|
||||
},
|
||||
|
||||
// Called when we are resetting state
|
||||
_filterReset: function() {
|
||||
this.unsetSelected();
|
||||
},
|
||||
|
||||
_compareSearchMatch: function(a, b) {
|
||||
let countA = this._matchedItems[a];
|
||||
let countB = this._matchedItems[b];
|
||||
if (countA > countB)
|
||||
return -1;
|
||||
else if (countA < countB)
|
||||
return 1;
|
||||
else
|
||||
return this._compareItems(a, b);
|
||||
},
|
||||
|
||||
_setMatches: function(matches) {
|
||||
this._matchedItems = matches;
|
||||
this._matchedItemKeys = [];
|
||||
for (let itemId in this._matchedItems) {
|
||||
this._matchedItemKeys.push(itemId);
|
||||
}
|
||||
this._matchedItemKeys.sort(Lang.bind(this, this._compareSearchMatch));
|
||||
},
|
||||
|
||||
/**
|
||||
* _redisplaySubSearch:
|
||||
* A somewhat more optimized function called when we know
|
||||
* that we're going to be displaying a subset of the items
|
||||
* we already had, in the same order. In that case, we can
|
||||
* just hide the actors that shouldn't be shown.
|
||||
*/
|
||||
_redisplaySubSearch: function() {
|
||||
let matches = this._getSearchMatchedItems(true);
|
||||
|
||||
// Just hide all from the old set,
|
||||
// we'll show the ones we want below
|
||||
for (let itemId in this._displayedItems) {
|
||||
let item = this._displayedItems[itemId];
|
||||
item.actor.hide();
|
||||
}
|
||||
|
||||
this._setMatches(matches);
|
||||
|
||||
for (let itemId in matches) {
|
||||
let item = this._displayedItems[itemId];
|
||||
item.actor.show();
|
||||
}
|
||||
this._list.queue_relayout();
|
||||
},
|
||||
|
||||
_redisplayReordering: function() {
|
||||
if (!this._filterActive()) {
|
||||
this._setDefaultList();
|
||||
} else {
|
||||
this._setMatches(this._getSearchMatchedItems(false));
|
||||
}
|
||||
this._list.remove_all();
|
||||
for (let i = 0; i < this._matchedItemKeys.length; i++) {
|
||||
let itemId = this._matchedItemKeys[i];
|
||||
let item = this._displayedItems[itemId];
|
||||
item.actor.show();
|
||||
this._list.add_actor(item.actor);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Updates the displayed items, applying the search string if one exists.
|
||||
* @flags: Flags controlling redisplay behavior as follows:
|
||||
* SUBSEARCH - Indicates that the current _search is a superstring of the previous
|
||||
* one, which implies we only need to re-search through previous results.
|
||||
* FULL - Indicates that we need recreate all displayed items.
|
||||
* IMMEDIATE - Do the full redisplay even if we're not mapped. This is useful
|
||||
* if you want to get the number of matched items and show/hide a section based on
|
||||
* that number.
|
||||
*/
|
||||
_redisplay: function(flags) {
|
||||
let immediate = (flags & RedisplayFlags.IMMEDIATE) != 0;
|
||||
if (!immediate && !this.actor.mapped) {
|
||||
this._pendingRedisplay |= flags;
|
||||
return;
|
||||
}
|
||||
|
||||
let isSubSearch = (flags & RedisplayFlags.SUBSEARCH) != 0;
|
||||
let fullReload = (flags & RedisplayFlags.FULL) != 0;
|
||||
|
||||
let hadSelected = this.hasSelected();
|
||||
this.unsetSelected();
|
||||
|
||||
if (!this._initialLoadComplete)
|
||||
fullReload = true;
|
||||
|
||||
if (!this._refreshCache())
|
||||
fullReload = true;
|
||||
|
||||
if (fullReload) {
|
||||
this._recreateDisplayItems();
|
||||
this._initialLoadComplete = true;
|
||||
}
|
||||
|
||||
if (isSubSearch) {
|
||||
this._redisplaySubSearch();
|
||||
} else {
|
||||
this._redisplayReordering();
|
||||
}
|
||||
|
||||
if (hadSelected) {
|
||||
this._selectedIndex = -1;
|
||||
this.selectFirstItem();
|
||||
}
|
||||
|
||||
this.emit('redisplayed');
|
||||
},
|
||||
|
||||
//// Pure virtual protected methods ////
|
||||
|
||||
// Performs the steps needed to have the latest information about the items.
|
||||
// Implementation should return %true if we are up to date, and %false
|
||||
// if a full reload occurred.
|
||||
_refreshCache: function() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
// Sets the list of the displayed items based on the default sorting order.
|
||||
// The default sorting order is specific to each implementing class.
|
||||
_setDefaultList: function() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
// Compares items associated with the item ids based on the order in which the
|
||||
// items should be displayed.
|
||||
// Intended to be used as a compareFunction for array.sort().
|
||||
// Returns an integer value indicating the result of the comparison.
|
||||
_compareItems: function(itemIdA, itemIdB) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
// Checks if the item info can be a match for the search string.
|
||||
// Returns a boolean flag indicating if that's the case.
|
||||
_isInfoMatching: function(itemInfo, search) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
// Creates a display item based on itemInfo.
|
||||
_createDisplayItem: function(itemInfo) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
//// Private methods ////
|
||||
|
||||
_getItemSearchScore: function(itemId, terms) {
|
||||
let item = this._allItems[itemId];
|
||||
let score = 0;
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
let term = terms[i];
|
||||
if (this._isInfoMatching(item, term)) {
|
||||
score++;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
},
|
||||
|
||||
_getSearchMatchedItems: function(isSubSearch) {
|
||||
// Break the search up into terms, and search for each
|
||||
// individual term. Keep track of the number of terms
|
||||
// each item matched.
|
||||
let terms = this._search.split(/\s+/);
|
||||
let matchScores = {};
|
||||
|
||||
if (isSubSearch) {
|
||||
for (let i = 0; i < this._matchedItemKeys.length; i++) {
|
||||
let itemId = this._matchedItemKeys[i];
|
||||
let score = this._getItemSearchScore(itemId, terms);
|
||||
if (score > 0)
|
||||
matchScores[itemId] = score;
|
||||
}
|
||||
} else {
|
||||
for (let itemId in this._displayedItems) {
|
||||
let score = this._getItemSearchScore(itemId, terms);
|
||||
if (score > 0)
|
||||
matchScores[itemId] = score;
|
||||
}
|
||||
}
|
||||
return matchScores;
|
||||
},
|
||||
|
||||
// Returns a display item based on its index in the ordering of the
|
||||
// display children.
|
||||
_findDisplayedByIndex: function(index) {
|
||||
let actor = this._list.get_children()[index];
|
||||
return this._findDisplayedByActor(actor);
|
||||
},
|
||||
|
||||
// Returns a display item based on the actor that represents it in
|
||||
// the display.
|
||||
_findDisplayedByActor: function(actor) {
|
||||
for (itemId in this._displayedItems) {
|
||||
let item = this._displayedItems[itemId];
|
||||
if (item.actor == actor) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// Selects (e.g. highlights) a display item at the provided index,
|
||||
// updates this.selectedItemDetails actor, and emits 'selected' signal.
|
||||
_selectIndex: function(index) {
|
||||
// Cleanup from the previous item
|
||||
if (this.hasSelected()) {
|
||||
this._findDisplayedByIndex(this._selectedIndex).markSelected(false);
|
||||
}
|
||||
|
||||
this._selectedIndex = index;
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
// Mark the new item as selected and create its details pane
|
||||
let item = this._findDisplayedByIndex(index);
|
||||
item.markSelected(true);
|
||||
this.emit('selected');
|
||||
},
|
||||
|
||||
_getVisibleCount: function() {
|
||||
return this._list.get_n_children();
|
||||
},
|
||||
|
||||
_onMappedNotify: function () {
|
||||
let mapped = this.actor.mapped;
|
||||
if (mapped && this._pendingRedisplay > RedisplayFlags.NONE)
|
||||
this._redisplay(this._pendingRedisplay);
|
||||
|
||||
this._pendingRedisplay = RedisplayFlags.NONE;
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(GenericDisplay.prototype);
|
@ -17,8 +17,7 @@ function BaseIcon(label, createIcon) {
|
||||
BaseIcon.prototype = {
|
||||
_init : function(label, params) {
|
||||
params = Params.parse(params, { createIcon: null,
|
||||
setSizeManually: false,
|
||||
showLabel: true });
|
||||
setSizeManually: false });
|
||||
this.actor = new St.Bin({ style_class: 'overview-icon',
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
@ -41,52 +40,42 @@ BaseIcon.prototype = {
|
||||
|
||||
box.add_actor(this._iconBin);
|
||||
|
||||
if (params.showLabel) {
|
||||
this._name = new St.Label({ text: label });
|
||||
box.add_actor(this._name);
|
||||
} else {
|
||||
this._name = null;
|
||||
}
|
||||
this._name = new St.Label({ text: label });
|
||||
box.add_actor(this._name);
|
||||
|
||||
if (params.createIcon)
|
||||
this.createIcon = params.createIcon;
|
||||
this._setSizeManually = params.setSizeManually;
|
||||
|
||||
this.icon = null;
|
||||
this.icon = this.createIcon(this.iconSize);
|
||||
this._iconBin.set_child(this.icon);
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
let iconSize = availHeight;
|
||||
|
||||
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(-1);
|
||||
let [iconMinHeight, iconNatHeight] = this._iconBin.get_preferred_height(-1);
|
||||
let [iconMinWidth, iconNatWidth] = this._iconBin.get_preferred_width(-1);
|
||||
let preferredHeight = iconNatHeight;
|
||||
let preferredHeight = labelNatHeight + this._spacing + iconNatHeight;
|
||||
let labelHeight = availHeight >= preferredHeight ? labelNatHeight
|
||||
: labelMinHeight;
|
||||
let iconSize = availHeight - this._spacing - labelHeight;
|
||||
let iconPadding = (availWidth - iconSize) / 2;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
if (this._name) {
|
||||
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(-1);
|
||||
preferredHeight += this._spacing + labelNatHeight;
|
||||
|
||||
let labelHeight = availHeight >= preferredHeight ? labelNatHeight
|
||||
: labelMinHeight;
|
||||
iconSize -= this._spacing + labelHeight;
|
||||
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = availWidth;
|
||||
childBox.y1 = iconSize + this._spacing;
|
||||
childBox.y2 = childBox.y1 + labelHeight;
|
||||
this._name.allocate(childBox, flags);
|
||||
}
|
||||
|
||||
childBox.x1 = Math.floor((availWidth - iconNatWidth) / 2);
|
||||
childBox.y1 = Math.floor((iconSize - iconNatHeight) / 2);
|
||||
childBox.x2 = childBox.x1 + iconNatWidth;
|
||||
childBox.y2 = childBox.y1 + iconNatHeight;
|
||||
childBox.x1 = iconPadding;
|
||||
childBox.y1 = 0;
|
||||
childBox.x2 = availWidth - iconPadding;
|
||||
childBox.y2 = iconSize;
|
||||
this._iconBin.allocate(childBox, flags);
|
||||
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = availWidth;
|
||||
childBox.y1 = iconSize + this._spacing;
|
||||
childBox.y2 = childBox.y1 + labelHeight;
|
||||
this._name.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
@ -95,14 +84,9 @@ BaseIcon.prototype = {
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
let [iconMinHeight, iconNatHeight] = this._iconBin.get_preferred_height(forWidth);
|
||||
alloc.min_size = iconMinHeight;
|
||||
alloc.natural_size = iconNatHeight;
|
||||
|
||||
if (this._name) {
|
||||
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(forWidth);
|
||||
alloc.min_size += this._spacing + labelMinHeight;
|
||||
alloc.natural_size += this._spacing + labelNatHeight;
|
||||
}
|
||||
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(forWidth);
|
||||
alloc.min_size = iconMinHeight + this._spacing + labelMinHeight;
|
||||
alloc.natural_size = iconNatHeight + this._spacing + labelNatHeight;
|
||||
},
|
||||
|
||||
// This can be overridden by a subclass, or by the createIcon
|
||||
@ -115,23 +99,16 @@ BaseIcon.prototype = {
|
||||
if (!this._setSizeManually)
|
||||
throw new Error('setSizeManually has to be set to use setIconsize');
|
||||
|
||||
this._setIconSize(size);
|
||||
},
|
||||
|
||||
_setIconSize: function(size) {
|
||||
if (size == this.iconSize)
|
||||
return;
|
||||
|
||||
this._createIconTexture(size);
|
||||
},
|
||||
|
||||
_createIconTexture: function(size) {
|
||||
if (this.icon)
|
||||
this.icon.destroy();
|
||||
this.icon.destroy();
|
||||
this.iconSize = size;
|
||||
this.icon = this.createIcon(this.iconSize);
|
||||
|
||||
// The icon returned by createIcon() might actually be smaller than
|
||||
// the requested icon size (for instance StTextureCache does this
|
||||
// for fallback icons), so set the size explicitly.
|
||||
this.icon.set_size(this.iconSize, this.iconSize);
|
||||
|
||||
this._iconBin.child = this.icon;
|
||||
},
|
||||
|
||||
@ -139,15 +116,12 @@ BaseIcon.prototype = {
|
||||
let node = this.actor.get_theme_node();
|
||||
this._spacing = node.get_length('spacing');
|
||||
|
||||
let size;
|
||||
if (this._setSizeManually) {
|
||||
size = this.iconSize;
|
||||
} else {
|
||||
let [found, len] = node.lookup_length('icon-size', false);
|
||||
size = found ? len : ICON_SIZE;
|
||||
}
|
||||
if (this._setSizeManually)
|
||||
return;
|
||||
|
||||
this._createIconTexture(size);
|
||||
let len = node.get_length('icon-size');
|
||||
if (len > 0)
|
||||
this._setIconSize(len);
|
||||
}
|
||||
};
|
||||
|
||||
@ -191,16 +165,8 @@ IconGrid.prototype = {
|
||||
alloc.natural_size = nColumns * this._item_size + totalSpacing;
|
||||
},
|
||||
|
||||
_getVisibleChildren: function() {
|
||||
let children = this._grid.get_children();
|
||||
children = children.filter(function(actor) {
|
||||
return actor.visible;
|
||||
});
|
||||
return children;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||
let children = this._getVisibleChildren();
|
||||
let children = this._grid.get_children();
|
||||
let [nColumns, usedWidth] = this._computeLayout(forWidth);
|
||||
let nRows;
|
||||
if (nColumns > 0)
|
||||
@ -216,7 +182,7 @@ IconGrid.prototype = {
|
||||
},
|
||||
|
||||
_allocate: function (grid, box, flags) {
|
||||
let children = this._getVisibleChildren();
|
||||
let children = this._grid.get_children();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
@ -281,11 +247,8 @@ IconGrid.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
childrenInRow: function(rowWidth) {
|
||||
return this._computeLayout(rowWidth)[0];
|
||||
},
|
||||
|
||||
_computeLayout: function (forWidth) {
|
||||
let children = this._grid.get_children();
|
||||
let nColumns = 0;
|
||||
let usedWidth = 0;
|
||||
while ((this._colLimit == null || nColumns < this._colLimit) &&
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Cogl = imports.gi.Cogl;
|
||||
const GConf = imports.gi.GConf;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
@ -14,7 +13,6 @@ const Mainloop = imports.mainloop;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const History = imports.misc.history;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const Link = imports.ui.link;
|
||||
const Tweener = imports.ui.tweener;
|
||||
@ -27,7 +25,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
'const Mainloop = imports.mainloop; ' +
|
||||
'const Meta = imports.gi.Meta; ' +
|
||||
'const Shell = imports.gi.Shell; ' +
|
||||
'const Tp = imports.gi.TelepathyGLib; ' +
|
||||
'const Main = imports.ui.main; ' +
|
||||
'const Lang = imports.lang; ' +
|
||||
'const Tweener = imports.ui.tweener; ' +
|
||||
@ -39,8 +36,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
'const it = Main.lookingGlass.getIt(); ' +
|
||||
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
||||
|
||||
const HISTORY_KEY = 'looking-glass-history';
|
||||
|
||||
function Notebook() {
|
||||
this._init();
|
||||
}
|
||||
@ -364,30 +359,6 @@ ObjInspector.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function addBorderPaintHook(actor) {
|
||||
let signalId = actor.connect_after('paint',
|
||||
function () {
|
||||
let color = new Cogl.Color();
|
||||
color.init_from_4ub(0xff, 0, 0, 0xc4);
|
||||
Cogl.set_source_color(color);
|
||||
|
||||
let geom = actor.get_allocation_geometry();
|
||||
let width = 2;
|
||||
|
||||
// clockwise order
|
||||
Cogl.rectangle(0, 0, geom.width, width);
|
||||
Cogl.rectangle(geom.width - width, width,
|
||||
geom.width, geom.height);
|
||||
Cogl.rectangle(0, geom.height,
|
||||
geom.width - width, geom.height - width);
|
||||
Cogl.rectangle(0, geom.height - width,
|
||||
width, width);
|
||||
});
|
||||
|
||||
actor.queue_redraw();
|
||||
return signalId;
|
||||
}
|
||||
|
||||
function Inspector() {
|
||||
this._init();
|
||||
}
|
||||
@ -427,9 +398,6 @@ Inspector.prototype = {
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
if (!this._eventHandler)
|
||||
return;
|
||||
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
let [minWidth, minHeight, natWidth, natHeight] =
|
||||
@ -447,7 +415,6 @@ Inspector.prototype = {
|
||||
Clutter.ungrab_pointer(this._eventHandler);
|
||||
Clutter.ungrab_keyboard(this._eventHandler);
|
||||
this._eventHandler.destroy();
|
||||
this._eventHandler = null;
|
||||
this.emit('closed');
|
||||
},
|
||||
|
||||
@ -523,13 +490,10 @@ Inspector.prototype = {
|
||||
let position = '[inspect x: ' + stageX + ' y: ' + stageY + ']';
|
||||
this._displayText.text = '';
|
||||
this._displayText.text = position + ' ' + this._target;
|
||||
|
||||
if (this._borderPaintTarget != this._target) {
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
this._borderPaintTarget = this._target;
|
||||
this._borderPaintId = addBorderPaintHook(this._target);
|
||||
}
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
this._borderPaintTarget = this._target;
|
||||
this._borderPaintId = Shell.add_hook_paint_red_border(this._target);
|
||||
}
|
||||
};
|
||||
|
||||
@ -675,10 +639,18 @@ function LookingGlass() {
|
||||
|
||||
LookingGlass.prototype = {
|
||||
_init : function() {
|
||||
this._idleHistorySaveId = 0;
|
||||
let historyPath = global.userdatadir + '/lookingglass-history.txt';
|
||||
this._historyFile = Gio.file_new_for_path(historyPath);
|
||||
this._savedText = null;
|
||||
this._historyNavIndex = -1;
|
||||
this._history = [];
|
||||
this._borderPaintTarget = null;
|
||||
this._borderPaintId = 0;
|
||||
this._borderDestroyId = 0;
|
||||
|
||||
this._readHistory();
|
||||
|
||||
this._open = false;
|
||||
|
||||
this._offset = 0;
|
||||
@ -691,11 +663,11 @@ LookingGlass.prototype = {
|
||||
style_class: 'lg-dialog',
|
||||
vertical: true,
|
||||
visible: false });
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._globalKeyPressEvent));
|
||||
|
||||
this._interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._interfaceSettings.connect('changed::monospace-font-name',
|
||||
Lang.bind(this, this._updateFont));
|
||||
let gconf = GConf.Client.get_default();
|
||||
gconf.add_dir('/desktop/gnome/interface', GConf.ClientPreloadType.PRELOAD_NONE);
|
||||
gconf.notify_add('/desktop/gnome/interface/monospace_font_name',
|
||||
Lang.bind(this, this._updateFont));
|
||||
this._updateFont();
|
||||
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
@ -775,15 +747,39 @@ LookingGlass.prototype = {
|
||||
if (text == '')
|
||||
return true;
|
||||
this._evaluate(text);
|
||||
this._historyNavIndex = -1;
|
||||
return true;
|
||||
}));
|
||||
|
||||
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
|
||||
entry: this._entry.clutter_text });
|
||||
this._entry.clutter_text.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Up) {
|
||||
if (this._historyNavIndex >= this._history.length - 1)
|
||||
return true;
|
||||
this._historyNavIndex++;
|
||||
if (this._historyNavIndex == 0)
|
||||
this._savedText = this._entry.text;
|
||||
this._entry.text = this._history[this._history.length - this._historyNavIndex - 1];
|
||||
return true;
|
||||
} else if (symbol == Clutter.Down) {
|
||||
if (this._historyNavIndex <= 0)
|
||||
return true;
|
||||
this._historyNavIndex--;
|
||||
if (this._historyNavIndex < 0)
|
||||
this._entry.text = this._savedText;
|
||||
else
|
||||
this._entry.text = this._history[this._history.length - this._historyNavIndex - 1];
|
||||
return true;
|
||||
} else {
|
||||
this._historyNavIndex = -1;
|
||||
this._savedText = null;
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_updateFont: function() {
|
||||
let fontName = this._interfaceSettings.get_string('monospace-font-name');
|
||||
let gconf = GConf.Client.get_default();
|
||||
let fontName = gconf.get_string('/desktop/gnome/interface/monospace_font_name');
|
||||
// This is mishandled by the scanner - should by Pango.FontDescription_from_string(fontName);
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=595889
|
||||
let fontDesc = Pango.font_description_from_string(fontName);
|
||||
@ -794,6 +790,29 @@ LookingGlass.prototype = {
|
||||
+ 'font-family: "' + fontDesc.get_family() + '";';
|
||||
},
|
||||
|
||||
_readHistory: function () {
|
||||
if (!this._historyFile.query_exists(null))
|
||||
return;
|
||||
let [result, contents, length, etag] = this._historyFile.load_contents(null);
|
||||
this._history = contents.split('\n').filter(function (e) { return e != ''; });
|
||||
},
|
||||
|
||||
_queueHistorySave: function() {
|
||||
if (this._idleHistorySaveId > 0)
|
||||
return;
|
||||
this._idleHistorySaveId = Mainloop.timeout_add_seconds(5, Lang.bind(this, this._doSaveHistory));
|
||||
},
|
||||
|
||||
_doSaveHistory: function () {
|
||||
this._idleHistorySaveId = false;
|
||||
let output = this._historyFile.replace(null, true, Gio.FileCreateFlags.NONE, null);
|
||||
let dataOut = new Gio.DataOutputStream({ base_stream: output });
|
||||
dataOut.put_string(this._history.join('\n'), null);
|
||||
dataOut.put_string('\n', null);
|
||||
dataOut.close(null);
|
||||
return false;
|
||||
},
|
||||
|
||||
_pushResult: function(command, obj) {
|
||||
let index = this._results.length + this._offset;
|
||||
let result = new Result('>>> ' + command, obj, index);
|
||||
@ -805,7 +824,7 @@ LookingGlass.prototype = {
|
||||
}
|
||||
if (obj instanceof Clutter.Actor) {
|
||||
this._borderPaintTarget = obj;
|
||||
this._borderPaintId = addBorderPaintHook(obj);
|
||||
this._borderPaintId = Shell.add_hook_paint_red_border(obj);
|
||||
this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () {
|
||||
this._borderDestroyId = 0;
|
||||
this._borderPaintTarget = null;
|
||||
@ -824,7 +843,8 @@ LookingGlass.prototype = {
|
||||
},
|
||||
|
||||
_evaluate : function(command) {
|
||||
this._history.addItem(command);
|
||||
this._history.push(command);
|
||||
this._queueHistorySave();
|
||||
|
||||
let fullCmd = commandHeader + command;
|
||||
|
||||
@ -905,16 +925,20 @@ LookingGlass.prototype = {
|
||||
if (this._open)
|
||||
return;
|
||||
|
||||
if (!Main.pushModal(this._entry))
|
||||
if (!Main.pushModal(this.actor))
|
||||
return;
|
||||
|
||||
this._keyPressEventId = global.stage.connect('key-press-event',
|
||||
Lang.bind(this, this._globalKeyPressEvent));
|
||||
|
||||
this.actor.show();
|
||||
this.actor.lower(Main.chrome.actor);
|
||||
this._open = true;
|
||||
this._history.lastItem();
|
||||
|
||||
Tweener.removeTweens(this.actor);
|
||||
|
||||
global.stage.set_key_focus(this._entry);
|
||||
|
||||
// We inverse compensate for the slow-down so you can change the factor
|
||||
// through LookingGlass without long waits.
|
||||
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
|
||||
@ -927,8 +951,12 @@ LookingGlass.prototype = {
|
||||
if (!this._open)
|
||||
return;
|
||||
|
||||
if (this._keyPressEventId)
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
|
||||
this._objInspector.actor.hide();
|
||||
|
||||
this._historyNavIndex = -1;
|
||||
this._open = false;
|
||||
Tweener.removeTweens(this.actor);
|
||||
|
||||
@ -938,7 +966,7 @@ LookingGlass.prototype = {
|
||||
this._borderPaintTarget = null;
|
||||
}
|
||||
|
||||
Main.popModal(this._entry);
|
||||
Main.popModal(this.actor);
|
||||
|
||||
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
|
||||
transition: 'easeOutQuad',
|
||||
|
@ -112,11 +112,6 @@ ShellMagnifier.prototype = {
|
||||
* [left, top, right, bottom].
|
||||
* @viewPort Array of integers, [left, top, right, bottom] that defines
|
||||
* the position of the ZoomRegion on screen.
|
||||
*
|
||||
* FIXME: The arguments here are redundant, since the width and height of
|
||||
* the ROI are determined by the viewport and magnification factors.
|
||||
* We ignore the passed in width and height.
|
||||
*
|
||||
* @return The newly created ZoomRegion.
|
||||
*/
|
||||
createZoomRegion: function(xMagFactor, yMagFactor, roi, viewPort) {
|
||||
|
541
js/ui/main.js
@ -17,13 +17,8 @@ const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Chrome = imports.ui.chrome;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
||||
const Environment = imports.ui.environment;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
@ -39,16 +34,13 @@ const ShellDBus = imports.ui.shellDBus;
|
||||
const TelepathyClient = imports.ui.telepathyClient;
|
||||
const WindowManager = imports.ui.windowManager;
|
||||
const Magnifier = imports.ui.magnifier;
|
||||
const XdndHandler = imports.ui.xdndHandler;
|
||||
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
||||
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
||||
|
||||
let chrome = null;
|
||||
let panel = null;
|
||||
let hotCorners = [];
|
||||
let placesManager = null;
|
||||
let overview = null;
|
||||
let runDialog = null;
|
||||
@ -58,19 +50,15 @@ let messageTray = null;
|
||||
let notificationDaemon = null;
|
||||
let windowAttentionHandler = null;
|
||||
let telepathyClient = null;
|
||||
let ctrlAltTabManager = null;
|
||||
let recorder = null;
|
||||
let shellDBusService = null;
|
||||
let modalCount = 0;
|
||||
let modalActorFocusStack = [];
|
||||
let uiGroup = null;
|
||||
let magnifier = null;
|
||||
let xdndHandler = null;
|
||||
let statusIconDispatcher = null;
|
||||
let _errorLogStack = [];
|
||||
let _startDate;
|
||||
let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
|
||||
let background = null;
|
||||
|
||||
@ -86,11 +74,9 @@ function start() {
|
||||
global.logError = _logError;
|
||||
global.log = _logDebug;
|
||||
|
||||
// Chain up async errors reported from C
|
||||
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
|
||||
|
||||
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
||||
|
||||
global.grab_dbus_service();
|
||||
shellDBusService = new ShellDBus.GnomeShell();
|
||||
// Force a connection now; dbus.js will do this internally
|
||||
// if we use its name acquisition stuff but we aren't right
|
||||
@ -98,10 +84,6 @@ function start() {
|
||||
// back into sync ones.
|
||||
DBus.session.flush();
|
||||
|
||||
// Load the calendar server. Note that we are careful about
|
||||
// not loading any events until the user presses the clock
|
||||
global.launch_calendar_server();
|
||||
|
||||
Environment.init();
|
||||
|
||||
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||
@ -121,8 +103,10 @@ function start() {
|
||||
global.stage.color = DEFAULT_BACKGROUND_COLOR;
|
||||
global.stage.no_clear_hint = true;
|
||||
|
||||
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
||||
loadTheme();
|
||||
let themeContext = St.ThemeContext.get_for_stage (global.stage);
|
||||
let stylesheetPath = global.datadir + '/theme/gnome-shell.css';
|
||||
let theme = new St.Theme ({ application_stylesheet: stylesheetPath });
|
||||
themeContext.set_theme (theme);
|
||||
|
||||
let shellwm = global.window_manager;
|
||||
shellwm.takeover_keybinding('panel_main_menu');
|
||||
@ -136,14 +120,11 @@ function start() {
|
||||
|
||||
// Set up stage hierarchy to group all UI actors under one container.
|
||||
uiGroup = new Clutter.Group();
|
||||
St.set_ui_root(global.stage, uiGroup);
|
||||
global.window_group.reparent(uiGroup);
|
||||
global.overlay_group.reparent(uiGroup);
|
||||
global.stage.add_actor(uiGroup);
|
||||
|
||||
placesManager = new PlaceDisplay.PlacesManager();
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
overview = new Overview.Overview();
|
||||
chrome = new Chrome.Chrome();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
@ -155,9 +136,6 @@ function start() {
|
||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||
telepathyClient = new TelepathyClient.Client();
|
||||
|
||||
overview.init();
|
||||
statusIconDispatcher.start(messageTray.actor);
|
||||
|
||||
_startDate = new Date();
|
||||
|
||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
@ -184,24 +162,11 @@ function start() {
|
||||
}
|
||||
});
|
||||
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
|
||||
|
||||
// Provide the bus object for gnome-session to
|
||||
// initiate logouts.
|
||||
EndSessionDialog.init();
|
||||
|
||||
// Attempt to become a PolicyKit authentication agent
|
||||
PolkitAuthenticationAgent.init()
|
||||
|
||||
global.screen.connect('monitors-changed', _relayout);
|
||||
global.gdk_screen.connect('monitors-changed', _relayout);
|
||||
|
||||
ExtensionSystem.init();
|
||||
ExtensionSystem.loadExtensions();
|
||||
|
||||
// Perform initial relayout here
|
||||
_relayout();
|
||||
|
||||
panel.startStatusArea();
|
||||
panel.startupAnimation();
|
||||
|
||||
let display = global.screen.get_display();
|
||||
@ -209,233 +174,20 @@ function start() {
|
||||
|
||||
global.stage.connect('captured-event', _globalKeyPressHandler);
|
||||
|
||||
// Perform initial relayout here
|
||||
_relayout();
|
||||
|
||||
_log('info', 'loaded at ' + _startDate);
|
||||
log('GNOME Shell started at ' + _startDate);
|
||||
|
||||
Mainloop.idle_add(_removeUnusedWorkspaces);
|
||||
|
||||
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
|
||||
if (perfModuleName) {
|
||||
let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
|
||||
let module = eval('imports.perf.' + perfModuleName + ';');
|
||||
Scripting.runPerfScript(module, perfOutput);
|
||||
}
|
||||
|
||||
global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
|
||||
|
||||
global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
|
||||
global.screen.connect('window-left-monitor', _windowLeftMonitor);
|
||||
|
||||
_nWorkspacesChanged();
|
||||
}
|
||||
|
||||
let _workspaces = [];
|
||||
let _checkWorkspacesId = 0;
|
||||
|
||||
/*
|
||||
* When the last window closed on a workspace is a dialog or splash
|
||||
* screen, we assume that it might be an initial window shown before
|
||||
* the main window of an application, and give the app a grace period
|
||||
* where it can map another window before we remove the workspace.
|
||||
*/
|
||||
const LAST_WINDOW_GRACE_TIME = 1000;
|
||||
|
||||
function _checkWorkspaces() {
|
||||
let i;
|
||||
let emptyWorkspaces = [];
|
||||
|
||||
for (i = 0; i < _workspaces.length; i++) {
|
||||
let lastRemoved = _workspaces[i]._lastRemovedWindow;
|
||||
if (lastRemoved &&
|
||||
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
|
||||
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
|
||||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG))
|
||||
emptyWorkspaces[i] = false;
|
||||
else
|
||||
emptyWorkspaces[i] = true;
|
||||
}
|
||||
|
||||
let windows = global.get_window_actors();
|
||||
for (i = 0; i < windows.length; i++) {
|
||||
let win = windows[i];
|
||||
|
||||
if (win.get_meta_window().is_on_all_workspaces())
|
||||
continue;
|
||||
|
||||
let workspaceIndex = win.get_workspace();
|
||||
emptyWorkspaces[workspaceIndex] = false;
|
||||
}
|
||||
|
||||
// If we don't have an empty workspace at the end, add one
|
||||
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
|
||||
global.screen.append_new_workspace(false, global.get_current_time());
|
||||
emptyWorkspaces.push(false);
|
||||
}
|
||||
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
|
||||
activeWorkspaceIndex < emptyWorkspaces.length - 1);
|
||||
// Don't enter the overview when removing multiple empty workspaces at startup
|
||||
let showOverview = (removingCurrentWorkspace &&
|
||||
!emptyWorkspaces.every(function(x) { return x; }));
|
||||
|
||||
if (removingCurrentWorkspace) {
|
||||
// "Merge" the empty workspace we are removing with the one at the end
|
||||
wm.blockAnimations();
|
||||
}
|
||||
|
||||
// Delete other empty workspaces; do it from the end to avoid index changes
|
||||
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
|
||||
if (emptyWorkspaces[i])
|
||||
global.screen.remove_workspace(_workspaces[i], global.get_current_time());
|
||||
}
|
||||
|
||||
if (removingCurrentWorkspace) {
|
||||
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
|
||||
wm.unblockAnimations();
|
||||
|
||||
if (!overview.visible && showOverview)
|
||||
overview.show();
|
||||
}
|
||||
|
||||
_checkWorkspacesId = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
function _windowRemoved(workspace, window) {
|
||||
workspace._lastRemovedWindow = window;
|
||||
_queueCheckWorkspaces();
|
||||
Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, function() {
|
||||
if (workspace._lastRemovedWindow == window) {
|
||||
workspace._lastRemovedWindow = null;
|
||||
_queueCheckWorkspaces();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
|
||||
// If the window left the primary monitor, that
|
||||
// might make that workspace empty
|
||||
if (monitorIndex == global.get_primary_monitor_index())
|
||||
_queueCheckWorkspaces();
|
||||
}
|
||||
|
||||
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
|
||||
// If the window entered the primary monitor, that
|
||||
// might make that workspace non-empty
|
||||
if (monitorIndex == global.get_primary_monitor_index())
|
||||
_queueCheckWorkspaces();
|
||||
}
|
||||
|
||||
function _queueCheckWorkspaces() {
|
||||
if (_checkWorkspacesId == 0)
|
||||
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
|
||||
}
|
||||
|
||||
function _nWorkspacesChanged() {
|
||||
let oldNumWorkspaces = _workspaces.length;
|
||||
let newNumWorkspaces = global.screen.n_workspaces;
|
||||
|
||||
if (oldNumWorkspaces == newNumWorkspaces)
|
||||
return false;
|
||||
|
||||
let lostWorkspaces = [];
|
||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
||||
let w;
|
||||
|
||||
// Assume workspaces are only added at the end
|
||||
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
||||
_workspaces[w] = global.screen.get_workspace_by_index(w);
|
||||
|
||||
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
||||
let workspace = _workspaces[w];
|
||||
workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
|
||||
workspace._windowRemovedId = workspace.connect('window-removed', _windowRemoved);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Assume workspaces are only removed sequentially
|
||||
// (e.g. 2,3,4 - not 2,4,7)
|
||||
let removedIndex;
|
||||
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
||||
for (let w = 0; w < oldNumWorkspaces; w++) {
|
||||
let workspace = global.screen.get_workspace_by_index(w);
|
||||
if (_workspaces[w] != workspace) {
|
||||
removedIndex = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
|
||||
lostWorkspaces.forEach(function(workspace) {
|
||||
workspace.disconnect(workspace._windowAddedId);
|
||||
workspace.disconnect(workspace._windowRemovedId);
|
||||
});
|
||||
}
|
||||
|
||||
_queueCheckWorkspaces();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* getThemeStylesheet:
|
||||
*
|
||||
* Get the theme CSS file that the shell will load
|
||||
*
|
||||
* Returns: A file path that contains the theme CSS,
|
||||
* null if using the default
|
||||
*/
|
||||
function getThemeStylesheet()
|
||||
{
|
||||
return _cssStylesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* setThemeStylesheet:
|
||||
* @cssStylesheet: A file path that contains the theme CSS,
|
||||
* set it to null to use the default
|
||||
*
|
||||
* Set the theme CSS file that the shell will load
|
||||
*/
|
||||
function setThemeStylesheet(cssStylesheet)
|
||||
{
|
||||
_cssStylesheet = cssStylesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* loadTheme:
|
||||
*
|
||||
* Reloads the theme CSS file
|
||||
*/
|
||||
function loadTheme() {
|
||||
let themeContext = St.ThemeContext.get_for_stage (global.stage);
|
||||
|
||||
let cssStylesheet = _defaultCssStylesheet;
|
||||
if (_cssStylesheet != null)
|
||||
cssStylesheet = _cssStylesheet;
|
||||
|
||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
||||
themeContext.set_theme (theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* notifyError:
|
||||
* @msg: An error message
|
||||
* @details: Additional information
|
||||
*
|
||||
* See shell_global_notify_problem().
|
||||
*/
|
||||
function notifyError(msg, details) {
|
||||
// Also print to stderr so it's logged somewhere
|
||||
if (details)
|
||||
log("error: " + msg + ": " + details);
|
||||
else
|
||||
log("error: " + msg)
|
||||
|
||||
let source = new MessageTray.SystemNotificationSource();
|
||||
messageTray.add(source);
|
||||
let notification = new MessageTray.Notification(source, msg, details);
|
||||
notification.setTransient(true);
|
||||
source.notify(notification);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -480,90 +232,45 @@ function _getAndClearErrorStack() {
|
||||
}
|
||||
|
||||
function _relayout() {
|
||||
let monitors = global.get_monitors();
|
||||
// destroy old corners
|
||||
for (let i = 0; i < hotCorners.length; i++)
|
||||
hotCorners[i].destroy();
|
||||
hotCorners = [];
|
||||
|
||||
|
||||
let primary = global.get_primary_monitor();
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
let monitor = monitors[i];
|
||||
let isPrimary = (monitor.x == primary.x &&
|
||||
monitor.y == primary.y &&
|
||||
monitor.width == primary.width &&
|
||||
monitor.height == primary.height);
|
||||
|
||||
let cornerX = monitor.x;
|
||||
let cornerY = monitor.y;
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
cornerX += monitor.width;
|
||||
|
||||
|
||||
let haveTopLeftCorner = true;
|
||||
|
||||
/* Check if we have a top left (right for RTL) corner.
|
||||
* I.e. if there is no monitor directly above or to the left(right) */
|
||||
let besideX;
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
besideX = monitor.x + 1;
|
||||
else
|
||||
besideX = cornerX - 1;
|
||||
let besideY = cornerY;
|
||||
let aboveX = cornerX;
|
||||
let aboveY = cornerY - 1;
|
||||
|
||||
for (let j = 0; j < monitors.length; j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
let otherMonitor = monitors[j];
|
||||
if (besideX >= otherMonitor.x &&
|
||||
besideX < otherMonitor.x + otherMonitor.width &&
|
||||
besideY >= otherMonitor.y &&
|
||||
besideY < otherMonitor.y + otherMonitor.height) {
|
||||
haveTopLeftCorner = false;
|
||||
break;
|
||||
}
|
||||
if (aboveX >= otherMonitor.x &&
|
||||
aboveX < otherMonitor.x + otherMonitor.width &&
|
||||
aboveY >= otherMonitor.y &&
|
||||
aboveY < otherMonitor.y + otherMonitor.height) {
|
||||
haveTopLeftCorner = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We only want hot corners where there is a natural top-left
|
||||
* corner, and on the primary monitor */
|
||||
if (!isPrimary && !haveTopLeftCorner)
|
||||
continue;
|
||||
|
||||
let corner = new Panel.HotCorner(isPrimary ? panel.button : null);
|
||||
hotCorners.push(corner);
|
||||
corner.actor.set_position(cornerX, cornerY);
|
||||
if (isPrimary)
|
||||
panel.setHotCorner(corner);
|
||||
}
|
||||
|
||||
panel.relayout();
|
||||
panel.actor.set_position(primary.x, primary.y);
|
||||
panel.actor.set_size(primary.width, Panel.PANEL_HEIGHT);
|
||||
overview.relayout();
|
||||
|
||||
// To avoid updating the position and size of the workspaces
|
||||
// in the overview, we just hide the overview. The positions
|
||||
// will be updated when it is next shown.
|
||||
// will be updated when it is next shown. We do the same for
|
||||
// the calendar popdown.
|
||||
overview.hide();
|
||||
panel.hideCalendar();
|
||||
}
|
||||
|
||||
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
||||
return win.get_workspace() == workspaceIndex ||
|
||||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
||||
}
|
||||
// metacity-clutter currently uses the same prefs as plain metacity,
|
||||
// which probably means we'll be starting out with multiple workspaces;
|
||||
// remove any unused ones. (We do this from an idle handler, because
|
||||
// global.get_window_actors() still returns NULL at the point when start()
|
||||
// is called.)
|
||||
function _removeUnusedWorkspaces() {
|
||||
|
||||
function getWindowActorsForWorkspace(workspaceIndex) {
|
||||
return global.get_window_actors().filter(function (win) {
|
||||
return isWindowActorDisplayedOnWorkspace(win, workspaceIndex);
|
||||
});
|
||||
let windows = global.get_window_actors();
|
||||
let maxWorkspace = 0;
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let win = windows[i];
|
||||
|
||||
if (!win.get_meta_window().is_on_all_workspaces() &&
|
||||
win.get_workspace() > maxWorkspace) {
|
||||
maxWorkspace = win.get_workspace();
|
||||
}
|
||||
}
|
||||
let screen = global.screen;
|
||||
if (screen.n_workspaces > maxWorkspace) {
|
||||
for (let w = screen.n_workspaces - 1; w > maxWorkspace; w--) {
|
||||
let workspace = screen.get_workspace_by_index(w);
|
||||
screen.remove_workspace(workspace, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// This function encapsulates hacks to make certain global keybindings
|
||||
@ -571,67 +278,57 @@ function getWindowActorsForWorkspace(workspaceIndex) {
|
||||
// are disabled with a global grab. (When there is a global grab, then
|
||||
// all key events will be delivered to the stage, so ::captured-event
|
||||
// on the stage can be used for global keybindings.)
|
||||
//
|
||||
// We expect to need to conditionally enable just a few keybindings
|
||||
// depending on circumstance; the main hackiness here is that we are
|
||||
// assuming that keybindings have their default values; really we
|
||||
// should be asking Mutter to resolve the key into an action and then
|
||||
// base our handling based on the action.
|
||||
function _globalKeyPressHandler(actor, event) {
|
||||
if (modalCount == 0)
|
||||
return false;
|
||||
if (event.type() != Clutter.EventType.KEY_PRESS)
|
||||
if (event.type() != Clutter.EventType.KEY_RELEASE)
|
||||
return false;
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
let keyCode = event.get_key_code();
|
||||
let modifierState = Shell.get_event_state(event);
|
||||
// Check the overview key first, this isn't a Meta.KeyBindingAction yet
|
||||
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
|
||||
// The super key is the default for triggering the overview, and should
|
||||
// get us out of the overview when we are already in it.
|
||||
if (overview.visible)
|
||||
overview.hide();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Whitelist some of the Metacity actions
|
||||
let display = global.screen.get_display();
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
|
||||
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
||||
let action = display.get_keybinding_action(keyCode, modifierState);
|
||||
|
||||
// The screenshot action should always be available (even if a
|
||||
// modal dialog is present)
|
||||
if (action == Meta.KeyBindingAction.COMMAND_SCREENSHOT) {
|
||||
let gconf = GConf.Client.get_default();
|
||||
let command = gconf.get_string('/apps/metacity/keybinding_commands/command_screenshot');
|
||||
if (command != null && command != '')
|
||||
Util.spawnCommandLine(command);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Other bindings are only available when the overview is up and
|
||||
// no modal dialog is present.
|
||||
if (!overview.visible || modalCount > 1)
|
||||
return false;
|
||||
|
||||
// This isn't a Meta.KeyBindingAction yet
|
||||
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
|
||||
overview.hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
||||
// but that could be considered confusing; we also disable them in the main view.
|
||||
//
|
||||
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
|
||||
// wm.actionMoveWorkspaceLeft();
|
||||
// return true;
|
||||
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
|
||||
// wm.actionMoveWorkspaceRight();
|
||||
// return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_UP:
|
||||
wm.actionMoveWorkspaceUp();
|
||||
case Meta.KeyBindingAction.COMMAND_SCREENSHOT:
|
||||
let gconf = GConf.Client.get_default();
|
||||
let command = gconf.get_string('/apps/metacity/keybinding_commands/command_screenshot');
|
||||
if (command != null && command != '') {
|
||||
let [ok, len, args] = GLib.shell_parse_argv(command);
|
||||
let p = new Shell.Process({'args' : args});
|
||||
p.run();
|
||||
}
|
||||
return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_DOWN:
|
||||
wm.actionMoveWorkspaceDown();
|
||||
case Meta.KeyBindingAction.WORKSPACE_LEFT:
|
||||
wm.actionMoveWorkspaceLeft();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_RIGHT:
|
||||
wm.actionMoveWorkspaceRight();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
|
||||
case Meta.KeyBindingAction.COMMAND_2:
|
||||
getRunDialog().open();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
||||
overview.hide();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.SWITCH_PANELS:
|
||||
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -639,8 +336,10 @@ function _globalKeyPressHandler(actor, event) {
|
||||
|
||||
function _findModal(actor) {
|
||||
for (let i = 0; i < modalActorFocusStack.length; i++) {
|
||||
if (modalActorFocusStack[i].actor == actor)
|
||||
let [stackActor, stackFocus] = modalActorFocusStack[i];
|
||||
if (stackActor == actor) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -648,29 +347,21 @@ function _findModal(actor) {
|
||||
/**
|
||||
* pushModal:
|
||||
* @actor: #ClutterActor which will be given keyboard focus
|
||||
* @timestamp: optional timestamp
|
||||
*
|
||||
* Ensure we are in a mode where all keyboard and mouse input goes to
|
||||
* the stage, and focus @actor. Multiple calls to this function act in
|
||||
* a stacking fashion; the effect will be undone when an equal number
|
||||
* of popModal() invocations have been made.
|
||||
* the stage. Multiple calls to this function act in a stacking fashion;
|
||||
* the effect will be undone when an equal number of popModal() invocations
|
||||
* have been made.
|
||||
*
|
||||
* Next, record the current Clutter keyboard focus on a stack. If the
|
||||
* modal stack returns to this actor, reset the focus to the actor
|
||||
* which was focused at the time pushModal() was invoked.
|
||||
*
|
||||
* @timestamp is optionally used to associate the call with a specific user
|
||||
* initiated event. If not provided then the value of
|
||||
* global.get_current_time() is assumed.
|
||||
* Next, record the current Clutter keyboard focus on a stack. If the modal stack
|
||||
* returns to this actor, reset the focus to the actor which was focused
|
||||
* at the time pushModal() was invoked.
|
||||
*
|
||||
* Returns: true iff we successfully acquired a grab or already had one
|
||||
*/
|
||||
function pushModal(actor, timestamp) {
|
||||
if (timestamp == undefined)
|
||||
timestamp = global.get_current_time();
|
||||
|
||||
function pushModal(actor) {
|
||||
if (modalCount == 0) {
|
||||
if (!global.begin_modal(timestamp)) {
|
||||
if (!global.begin_modal(global.get_current_time())) {
|
||||
log('pushModal: invocation of begin_modal failed');
|
||||
return false;
|
||||
}
|
||||
@ -679,80 +370,52 @@ function pushModal(actor, timestamp) {
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
|
||||
|
||||
modalCount += 1;
|
||||
let actorDestroyId = actor.connect('destroy', function() {
|
||||
actor.connect('destroy', function() {
|
||||
let index = _findModal(actor);
|
||||
if (index >= 0)
|
||||
modalActorFocusStack.splice(index, 1);
|
||||
});
|
||||
let curFocus = global.stage.get_key_focus();
|
||||
let curFocusDestroyId;
|
||||
if (curFocus != null) {
|
||||
curFocusDestroyId = curFocus.connect('destroy', function() {
|
||||
curFocus.connect('destroy', function() {
|
||||
let index = _findModal(actor);
|
||||
if (index >= 0)
|
||||
modalActorFocusStack[index].actor = null;
|
||||
modalActorFocusStack[index][1] = null;
|
||||
});
|
||||
}
|
||||
modalActorFocusStack.push({ actor: actor,
|
||||
focus: curFocus,
|
||||
destroyId: actorDestroyId,
|
||||
focusDestroyId: curFocusDestroyId });
|
||||
modalActorFocusStack.push([actor, curFocus]);
|
||||
|
||||
global.stage.set_key_focus(actor);
|
||||
global.stage.set_key_focus(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* popModal:
|
||||
* @actor: #ClutterActor passed to original invocation of pushModal().
|
||||
* @timestamp: optional timestamp
|
||||
*
|
||||
* Reverse the effect of pushModal(). If this invocation is undoing
|
||||
* the topmost invocation, then the focus will be restored to the
|
||||
* previous focus at the time when pushModal() was invoked.
|
||||
*
|
||||
* @timestamp is optionally used to associate the call with a specific user
|
||||
* initiated event. If not provided then the value of
|
||||
* global.get_current_time() is assumed.
|
||||
*/
|
||||
function popModal(actor, timestamp) {
|
||||
if (timestamp == undefined)
|
||||
timestamp = global.get_current_time();
|
||||
|
||||
let focusIndex = _findModal(actor);
|
||||
if (focusIndex < 0) {
|
||||
global.stage.set_key_focus(null);
|
||||
global.end_modal(timestamp);
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
|
||||
throw new Error('incorrect pop');
|
||||
}
|
||||
|
||||
function popModal(actor) {
|
||||
modalCount -= 1;
|
||||
|
||||
let record = modalActorFocusStack[focusIndex];
|
||||
record.actor.disconnect(record.destroyId);
|
||||
|
||||
if (focusIndex == modalActorFocusStack.length - 1) {
|
||||
if (record.focus)
|
||||
record.focus.disconnect(record.focusDestroyId);
|
||||
global.stage.set_key_focus(record.focus);
|
||||
} else {
|
||||
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
|
||||
if (t.focus)
|
||||
t.focus.disconnect(t.focusDestroyId);
|
||||
// Remove from the middle, shift the focus chain up
|
||||
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
|
||||
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
|
||||
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
|
||||
let focusIndex = _findModal(actor);
|
||||
if (focusIndex >= 0) {
|
||||
if (focusIndex == modalActorFocusStack.length - 1) {
|
||||
let [stackActor, stackFocus] = modalActorFocusStack[focusIndex];
|
||||
global.stage.set_key_focus(stackFocus);
|
||||
} else {
|
||||
// Remove from the middle, shift the focus chain up
|
||||
for (let i = focusIndex; i < modalActorFocusStack.length - 1; i++) {
|
||||
modalActorFocusStack[i + 1][1] = modalActorFocusStack[i][1];
|
||||
}
|
||||
}
|
||||
modalActorFocusStack.splice(focusIndex, 1);
|
||||
}
|
||||
modalActorFocusStack.splice(focusIndex, 1);
|
||||
|
||||
if (modalCount > 0)
|
||||
return;
|
||||
|
||||
global.end_modal(timestamp);
|
||||
global.end_modal(global.get_current_time());
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
}
|
||||
|
||||
|
1500
js/ui/messageTray.js
@ -1,277 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Pango = imports.gi.Pango;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const OPEN_AND_CLOSE_TIME = 0.1;
|
||||
const FADE_OUT_DIALOG_TIME = 1.0;
|
||||
|
||||
const State = {
|
||||
OPENED: 0,
|
||||
CLOSED: 1,
|
||||
OPENING: 2,
|
||||
CLOSING: 3,
|
||||
FADED_OUT: 4
|
||||
};
|
||||
|
||||
function ModalDialog() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
ModalDialog.prototype = {
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { styleClass: null });
|
||||
|
||||
this.state = State.CLOSED;
|
||||
this._hasModal = false;
|
||||
|
||||
this._group = new St.Group({ visible: false,
|
||||
x: 0,
|
||||
y: 0 });
|
||||
Main.uiGroup.add_actor(this._group);
|
||||
|
||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||
coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE });
|
||||
this._group.add_constraint(constraint);
|
||||
|
||||
global.focus_manager.add_group(this._group);
|
||||
this._initialKeyFocus = this._group;
|
||||
this._savedKeyFocus = null;
|
||||
|
||||
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
||||
|
||||
this._actionKeys = {};
|
||||
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
|
||||
this._lightbox = new Lightbox.Lightbox(this._group,
|
||||
{ inhibitEvents: true });
|
||||
|
||||
this._backgroundBin = new St.Bin();
|
||||
|
||||
this._group.add_actor(this._backgroundBin);
|
||||
this._lightbox.highlight(this._backgroundBin);
|
||||
|
||||
this._backgroundStack = new Shell.Stack();
|
||||
this._backgroundBin.child = this._backgroundStack;
|
||||
|
||||
this._eventBlocker = new Clutter.Group({ reactive: true });
|
||||
this._backgroundStack.add_actor(this._eventBlocker);
|
||||
|
||||
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||
vertical: true });
|
||||
if (params.styleClass != null) {
|
||||
this._dialogLayout.add_style_class_name(params.styleClass);
|
||||
}
|
||||
this._backgroundStack.add_actor(this._dialogLayout);
|
||||
|
||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||
this._dialogLayout.add(this.contentLayout,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||
opacity: 220,
|
||||
vertical: false });
|
||||
this._dialogLayout.add(this._buttonLayout,
|
||||
{ expand: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.END });
|
||||
},
|
||||
|
||||
setButtons: function(buttons) {
|
||||
this._buttonLayout.destroy_children();
|
||||
this._actionKeys = {};
|
||||
|
||||
let i = 0;
|
||||
for (let index in buttons) {
|
||||
let buttonInfo = buttons[index];
|
||||
let label = buttonInfo['label'];
|
||||
let action = buttonInfo['action'];
|
||||
let key = buttonInfo['key'];
|
||||
|
||||
let button = new St.Button({ style_class: 'modal-dialog-button',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
label: label });
|
||||
|
||||
let x_alignment;
|
||||
if (buttons.length == 1)
|
||||
x_alignment = St.Align.END;
|
||||
else if (i == 0)
|
||||
x_alignment = St.Align.START;
|
||||
else if (i == buttons.length - 1)
|
||||
x_alignment = St.Align.END;
|
||||
else
|
||||
x_alignment = St.Align.MIDDLE;
|
||||
|
||||
this._initialKeyFocus = button;
|
||||
this._buttonLayout.add(button,
|
||||
{ expand: true,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: x_alignment,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
button.connect('clicked', action);
|
||||
|
||||
if (key)
|
||||
this._actionKeys[key] = action;
|
||||
i++;
|
||||
}
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(object, keyPressEvent) {
|
||||
let symbol = keyPressEvent.get_key_symbol();
|
||||
let action = this._actionKeys[symbol];
|
||||
|
||||
if (action)
|
||||
action();
|
||||
},
|
||||
|
||||
_onGroupDestroy: function() {
|
||||
this.emit('destroy');
|
||||
},
|
||||
|
||||
_fadeOpen: function() {
|
||||
let monitor = global.get_focus_monitor();
|
||||
|
||||
this._backgroundBin.set_position(monitor.x, monitor.y);
|
||||
this._backgroundBin.set_size(monitor.width, monitor.height);
|
||||
|
||||
this.state = State.OPENING;
|
||||
|
||||
this._dialogLayout.opacity = 255;
|
||||
this._lightbox.show();
|
||||
this._group.opacity = 0;
|
||||
this._group.show();
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 255,
|
||||
time: OPEN_AND_CLOSE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.state = State.OPENED;
|
||||
this.emit('opened');
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
setInitialKeyFocus: function(actor) {
|
||||
this._initialKeyFocus = actor;
|
||||
},
|
||||
|
||||
open: function(timestamp) {
|
||||
if (this.state == State.OPENED || this.state == State.OPENING)
|
||||
return true;
|
||||
|
||||
if (!this.pushModal(timestamp))
|
||||
return false;
|
||||
|
||||
this._fadeOpen();
|
||||
return true;
|
||||
},
|
||||
|
||||
close: function(timestamp) {
|
||||
if (this.state == State.CLOSED || this.state == State.CLOSING)
|
||||
return;
|
||||
|
||||
this.state = State.CLOSING;
|
||||
this.popModal(timestamp);
|
||||
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
time: OPEN_AND_CLOSE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.state = State.CLOSED;
|
||||
this._group.hide();
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
// Drop modal status without closing the dialog; this makes the
|
||||
// dialog insensitive as well, so it needs to be followed shortly
|
||||
// by either a close() or a pushModal()
|
||||
popModal: function(timestamp) {
|
||||
if (!this._hasModal)
|
||||
return;
|
||||
|
||||
let focus = global.stage.key_focus;
|
||||
if (focus && this._group.contains(focus))
|
||||
this._savedKeyFocus = focus;
|
||||
else
|
||||
this._savedKeyFocus = null;
|
||||
Main.popModal(this._group, timestamp);
|
||||
global.gdk_screen.get_display().sync();
|
||||
this._hasModal = false;
|
||||
|
||||
this._eventBlocker.raise_top();
|
||||
},
|
||||
|
||||
pushModal: function (timestamp) {
|
||||
if (this._hasModal)
|
||||
return true;
|
||||
if (!Main.pushModal(this._group, timestamp))
|
||||
return false;
|
||||
|
||||
this._hasModal = true;
|
||||
if (this._savedKeyFocus) {
|
||||
this._savedKeyFocus.grab_key_focus();
|
||||
this._savedKeyFocus = null;
|
||||
} else
|
||||
this._initialKeyFocus.grab_key_focus();
|
||||
|
||||
this._eventBlocker.lower_bottom();
|
||||
return true;
|
||||
},
|
||||
|
||||
// This method is like close, but fades the dialog out much slower,
|
||||
// and leaves the lightbox in place. Once in the faded out state,
|
||||
// the dialog can be brought back by an open call, or the lightbox
|
||||
// can be dismissed by a close call.
|
||||
//
|
||||
// The main point of this method is to give some indication to the user
|
||||
// that the dialog reponse has been acknowledged but will take a few
|
||||
// moments before being processed.
|
||||
// e.g., if a user clicked "Log Out" then the dialog should go away
|
||||
// imediately, but the lightbox should remain until the logout is
|
||||
// complete.
|
||||
_fadeOutDialog: function(timestamp) {
|
||||
if (this.state == State.CLOSED || this.state == State.CLOSING)
|
||||
return;
|
||||
|
||||
if (this.state == State.FADED_OUT)
|
||||
return;
|
||||
|
||||
this.popModal(timestamp);
|
||||
Tweener.addTween(this._dialogLayout,
|
||||
{ opacity: 0,
|
||||
time: FADE_OUT_DIALOG_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.state = State.FADED_OUT;
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ModalDialog.prototype);
|
@ -1,6 +1,5 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const DBus = imports.dbus;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
@ -10,11 +9,9 @@ const St = imports.gi.St;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Params = imports.misc.params;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
let nextNotificationId = 1;
|
||||
|
||||
@ -78,11 +75,11 @@ const Urgency = {
|
||||
const rewriteRules = {
|
||||
'XChat': [
|
||||
{ pattern: /^XChat: Private message from: (\S*) \(.*\)$/,
|
||||
replacement: '<$1>' },
|
||||
replacement: '<$1>' },
|
||||
{ pattern: /^XChat: New public message from: (\S*) \((.*)\)$/,
|
||||
replacement: '$2 <$1>' },
|
||||
replacement: '$2 <$1>' },
|
||||
{ pattern: /^XChat: Highlighted message from: (\S*) \((.*)\)$/,
|
||||
replacement: '$2 <$1>' }
|
||||
replacement: '$2 <$1>' }
|
||||
]
|
||||
};
|
||||
|
||||
@ -94,6 +91,16 @@ NotificationDaemon.prototype = {
|
||||
_init: function() {
|
||||
DBus.session.exportObject('/org/freedesktop/Notifications', this);
|
||||
|
||||
this._everAcquiredName = false;
|
||||
DBus.session.acquire_name('org.freedesktop.Notifications',
|
||||
// We pass MANY_INSTANCES so that if
|
||||
// notification-daemon is running, we'll
|
||||
// get queued behind it and then get the
|
||||
// name after killing it below
|
||||
DBus.MANY_INSTANCES,
|
||||
Lang.bind(this, this._acquiredName),
|
||||
Lang.bind(this, this._lostName));
|
||||
|
||||
this._sources = {};
|
||||
this._senderToPid = {};
|
||||
this._notifications = {};
|
||||
@ -108,6 +115,31 @@ NotificationDaemon.prototype = {
|
||||
Lang.bind(this, this._onFocusAppChanged));
|
||||
},
|
||||
|
||||
_acquiredName: function() {
|
||||
this._everAcquiredName = true;
|
||||
},
|
||||
|
||||
_lostName: function() {
|
||||
if (this._everAcquiredName)
|
||||
log('Lost name org.freedesktop.Notifications!');
|
||||
else if (GLib.getenv('GNOME_SHELL_NO_REPLACE'))
|
||||
log('Failed to acquire org.freedesktop.Notifications');
|
||||
else {
|
||||
log('Failed to acquire org.freedesktop.Notifications; trying again');
|
||||
|
||||
// kill the notification-daemon. pkill is more portable
|
||||
// than killall, but on Linux at least it won't match if
|
||||
// you pass more than 15 characters of the process name...
|
||||
// However, if you use the '-f' flag to match the entire
|
||||
// command line, it will work, but we have to be careful
|
||||
// in that case that we don't match 'gedit
|
||||
// notification-daemon.c' or whatever...
|
||||
let p = new Shell.Process({ args: ['pkill', '-f',
|
||||
'^([^ ]*/)?(notification-daemon|notify-osd)$']});
|
||||
p.run();
|
||||
}
|
||||
},
|
||||
|
||||
_iconForNotificationData: function(icon, hints, size) {
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
|
||||
@ -121,9 +153,9 @@ NotificationDaemon.prototype = {
|
||||
return new St.Icon({ icon_name: icon,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: size });
|
||||
} else if (hints['image-data']) {
|
||||
} else if (hints.icon_data) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||
bitsPerSample, nChannels, data] = hints.icon_data;
|
||||
return textureCache.load_from_raw(data, data.length, hasAlpha,
|
||||
width, height, rowStride, size);
|
||||
} else {
|
||||
@ -143,43 +175,14 @@ NotificationDaemon.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
// Returns the source associated with ndata.notification if it is set.
|
||||
// Otherwise, returns the source associated with the pid if one is
|
||||
// stored in this._sources and the notification is not transient.
|
||||
// Otherwise, creates a new source as long as pid is provided.
|
||||
//
|
||||
// Either a pid or ndata.notification is needed to retrieve or
|
||||
// create a source.
|
||||
_getSource: function(title, pid, ndata) {
|
||||
if (!pid && !(ndata && ndata.notification))
|
||||
return null;
|
||||
|
||||
// We use notification's source for the notifications we still have
|
||||
// around that are getting replaced because we don't keep sources
|
||||
// for transient notifications in this._sources, but we still want
|
||||
// the notification associated with them to get replaced correctly.
|
||||
if (ndata && ndata.notification)
|
||||
return ndata.notification.source;
|
||||
|
||||
let isForTransientNotification = (ndata && ndata.hints['transient'] == true);
|
||||
|
||||
// We don't want to override a persistent notification
|
||||
// with a transient one from the same sender, so we
|
||||
// always create a new source object for new transient notifications
|
||||
// and never add it to this._sources .
|
||||
if (!isForTransientNotification && this._sources[pid])
|
||||
return this._sources[pid];
|
||||
|
||||
_newSource: function(title, pid) {
|
||||
let source = new Source(title, pid);
|
||||
source.setTransient(isForTransientNotification);
|
||||
this._sources[pid] = source;
|
||||
|
||||
if (!isForTransientNotification) {
|
||||
this._sources[pid] = source;
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
delete this._sources[pid];
|
||||
}));
|
||||
}
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
delete this._sources[pid];
|
||||
}));
|
||||
|
||||
Main.messageTray.add(source);
|
||||
return source;
|
||||
@ -202,6 +205,8 @@ NotificationDaemon.prototype = {
|
||||
return id;
|
||||
}
|
||||
|
||||
summary = GLib.markup_escape_text(summary, -1);
|
||||
|
||||
let rewrites = rewriteRules[appName];
|
||||
if (rewrites) {
|
||||
for (let i = 0; i < rewrites.length; i++) {
|
||||
@ -213,15 +218,6 @@ NotificationDaemon.prototype = {
|
||||
|
||||
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
||||
|
||||
// Be compatible with the various hints for image data
|
||||
// 'image-data' is the latest name of this hint, introduced in 1.2
|
||||
if (!hints['image-data']) {
|
||||
if (hints['image_data'])
|
||||
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
|
||||
else if (hints['icon_data'])
|
||||
hints['image-data'] = hints['icon_data']; // previous versions of the spec
|
||||
}
|
||||
|
||||
let ndata = { appName: appName,
|
||||
icon: icon,
|
||||
summary: summary,
|
||||
@ -240,8 +236,7 @@ NotificationDaemon.prototype = {
|
||||
|
||||
let sender = DBus.getCurrentMessageContext().sender;
|
||||
let pid = this._senderToPid[sender];
|
||||
|
||||
let source = this._getSource(appName, pid, ndata);
|
||||
let source = pid ? this._sources[pid] : null;
|
||||
|
||||
if (source) {
|
||||
this._notifyForSource(source, ndata);
|
||||
@ -262,23 +257,16 @@ NotificationDaemon.prototype = {
|
||||
if (!ndata)
|
||||
return;
|
||||
|
||||
source = this._getSource(appName, pid, ndata);
|
||||
this._senderToPid[sender] = pid;
|
||||
source = this._sources[pid];
|
||||
|
||||
if (!source)
|
||||
source = this._newSource(appName, pid);
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
delete this._senderToPid[sender];
|
||||
}));
|
||||
|
||||
// We only store sender-pid entries for persistent sources.
|
||||
// Removing the entries once the source is destroyed
|
||||
// would result in the entries associated with transient
|
||||
// sources removed once the notification is shown anyway.
|
||||
// However, keeping these pairs would mean that we would
|
||||
// possibly remove an entry associated with a persistent
|
||||
// source when a transient source for the same sender is
|
||||
// distroyed.
|
||||
if (!source.isTransient) {
|
||||
this._senderToPid[sender] = pid;
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
delete this._senderToPid[sender];
|
||||
}));
|
||||
}
|
||||
this._notifyForSource(source, ndata);
|
||||
}));
|
||||
|
||||
@ -293,34 +281,19 @@ NotificationDaemon.prototype = {
|
||||
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
||||
|
||||
if (notification == null) {
|
||||
notification = new MessageTray.Notification(source, summary, body,
|
||||
{ icon: iconActor,
|
||||
bannerMarkup: true });
|
||||
notification = new MessageTray.Notification(source, summary, body, { icon: iconActor });
|
||||
ndata.notification = notification;
|
||||
notification.connect('clicked', Lang.bind(this,
|
||||
function(n) {
|
||||
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
||||
}));
|
||||
notification.connect('destroy', Lang.bind(this,
|
||||
function(n, reason) {
|
||||
function(n) {
|
||||
delete this._notifications[id];
|
||||
let notificationClosedReason;
|
||||
switch (reason) {
|
||||
case MessageTray.NotificationDestroyedReason.EXPIRED:
|
||||
notificationClosedReason = NotificationClosedReason.EXPIRED;
|
||||
break;
|
||||
case MessageTray.NotificationDestroyedReason.DISMISSED:
|
||||
notificationClosedReason = NotificationClosedReason.DISMISSED;
|
||||
break;
|
||||
case MessageTray.NotificationDestroyedReason.SOURCE_CLOSED:
|
||||
notificationClosedReason = NotificationClosedReason.APP_CLOSED;
|
||||
break;
|
||||
}
|
||||
this._emitNotificationClosed(id, notificationClosedReason);
|
||||
}));
|
||||
notification.connect('action-invoked', Lang.bind(this,
|
||||
function(n, actionId) {
|
||||
this._emitActionInvoked(id, actionId);
|
||||
}));
|
||||
notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
|
||||
} else {
|
||||
notification.update(summary, body, { icon: iconActor,
|
||||
bannerMarkup: true,
|
||||
clear: true });
|
||||
}
|
||||
|
||||
@ -329,33 +302,21 @@ NotificationDaemon.prototype = {
|
||||
for (let i = 0; i < actions.length - 1; i += 2)
|
||||
notification.addButton(actions[i], actions[i + 1]);
|
||||
}
|
||||
switch (hints.urgency) {
|
||||
case Urgency.LOW:
|
||||
notification.setUrgency(MessageTray.Urgency.LOW);
|
||||
break;
|
||||
case Urgency.NORMAL:
|
||||
notification.setUrgency(MessageTray.Urgency.NORMAL);
|
||||
break;
|
||||
case Urgency.CRITICAL:
|
||||
notification.setUrgency(MessageTray.Urgency.CRITICAL);
|
||||
break;
|
||||
}
|
||||
notification.setResident(hints.resident == true);
|
||||
// 'transient' is a reserved keyword in JS, so we have to retrieve the value
|
||||
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
||||
notification.setTransient(hints['transient'] == true);
|
||||
|
||||
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
|
||||
|
||||
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
|
||||
source.processNotification(notification, sourceIconActor);
|
||||
source.notify(notification, sourceIconActor);
|
||||
},
|
||||
|
||||
CloseNotification: function(id) {
|
||||
let ndata = this._notifications[id];
|
||||
if (ndata) {
|
||||
if (ndata.notification)
|
||||
ndata.notification.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
|
||||
ndata.notification.destroy();
|
||||
delete this._notifications[id];
|
||||
}
|
||||
this._emitNotificationClosed(id, NotificationClosedReason.APP_CLOSED);
|
||||
},
|
||||
|
||||
GetCapabilities: function() {
|
||||
@ -375,10 +336,10 @@ NotificationDaemon.prototype = {
|
||||
|
||||
GetServerInformation: function() {
|
||||
return [
|
||||
Config.PACKAGE_NAME,
|
||||
'GNOME Shell',
|
||||
'GNOME',
|
||||
Config.PACKAGE_VERSION,
|
||||
'1.2'
|
||||
'0.1', // FIXME, get this from somewhere
|
||||
'1.0'
|
||||
];
|
||||
},
|
||||
|
||||
@ -390,12 +351,17 @@ NotificationDaemon.prototype = {
|
||||
for (let id in this._sources) {
|
||||
let source = this._sources[id];
|
||||
if (source.app == tracker.focus_app) {
|
||||
source.destroyNonResidentNotifications();
|
||||
source.activated();
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_actionInvoked: function(notification, action, source, id) {
|
||||
source.activated();
|
||||
this._emitActionInvoked(id, action);
|
||||
},
|
||||
|
||||
_emitNotificationClosed: function(id, reason) {
|
||||
DBus.session.emit_signal('/org/freedesktop/Notifications',
|
||||
'org.freedesktop.Notifications',
|
||||
@ -411,7 +377,9 @@ NotificationDaemon.prototype = {
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon) {
|
||||
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null);
|
||||
let source = this._sources[icon.pid];
|
||||
if (!source)
|
||||
source = this._newSource(icon.title || icon.wm_class || _("Unknown"), icon.pid);
|
||||
source.setTrayIcon(icon);
|
||||
},
|
||||
|
||||
@ -435,74 +403,30 @@ Source.prototype = {
|
||||
MessageTray.Source.prototype._init.call(this, title);
|
||||
|
||||
this._pid = pid;
|
||||
this._appStateChangedId = 0;
|
||||
this._setApp();
|
||||
if (this.app)
|
||||
this.title = this.app.get_name();
|
||||
else
|
||||
this.useNotificationIcon = true;
|
||||
this._trayIcon = null;
|
||||
this._isTrayIcon = false;
|
||||
},
|
||||
|
||||
processNotification: function(notification, icon) {
|
||||
notify: function(notification, icon) {
|
||||
if (!this.app)
|
||||
this._setApp();
|
||||
if (!this.app && icon)
|
||||
this._setSummaryIcon(icon);
|
||||
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
if (notification.resident && this.app && tracker.focus_app == this.app)
|
||||
this.pushNotification(notification);
|
||||
else
|
||||
this.notify(notification);
|
||||
},
|
||||
|
||||
handleSummaryClick: function() {
|
||||
if (!this._trayIcon)
|
||||
return false;
|
||||
|
||||
let event = Clutter.get_current_event();
|
||||
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
||||
return false;
|
||||
|
||||
// Left clicks are passed through only where there aren't unacknowledged
|
||||
// notifications, so it possible to open them in summary mode; right
|
||||
// clicks are always forwarded, as the right click menu is not useful for
|
||||
// tray icons
|
||||
if (event.get_button() == 1 &&
|
||||
this.notifications.length > 0)
|
||||
return false;
|
||||
|
||||
if (Main.overview.visible) {
|
||||
// We can't just connect to Main.overview's 'hidden' signal,
|
||||
// because it's emitted *before* it calls popModal()...
|
||||
let id = global.connect('notify::stage-input-mode', Lang.bind(this,
|
||||
function () {
|
||||
global.disconnect(id);
|
||||
this._trayIcon.click(event);
|
||||
}));
|
||||
Main.overview.hide();
|
||||
} else {
|
||||
this._trayIcon.click(event);
|
||||
}
|
||||
return true;
|
||||
MessageTray.Source.prototype.notify.call(this, notification);
|
||||
},
|
||||
|
||||
_setApp: function() {
|
||||
if (this.app)
|
||||
return;
|
||||
|
||||
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this._pid);
|
||||
if (!this.app)
|
||||
return;
|
||||
|
||||
// We only update the app if this.app is null, so we can't disconnect the old this._appStateChangedId
|
||||
// even if it were non-zero for some reason.
|
||||
this._appStateChangedId = this.app.connect('notify::state', Lang.bind(this, this._appStateChanged));
|
||||
|
||||
// Only override the icon if we were previously using
|
||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
||||
if (!this._trayIcon) {
|
||||
// notification-based icons (ie, not a trayicon)
|
||||
if (this.useNotificationIcon) {
|
||||
this.useNotificationIcon = false;
|
||||
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
|
||||
}
|
||||
@ -511,29 +435,17 @@ Source.prototype = {
|
||||
setTrayIcon: function(icon) {
|
||||
this._setSummaryIcon(icon);
|
||||
this.useNotificationIcon = false;
|
||||
this._trayIcon = icon;
|
||||
this._isTrayIcon = true;
|
||||
},
|
||||
|
||||
open: function(notification) {
|
||||
this.destroyNonResidentNotifications();
|
||||
_notificationClicked: function(notification) {
|
||||
notification.destroy();
|
||||
this.openApp();
|
||||
this.activated();
|
||||
},
|
||||
|
||||
_lastNotificationRemoved: function() {
|
||||
if (!this._trayIcon)
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_appStateChanged: function() {
|
||||
// Destroy notification sources when their apps exit.
|
||||
// The app exiting would normally result in a tray icon being removed,
|
||||
// so the associated source would be destroyed through the code path
|
||||
// that handles the tray icon being removed. We should not destroy
|
||||
// the source associated with a tray icon when the application state
|
||||
// is Shell.AppState.STOPPED because running applications that have
|
||||
// no open windows would also have that state. This is often the case
|
||||
// for applications that use tray icons.
|
||||
if (!this._trayIcon && this.app.get_state() == Shell.AppState.STOPPED)
|
||||
activated: function() {
|
||||
if (!this._isTrayIcon)
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
@ -546,13 +458,5 @@ Source.prototype = {
|
||||
let mostRecentWindow = windows[0];
|
||||
Main.activateWindow(mostRecentWindow);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.app && this._appStateChangedId) {
|
||||
this.app.disconnect(this._appStateChangedId);
|
||||
this._appStateChangedId = 0;
|
||||
}
|
||||
MessageTray.Source.prototype.destroy.call(this);
|
||||
}
|
||||
};
|
||||
|
@ -1,21 +1,18 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const Dash = imports.ui.dash;
|
||||
const DND = imports.ui.dnd;
|
||||
const DocDisplay = imports.ui.docDisplay;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
@ -24,7 +21,6 @@ const PlaceDisplay = imports.ui.placeDisplay;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const ViewSelector = imports.ui.viewSelector;
|
||||
const WorkspacesView = imports.ui.workspacesView;
|
||||
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||
|
||||
// Time for initial animation going into Overview mode
|
||||
const ANIMATION_TIME = 0.25;
|
||||
@ -32,19 +28,31 @@ const ANIMATION_TIME = 0.25;
|
||||
// We split the screen vertically between the dash and the view selector.
|
||||
const DASH_SPLIT_FRACTION = 0.1;
|
||||
|
||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
|
||||
const SHELL_INFO_HIDE_TIMEOUT = 10;
|
||||
|
||||
const SwipeScrollDirection = {
|
||||
NONE: 0,
|
||||
HORIZONTAL: 1,
|
||||
VERTICAL: 2
|
||||
};
|
||||
function Source() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
const SwipeScrollResult = {
|
||||
CANCEL: 0,
|
||||
SWIPE: 1,
|
||||
CLICK: 2
|
||||
};
|
||||
Source.prototype = {
|
||||
__proto__: MessageTray.Source.prototype,
|
||||
|
||||
_init: function() {
|
||||
MessageTray.Source.prototype._init.call(this,
|
||||
"System Information");
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ icon_name: 'info',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: this.ICON_SIZE });
|
||||
},
|
||||
|
||||
_notificationClicked: function() {
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
function ShellInfo() {
|
||||
this._init();
|
||||
@ -53,10 +61,14 @@ function ShellInfo() {
|
||||
ShellInfo.prototype = {
|
||||
_init: function() {
|
||||
this._source = null;
|
||||
this._timeoutId = 0;
|
||||
this._undoCallback = null;
|
||||
},
|
||||
|
||||
_onUndoClicked: function() {
|
||||
Mainloop.source_remove(this._timeoutId);
|
||||
this._timeoutId = 0;
|
||||
|
||||
if (this._undoCallback)
|
||||
this._undoCallback();
|
||||
this._undoCallback = null;
|
||||
@ -65,9 +77,22 @@ ShellInfo.prototype = {
|
||||
this._source.destroy();
|
||||
},
|
||||
|
||||
_onTimeout: function() {
|
||||
this._timeoutId = 0;
|
||||
if (this._source)
|
||||
this._source.destroy();
|
||||
return false;
|
||||
},
|
||||
|
||||
setMessage: function(text, undoCallback, undoLabel) {
|
||||
if (this._timeoutId)
|
||||
Mainloop.source_remove(this._timeoutId);
|
||||
|
||||
this._timeoutId = Mainloop.timeout_add_seconds(SHELL_INFO_HIDE_TIMEOUT,
|
||||
Lang.bind(this, this._onTimeout));
|
||||
|
||||
if (this._source == null) {
|
||||
this._source = new MessageTray.SystemNotificationSource();
|
||||
this._source = new Source();
|
||||
this._source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
this._source = null;
|
||||
@ -75,15 +100,11 @@ ShellInfo.prototype = {
|
||||
Main.messageTray.add(this._source);
|
||||
}
|
||||
|
||||
let notification = null;
|
||||
if (this._source.notifications.length == 0) {
|
||||
let notification = this._source.notification;
|
||||
if (notification == null)
|
||||
notification = new MessageTray.Notification(this._source, text, null);
|
||||
} else {
|
||||
notification = this._source.notifications[0];
|
||||
else
|
||||
notification.update(text, null, { clear: true });
|
||||
}
|
||||
|
||||
notification.setTransient(true);
|
||||
|
||||
this._undoCallback = undoCallback;
|
||||
if (undoCallback) {
|
||||
@ -103,19 +124,18 @@ function Overview() {
|
||||
|
||||
Overview.prototype = {
|
||||
_init : function() {
|
||||
this._desktopFade = new St.Bin();
|
||||
global.overlay_group.add_actor(this._desktopFade);
|
||||
|
||||
// The actual global.background_actor is inside global.window_group,
|
||||
// which is hidden when displaying the overview, so we display a clone.
|
||||
this._background = new Clutter.Clone({ source: global.background_actor });
|
||||
this._background.hide();
|
||||
global.overlay_group.add_actor(this._background);
|
||||
|
||||
this._desktopFade = new St.Bin();
|
||||
global.overlay_group.add_actor(this._desktopFade);
|
||||
|
||||
this._spacing = 0;
|
||||
|
||||
this._group = new St.Group({ name: 'overview',
|
||||
reactive: true });
|
||||
this._group = new St.Group({ name: 'overview' });
|
||||
this._group._delegate = this;
|
||||
this._group.connect('style-changed',
|
||||
Lang.bind(this, function() {
|
||||
@ -127,17 +147,11 @@ Overview.prototype = {
|
||||
}
|
||||
}));
|
||||
|
||||
this._scrollDirection = SwipeScrollDirection.NONE;
|
||||
this._scrollAdjustment = null;
|
||||
this._capturedEventId = 0;
|
||||
this._buttonPressId = 0;
|
||||
this.shellInfo = new ShellInfo();
|
||||
|
||||
this._workspacesDisplay = null;
|
||||
|
||||
this.visible = false; // animating to overview, in overview, animating out
|
||||
this._shown = false; // show() and not hide()
|
||||
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
|
||||
this._modal = false; // have a modal grab
|
||||
this.visible = false;
|
||||
this.animationInProgress = false;
|
||||
this._hideInProgress = false;
|
||||
|
||||
@ -153,276 +167,31 @@ Overview.prototype = {
|
||||
this._group.hide();
|
||||
global.overlay_group.add_actor(this._group);
|
||||
|
||||
this._coverPane.hide();
|
||||
|
||||
// XDND
|
||||
this._dragMonitor = {
|
||||
dragMotion: Lang.bind(this, this._onDragMotion)
|
||||
};
|
||||
|
||||
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
|
||||
this._windowSwitchTimeoutId = 0;
|
||||
this._windowSwitchTimestamp = 0;
|
||||
this._lastActiveWorkspaceIndex = -1;
|
||||
this._lastHoveredWindow = null;
|
||||
this._needsFakePointerEvent = false;
|
||||
|
||||
this.workspaces = null;
|
||||
},
|
||||
|
||||
// The members we construct that are implemented in JS might
|
||||
// want to access the overview as Main.overview to connect
|
||||
// signal handlers and so forth. So we create them after
|
||||
// construction in this init() method.
|
||||
init: function() {
|
||||
this.shellInfo = new ShellInfo();
|
||||
|
||||
this.viewSelector = new ViewSelector.ViewSelector();
|
||||
this._group.add_actor(this.viewSelector.actor);
|
||||
|
||||
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
||||
this.viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
|
||||
this.viewSelector.addViewTab("Windows", this._workspacesDisplay.actor);
|
||||
|
||||
let appView = new AppDisplay.AllAppDisplay();
|
||||
this.viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
|
||||
this.viewSelector.addViewTab("Applications", appView.actor);
|
||||
|
||||
// Default search providers
|
||||
this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||
this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
|
||||
this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
|
||||
this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
|
||||
this.viewSelector.addSearchProvider(new WorkspacesView.WindowSearchProvider());
|
||||
|
||||
// TODO - recalculate everything when desktop size changes
|
||||
this.dash = new Dash.Dash();
|
||||
this._group.add_actor(this.dash.actor);
|
||||
this.dash.actor.add_constraint(this.viewSelector.constrainY);
|
||||
this.dash.actor.add_constraint(this.viewSelector.constrainHeight);
|
||||
this._dash = new Dash.Dash();
|
||||
this._group.add_actor(this._dash.actor);
|
||||
this._dash.actor.add_constraint(this.viewSelector.constrainY);
|
||||
this._dash.actor.add_constraint(this.viewSelector.constrainHeight);
|
||||
|
||||
// Translators: this is the name of the dock/favorites area on
|
||||
// the left of the overview
|
||||
Main.ctrlAltTabManager.addGroup(this.dash.actor, _("Dash"), 'user-bookmarks');
|
||||
this._coverPane.lower_bottom();
|
||||
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
DND.addDragMonitor(this._dragMonitor);
|
||||
// Remember the workspace we started from
|
||||
this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
},
|
||||
|
||||
_onDragEnd: function(time) {
|
||||
// In case the drag was canceled while in the overview
|
||||
// we have to go back to where we started and hide
|
||||
// the overview
|
||||
if (this._shownTemporarily) {
|
||||
global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
|
||||
this.hideTemporarily();
|
||||
}
|
||||
this._resetWindowSwitchTimeout();
|
||||
this._lastHoveredWindow = null;
|
||||
DND.removeMonitor(this._dragMonitor);
|
||||
this.endItemDrag();
|
||||
},
|
||||
|
||||
_resetWindowSwitchTimeout: function() {
|
||||
if (this._windowSwitchTimeoutId != 0) {
|
||||
Mainloop.source_remove(this._windowSwitchTimeoutId);
|
||||
this._windowSwitchTimeoutId = 0;
|
||||
this._needsFakePointerEvent = false;
|
||||
}
|
||||
},
|
||||
|
||||
_fakePointerEvent: function() {
|
||||
let display = Gdk.Display.get_default();
|
||||
let deviceManager = display.get_device_manager();
|
||||
let pointer = deviceManager.get_client_pointer();
|
||||
let [screen, pointerX, pointerY] = pointer.get_position();
|
||||
|
||||
pointer.warp(screen, pointerX, pointerY);
|
||||
},
|
||||
|
||||
_onDragMotion: function(dragEvent) {
|
||||
let targetIsWindow = dragEvent.targetActor &&
|
||||
dragEvent.targetActor._delegate &&
|
||||
dragEvent.targetActor._delegate.metaWindow &&
|
||||
!(dragEvent.targetActor._delegate instanceof WorkspaceThumbnail.WindowClone);
|
||||
|
||||
this._windowSwitchTimestamp = global.get_current_time();
|
||||
|
||||
if (targetIsWindow &&
|
||||
dragEvent.targetActor._delegate.metaWindow == this._lastHoveredWindow)
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
this._lastHoveredWindow = null;
|
||||
|
||||
this._resetWindowSwitchTimeout();
|
||||
|
||||
if (targetIsWindow) {
|
||||
this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
|
||||
this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
|
||||
Lang.bind(this, function() {
|
||||
this._needsFakePointerEvent = true;
|
||||
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
|
||||
this._windowSwitchTimestamp);
|
||||
this.hideTemporarily();
|
||||
this._lastHoveredWindow = null;
|
||||
}));
|
||||
}
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
setScrollAdjustment: function(adjustment, direction) {
|
||||
this._scrollAdjustment = adjustment;
|
||||
if (this._scrollAdjustment == null)
|
||||
this._scrollDirection = SwipeScrollDirection.NONE;
|
||||
else
|
||||
this._scrollDirection = direction;
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
if (this._scrollDirection == SwipeScrollDirection.NONE)
|
||||
return;
|
||||
|
||||
let [stageX, stageY] = event.get_coords();
|
||||
this._dragStartX = this._dragX = stageX;
|
||||
this._dragStartY = this._dragY = stageY;
|
||||
this._dragStartValue = this._scrollAdjustment.value;
|
||||
this._lastMotionTime = -1; // used to track "stopping" while swipe-scrolling
|
||||
this._capturedEventId = global.stage.connect('captured-event',
|
||||
Lang.bind(this, this._onCapturedEvent));
|
||||
this.emit('swipe-scroll-begin');
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let stageX, stageY;
|
||||
let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
|
||||
|
||||
switch(event.type()) {
|
||||
case Clutter.EventType.BUTTON_RELEASE:
|
||||
[stageX, stageY] = event.get_coords();
|
||||
|
||||
// default to snapping back to the original value
|
||||
let newValue = this._dragStartValue;
|
||||
|
||||
let minValue = this._scrollAdjustment.lower;
|
||||
let maxValue = this._scrollAdjustment.upper - this._scrollAdjustment.page_size;
|
||||
|
||||
let direction;
|
||||
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
|
||||
direction = stageX > this._dragStartX ? -1 : 1;
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
direction *= -1;
|
||||
} else {
|
||||
direction = stageY > this._dragStartY ? -1 : 1;
|
||||
}
|
||||
|
||||
// We default to scroll a full page size; both the first
|
||||
// and the last page may be smaller though, so we need to
|
||||
// adjust difference in those cases.
|
||||
let difference = direction * this._scrollAdjustment.page_size;
|
||||
if (this._dragStartValue + difference > maxValue)
|
||||
difference = maxValue - this._dragStartValue;
|
||||
else if (this._dragStartValue + difference < minValue)
|
||||
difference = minValue - this._dragStartValue;
|
||||
|
||||
// If the user has moved more than half the scroll
|
||||
// difference, we want to "settle" to the new value
|
||||
// even if the user stops dragging rather "throws" by
|
||||
// releasing during the drag.
|
||||
let distance = this._dragStartValue - this._scrollAdjustment.value;
|
||||
let noStop = Math.abs(distance / difference) > 0.5;
|
||||
|
||||
// We detect if the user is stopped by comparing the
|
||||
// timestamp of the button release with the timestamp of
|
||||
// the last motion. Experimentally, a difference of 0 or 1
|
||||
// millisecond indicates that the mouse is in motion, a
|
||||
// larger difference indicates that the mouse is stopped.
|
||||
if ((this._lastMotionTime > 0 &&
|
||||
this._lastMotionTime > event.get_time() - 2) ||
|
||||
noStop) {
|
||||
if (this._dragStartValue + difference >= minValue &&
|
||||
this._dragStartValue + difference <= maxValue)
|
||||
newValue += difference;
|
||||
}
|
||||
|
||||
let result;
|
||||
|
||||
// See if the user has moved the mouse enough to trigger
|
||||
// a drag
|
||||
if (Math.abs(stageX - this._dragStartX) < threshold &&
|
||||
Math.abs(stageY - this._dragStartY) < threshold) {
|
||||
// no motion? It's a click!
|
||||
result = SwipeScrollResult.CLICK;
|
||||
this.emit('swipe-scroll-end', result);
|
||||
} else {
|
||||
if (newValue == this._dragStartValue)
|
||||
result = SwipeScrollResult.CANCEL;
|
||||
else
|
||||
result = SwipeScrollResult.SWIPE;
|
||||
|
||||
// The event capture handler is disconnected
|
||||
// while scrolling to the final position, so
|
||||
// to avoid undesired prelights we raise
|
||||
// the cover pane.
|
||||
this._coverPane.raise_top();
|
||||
this._coverPane.show();
|
||||
|
||||
Tweener.addTween(this._scrollAdjustment,
|
||||
{ value: newValue,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onCompleteScope: this,
|
||||
onComplete: function() {
|
||||
this._coverPane.hide();
|
||||
this.emit('swipe-scroll-end',
|
||||
result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
|
||||
return result != SwipeScrollResult.CLICK;
|
||||
|
||||
case Clutter.EventType.MOTION:
|
||||
[stageX, stageY] = event.get_coords();
|
||||
let dx = this._dragX - stageX;
|
||||
let dy = this._dragY - stageY;
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
this._dragX = stageX;
|
||||
this._dragY = stageY;
|
||||
this._lastMotionTime = event.get_time();
|
||||
|
||||
// See if the user has moved the mouse enough to trigger
|
||||
// a drag
|
||||
if (Math.abs(stageX - this._dragStartX) < threshold &&
|
||||
Math.abs(stageY - this._dragStartY) < threshold)
|
||||
return true;
|
||||
|
||||
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
this._scrollAdjustment.value -= (dx / primary.width) * this._scrollAdjustment.page_size;
|
||||
else
|
||||
this._scrollAdjustment.value += (dx / primary.width) * this._scrollAdjustment.page_size;
|
||||
} else {
|
||||
this._scrollAdjustment.value += (dy / primary.height) * this._scrollAdjustment.page_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
// Block enter/leave events to avoid prelights
|
||||
// during swipe-scroll
|
||||
case Clutter.EventType.ENTER:
|
||||
case Clutter.EventType.LEAVE:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
this.workspaces = null;
|
||||
},
|
||||
|
||||
_getDesktopClone: function() {
|
||||
@ -443,7 +212,7 @@ Overview.prototype = {
|
||||
let primary = global.get_primary_monitor();
|
||||
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
|
||||
|
||||
let contentY = Main.panel.actor.height;
|
||||
let contentY = Panel.PANEL_HEIGHT;
|
||||
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
|
||||
|
||||
this._group.set_position(primary.x, primary.y);
|
||||
@ -452,21 +221,21 @@ Overview.prototype = {
|
||||
this._coverPane.set_position(0, contentY);
|
||||
this._coverPane.set_size(primary.width, contentHeight);
|
||||
|
||||
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
|
||||
let viewWidth = primary.width - dashWidth - this._spacing;
|
||||
let viewWidth = (1.0 - DASH_SPLIT_FRACTION) * primary.width - this._spacing;
|
||||
let viewHeight = contentHeight - 2 * this._spacing;
|
||||
let viewY = contentY + this._spacing;
|
||||
let viewX = rtl ? 0 : dashWidth + this._spacing;
|
||||
let viewX = rtl ? 0
|
||||
: Math.floor(DASH_SPLIT_FRACTION * primary.width) + this._spacing;
|
||||
|
||||
// Set the dash's x position - y is handled by a constraint
|
||||
let dashX;
|
||||
if (rtl) {
|
||||
this.dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||
dashX = primary.width;
|
||||
} else {
|
||||
dashX = 0;
|
||||
}
|
||||
this.dash.actor.set_x(dashX);
|
||||
this._dash.actor.set_x(dashX);
|
||||
|
||||
this.viewSelector.actor.set_position(viewX, viewY);
|
||||
this.viewSelector.actor.set_size(viewWidth, viewHeight);
|
||||
@ -478,10 +247,6 @@ Overview.prototype = {
|
||||
this.emit('item-drag-begin');
|
||||
},
|
||||
|
||||
cancelledItemDrag: function(source) {
|
||||
this.emit('item-drag-cancelled');
|
||||
},
|
||||
|
||||
endItemDrag: function(source) {
|
||||
this.emit('item-drag-end');
|
||||
},
|
||||
@ -490,33 +255,39 @@ Overview.prototype = {
|
||||
this.emit('window-drag-begin');
|
||||
},
|
||||
|
||||
cancelledWindowDrag: function(source) {
|
||||
this.emit('window-drag-cancelled');
|
||||
},
|
||||
|
||||
endWindowDrag: function(source) {
|
||||
this.emit('window-drag-end');
|
||||
},
|
||||
|
||||
// show:
|
||||
//
|
||||
// Animates the overview visible and grabs mouse and keyboard input
|
||||
show : function() {
|
||||
if (this._shown)
|
||||
return;
|
||||
// Do this manually instead of using _syncInputMode, to handle failure
|
||||
if (!Main.pushModal(this._group))
|
||||
return;
|
||||
this._modal = true;
|
||||
this._animateVisible();
|
||||
this._shown = true;
|
||||
|
||||
this._buttonPressId = this._group.connect('button-press-event',
|
||||
Lang.bind(this, this._onButtonPress));
|
||||
// Returns the scale the Overview has when we just start zooming out
|
||||
// to overview mode. That is, when just the active workspace is showing.
|
||||
getZoomedInScale : function() {
|
||||
return 1 / this.workspaces.getScale();
|
||||
},
|
||||
|
||||
_animateVisible: function() {
|
||||
if (this.visible || this.animationInProgress)
|
||||
// Returns the position the Overview has when we just start zooming out
|
||||
// to overview mode. That is, when just the active workspace is showing.
|
||||
getZoomedInPosition : function() {
|
||||
let [posX, posY] = this.workspaces.getActiveWorkspacePosition();
|
||||
let scale = this.getZoomedInScale();
|
||||
|
||||
return [- posX * scale, - posY * scale];
|
||||
},
|
||||
|
||||
// Returns the current scale of the Overview.
|
||||
getScale : function() {
|
||||
return this.workspaces.actor.scaleX;
|
||||
},
|
||||
|
||||
// Returns the current position of the Overview.
|
||||
getPosition : function() {
|
||||
return [this.workspaces.actor.x, this.workspaces.actor.y];
|
||||
},
|
||||
|
||||
show : function() {
|
||||
if (this.visible)
|
||||
return;
|
||||
if (!Main.pushModal(this.viewSelector.actor))
|
||||
return;
|
||||
|
||||
this.visible = true;
|
||||
@ -533,7 +304,9 @@ Overview.prototype = {
|
||||
this._group.show();
|
||||
this._background.show();
|
||||
|
||||
this.viewSelector.show();
|
||||
this._workspacesDisplay.show();
|
||||
this._dash.show();
|
||||
|
||||
this.workspaces = this._workspacesDisplay.workspacesView;
|
||||
global.overlay_group.add_actor(this.workspaces.actor);
|
||||
@ -551,108 +324,38 @@ Overview.prototype = {
|
||||
});
|
||||
}
|
||||
|
||||
this._group.opacity = 0;
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 255,
|
||||
// Create a zoom out effect. First scale the workspaces view up and
|
||||
// position it so that the active workspace fills up the whole screen,
|
||||
// then transform it to its normal dimensions and position.
|
||||
// The opposite transition is used in hide().
|
||||
this.workspaces.actor.scaleX = this.workspaces.actor.scaleY = this.getZoomedInScale();
|
||||
[this.workspaces.actor.x, this.workspaces.actor.y] = this.getZoomedInPosition();
|
||||
let primary = global.get_primary_monitor();
|
||||
Tweener.addTween(this.workspaces.actor,
|
||||
{ x: primary.x - this._group.x,
|
||||
y: primary.y - this._group.y,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
onComplete: this._showDone,
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
// Make the other elements fade in.
|
||||
this._group.opacity = 0;
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 255,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME
|
||||
});
|
||||
|
||||
this._coverPane.raise_top();
|
||||
this._coverPane.show();
|
||||
this.emit('showing');
|
||||
},
|
||||
|
||||
// showTemporarily:
|
||||
//
|
||||
// Animates the overview visible without grabbing mouse and keyboard input;
|
||||
// if show() has already been called, this has no immediate effect, but
|
||||
// will result in the overview not being hidden until hideTemporarily() is
|
||||
// called.
|
||||
showTemporarily: function() {
|
||||
if (this._shownTemporarily)
|
||||
return;
|
||||
|
||||
this._syncInputMode();
|
||||
this._animateVisible();
|
||||
this._shownTemporarily = true;
|
||||
},
|
||||
|
||||
// hide:
|
||||
//
|
||||
// Reverses the effect of show()
|
||||
hide: function() {
|
||||
if (!this._shown)
|
||||
return;
|
||||
|
||||
if (!this._shownTemporarily)
|
||||
this._animateNotVisible();
|
||||
|
||||
this._shown = false;
|
||||
this._syncInputMode();
|
||||
|
||||
if (this._buttonPressId > 0)
|
||||
this._group.disconnect(this._buttonPressId);
|
||||
this._buttonPressId = 0;
|
||||
},
|
||||
|
||||
// hideTemporarily:
|
||||
//
|
||||
// Reverses the effect of showTemporarily()
|
||||
hideTemporarily: function() {
|
||||
if (!this._shownTemporarily)
|
||||
return;
|
||||
|
||||
if (!this._shown)
|
||||
this._animateNotVisible();
|
||||
|
||||
this._shownTemporarily = false;
|
||||
this._syncInputMode();
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this._shown)
|
||||
this.hide();
|
||||
else
|
||||
this.show();
|
||||
},
|
||||
|
||||
//// Private methods ////
|
||||
|
||||
_syncInputMode: function() {
|
||||
// We delay input mode changes during animation so that when removing the
|
||||
// overview we don't have a problem with the release of a press/release
|
||||
// going to an application.
|
||||
if (this.animationInProgress)
|
||||
return;
|
||||
|
||||
if (this._shown) {
|
||||
if (!this._modal) {
|
||||
if (Main.pushModal(this._group))
|
||||
this._modal = true;
|
||||
else
|
||||
this.hide();
|
||||
}
|
||||
} else if (this._shownTemporarily) {
|
||||
if (this._modal) {
|
||||
Main.popModal(this._group);
|
||||
this._modal = false;
|
||||
}
|
||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
||||
} else {
|
||||
if (this._modal) {
|
||||
Main.popModal(this._group);
|
||||
this._modal = false;
|
||||
}
|
||||
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
|
||||
global.stage_input_mode = Shell.StageInputMode.NORMAL;
|
||||
}
|
||||
},
|
||||
|
||||
_animateNotVisible: function() {
|
||||
if (!this.visible || this.animationInProgress)
|
||||
if (!this.visible || this._hideInProgress)
|
||||
return;
|
||||
|
||||
this.animationInProgress = true;
|
||||
@ -669,32 +372,63 @@ Overview.prototype = {
|
||||
|
||||
this.workspaces.hide();
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
// Create a zoom in effect by transforming the workspaces view so that
|
||||
// the active workspace fills up the whole screen. The opposite
|
||||
// transition is used in show().
|
||||
let scale = this.getZoomedInScale();
|
||||
let [posX, posY] = this.getZoomedInPosition();
|
||||
Tweener.addTween(this.workspaces.actor,
|
||||
{ x: posX,
|
||||
y: posY,
|
||||
scaleX: scale,
|
||||
scaleY: scale,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
onComplete: this._hideDone,
|
||||
onCompleteScope: this
|
||||
});
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME
|
||||
});
|
||||
|
||||
this._coverPane.raise_top();
|
||||
this._coverPane.show();
|
||||
this.emit('hiding');
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this.visible)
|
||||
this.hide();
|
||||
else
|
||||
this.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* getWorkspacesForWindow:
|
||||
* @metaWindow: A #MetaWindow
|
||||
*
|
||||
* Returns the Workspaces object associated with the given window.
|
||||
* This method is not be accessible if the overview is not open
|
||||
* and will return %null.
|
||||
*/
|
||||
getWorkspacesForWindow: function(metaWindow) {
|
||||
return this.workspaces;
|
||||
},
|
||||
|
||||
//// Private methods ////
|
||||
|
||||
_showDone: function() {
|
||||
if (this._hideInProgress)
|
||||
return;
|
||||
|
||||
this.animationInProgress = false;
|
||||
this._desktopFade.hide();
|
||||
this._coverPane.hide();
|
||||
this._coverPane.lower_bottom();
|
||||
|
||||
this.emit('shown');
|
||||
// Handle any calls to hide* while we were showing
|
||||
if (!this._shown && !this._shownTemporarily)
|
||||
this._animateNotVisible();
|
||||
|
||||
this._syncInputMode();
|
||||
global.sync_pointer();
|
||||
},
|
||||
|
||||
_hideDone: function() {
|
||||
@ -704,6 +438,8 @@ Overview.prototype = {
|
||||
this.workspaces = null;
|
||||
|
||||
this._workspacesDisplay.hide();
|
||||
this.viewSelector.hide();
|
||||
this._dash.hide();
|
||||
|
||||
this._desktopFade.hide();
|
||||
this._background.hide();
|
||||
@ -713,20 +449,10 @@ Overview.prototype = {
|
||||
this.animationInProgress = false;
|
||||
this._hideInProgress = false;
|
||||
|
||||
this._coverPane.hide();
|
||||
this._coverPane.lower_bottom();
|
||||
|
||||
Main.popModal(this.viewSelector.actor);
|
||||
this.emit('hidden');
|
||||
// Handle any calls to show* while we were hiding
|
||||
if (this._shown || this._shownTemporarily)
|
||||
this._animateVisible();
|
||||
|
||||
this._syncInputMode();
|
||||
|
||||
// Fake a pointer event if requested
|
||||
if (this._needsFakePointerEvent) {
|
||||
this._fakePointerEvent();
|
||||
this._needsFakePointerEvent = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(Overview.prototype);
|
||||
|
996
js/ui/panel.js
@ -1,9 +1,7 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Lang = imports.lang;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Main = imports.ui.main;
|
||||
@ -22,10 +20,9 @@ Button.prototype = {
|
||||
track_hover: true });
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
|
||||
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0);
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
|
||||
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, /* FIXME */ 0);
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
|
||||
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
|
||||
Main.chrome.addActor(this.menu.actor, { visibleInOverview: true,
|
||||
affectsStruts: false });
|
||||
this.menu.actor.hide();
|
||||
@ -35,42 +32,28 @@ Button.prototype = {
|
||||
this.menu.toggle();
|
||||
},
|
||||
|
||||
_onSourceKeyPress: function(actor, event) {
|
||||
_onKeyPress: function(actor, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
|
||||
this.menu.toggle();
|
||||
return true;
|
||||
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
|
||||
this.menu.close();
|
||||
return true;
|
||||
} else if (symbol == Clutter.KEY_Down) {
|
||||
if (!this.menu.isOpen)
|
||||
this.menu.toggle();
|
||||
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
|
||||
this.menu.activateFirst();
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
},
|
||||
|
||||
_onMenuKeyPress: function(actor, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
||||
let focusManager = St.FocusManager.get_for_stage(global.stage);
|
||||
let group = focusManager.get_group(this.actor);
|
||||
if (group) {
|
||||
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
|
||||
group.navigate_focus(this.actor, direction, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_onOpenStateChanged: function(menu, open) {
|
||||
if (open)
|
||||
this.actor.add_style_pseudo_class('active');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('active');
|
||||
if (open) {
|
||||
this.actor.add_style_pseudo_class('pressed');
|
||||
let focus = global.stage.get_key_focus();
|
||||
if (!focus || (focus != this.actor && !menu.contains(focus)))
|
||||
this.actor.grab_key_focus();
|
||||
} else
|
||||
this.actor.remove_style_pseudo_class('pressed');
|
||||
}
|
||||
};
|
||||
|
||||
@ -88,7 +71,7 @@ SystemStatusButton.prototype = {
|
||||
__proto__: Button.prototype,
|
||||
|
||||
_init: function(iconName,tooltipText) {
|
||||
Button.prototype._init.call(this, 0.0);
|
||||
Button.prototype._init.call(this, St.Align.START);
|
||||
this._iconActor = new St.Icon({ icon_name: iconName,
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
style_class: 'system-status-icon' });
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const GConf = imports.gi.GConf;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Shell = imports.gi.Shell;
|
||||
@ -12,9 +13,12 @@ const _ = Gettext.gettext;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Search = imports.ui.search;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const NAUTILUS_PREFS_DIR = '/apps/nautilus/preferences';
|
||||
const DESKTOP_IS_HOME_KEY = NAUTILUS_PREFS_DIR + '/desktop_is_home_dir';
|
||||
|
||||
const PLACES_ICON_SIZE = 16;
|
||||
|
||||
/**
|
||||
* Represents a place object, which is most normally a bookmark entry,
|
||||
@ -59,21 +63,6 @@ PlaceInfo.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to translate launch parameters into a GAppLaunchContext
|
||||
function _makeLaunchContext(params)
|
||||
{
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
|
||||
let launchContext = global.create_app_launch_context();
|
||||
if (params.workspace != null)
|
||||
launchContext.set_desktop(params.workspace.index());
|
||||
if (params.timestamp != null)
|
||||
launchContext.set_timestamp(params.timestamp);
|
||||
|
||||
return launchContext;
|
||||
}
|
||||
|
||||
function PlaceDeviceInfo(mount) {
|
||||
this._init(mount);
|
||||
}
|
||||
@ -93,9 +82,9 @@ PlaceDeviceInfo.prototype = {
|
||||
return St.TextureCache.get_default().load_gicon(null, icon, size);
|
||||
},
|
||||
|
||||
launch: function(params) {
|
||||
launch: function() {
|
||||
Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
|
||||
_makeLaunchContext(params));
|
||||
global.create_app_launch_context());
|
||||
},
|
||||
|
||||
isRemovable: function() {
|
||||
@ -127,15 +116,20 @@ PlaceDeviceInfo.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function PlacesManager() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
PlacesManager.prototype = {
|
||||
_init: function() {
|
||||
let gconf = GConf.Client.get_default();
|
||||
gconf.add_dir(NAUTILUS_PREFS_DIR, GConf.ClientPreloadType.PRELOAD_NONE);
|
||||
|
||||
this._defaultPlaces = [];
|
||||
this._mounts = [];
|
||||
this._bookmarks = [];
|
||||
this._isDesktopHome = gconf.get_bool(DESKTOP_IS_HOME_KEY);
|
||||
|
||||
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
||||
let homeUri = homeFile.get_uri();
|
||||
@ -145,8 +139,8 @@ PlacesManager.prototype = {
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, homeIcon, size);
|
||||
},
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params));
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(homeUri, global.create_app_launch_context());
|
||||
});
|
||||
|
||||
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
||||
@ -158,8 +152,8 @@ PlacesManager.prototype = {
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, desktopIcon, size);
|
||||
},
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params));
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(desktopUri, global.create_app_launch_context());
|
||||
});
|
||||
|
||||
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
|
||||
@ -168,16 +162,40 @@ PlacesManager.prototype = {
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: size });
|
||||
},
|
||||
function (params) {
|
||||
// BUG: nautilus-connect-server doesn't have a desktop file, so we can't
|
||||
// launch it with the workspace from params. It's probably pretty rare
|
||||
// and odd to drag this place onto a workspace in any case
|
||||
|
||||
Util.spawn(['nautilus-connect-server']);
|
||||
function () {
|
||||
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
|
||||
});
|
||||
|
||||
let networkApp = null;
|
||||
try {
|
||||
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('gnome-network-scheme.desktop');
|
||||
} catch(e) {
|
||||
try {
|
||||
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('network-scheme.desktop');
|
||||
} catch(e) {
|
||||
log('Cannot create "Network" item, .desktop file not found or corrupt.');
|
||||
}
|
||||
}
|
||||
|
||||
if (networkApp != null) {
|
||||
this._network = new PlaceInfo('special:network', networkApp.get_name(),
|
||||
function(size) {
|
||||
return networkApp.create_icon_texture(size);
|
||||
},
|
||||
function () {
|
||||
networkApp.launch();
|
||||
});
|
||||
}
|
||||
|
||||
this._defaultPlaces.push(this._home);
|
||||
this._defaultPlaces.push(this._desktopMenu);
|
||||
|
||||
this._desktopMenuIndex = this._defaultPlaces.length;
|
||||
if (!this._isDesktopHome)
|
||||
this._defaultPlaces.push(this._desktopMenu);
|
||||
|
||||
if (this._network)
|
||||
this._defaultPlaces.push(this._network);
|
||||
|
||||
this._defaultPlaces.push(this._connect);
|
||||
|
||||
/*
|
||||
@ -211,6 +229,9 @@ PlacesManager.prototype = {
|
||||
}));
|
||||
|
||||
this._reloadBookmarks();
|
||||
|
||||
gconf.notify_add(DESKTOP_IS_HOME_KEY, Lang.bind(this, this._updateDesktopMenuVisibility));
|
||||
|
||||
},
|
||||
|
||||
_updateDevices: function() {
|
||||
@ -308,8 +329,8 @@ PlacesManager.prototype = {
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, icon, size);
|
||||
},
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params));
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(bookmark, global.create_app_launch_context());
|
||||
});
|
||||
this._bookmarks.push(item);
|
||||
}
|
||||
@ -319,6 +340,21 @@ PlacesManager.prototype = {
|
||||
this.emit('places-updated');
|
||||
},
|
||||
|
||||
_updateDesktopMenuVisibility: function() {
|
||||
let gconf = GConf.Client.get_default();
|
||||
this._isDesktopHome = gconf.get_boolean(DESKTOP_IS_HOME_KEY);
|
||||
|
||||
if (this._isDesktopHome)
|
||||
this._removeById(this._defaultPlaces, 'special:desktop');
|
||||
else
|
||||
this._defaultPlaces.splice(this._desktopMenuIndex, 0,
|
||||
this._desktopMenu);
|
||||
|
||||
/* See comment in _updateDevices for explanation why there are two signals. */
|
||||
this.emit('defaults-updated');
|
||||
this.emit('places-updated');
|
||||
},
|
||||
|
||||
_addMount: function(mount) {
|
||||
let devItem = new PlaceDeviceInfo(mount);
|
||||
this._mounts.push(devItem);
|
||||
@ -366,8 +402,150 @@ PlacesManager.prototype = {
|
||||
sourceArray.splice(this._lookupIndexById(sourceArray, id), 1);
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(PlacesManager.prototype);
|
||||
|
||||
/**
|
||||
* An entry in the places menu.
|
||||
* @info The corresponding PlaceInfo to populate this entry.
|
||||
*/
|
||||
function DashPlaceDisplayItem(info) {
|
||||
this._init(info);
|
||||
}
|
||||
|
||||
DashPlaceDisplayItem.prototype = {
|
||||
_init: function(info) {
|
||||
this.name = info.name;
|
||||
this._info = info;
|
||||
this._icon = info.iconFactory(PLACES_ICON_SIZE);
|
||||
|
||||
this.actor = new St.Clickable({ style_class: 'places-item',
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
|
||||
let box = new St.BoxLayout({ style_class: 'places-item-box' });
|
||||
this.actor.set_child(box);
|
||||
|
||||
let bin = new St.Bin({ child: this._icon });
|
||||
box.add(bin);
|
||||
|
||||
let text = new St.Label({ text: info.name });
|
||||
box.add(text, { expand: true, x_fill: true });
|
||||
|
||||
if (info.isRemovable()) {
|
||||
let removeIcon = new St.Icon({ icon_name: 'media-eject',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: PLACES_ICON_SIZE });
|
||||
let removeIconBox = new St.Clickable({ child: removeIcon,
|
||||
reactive: true });
|
||||
box.add(removeIconBox);
|
||||
removeIconBox.connect('clicked',
|
||||
Lang.bind(this, function() {
|
||||
this._info.remove();
|
||||
}));
|
||||
}
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
|
||||
this.actor._delegate = this;
|
||||
this._draggable = DND.makeDraggable(this.actor);
|
||||
this._draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.beginItemDrag(this);
|
||||
}));
|
||||
this._draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
},
|
||||
|
||||
_onClicked: function(b) {
|
||||
this._info.launch();
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
getDragActorSource: function() {
|
||||
return this._icon;
|
||||
},
|
||||
|
||||
getDragActor: function(stageX, stageY) {
|
||||
return this._info.iconFactory(PLACES_ICON_SIZE);
|
||||
},
|
||||
|
||||
//// Drag and drop methods ////
|
||||
|
||||
shellWorkspaceLaunch: function() {
|
||||
this._info.launch();
|
||||
}
|
||||
};
|
||||
|
||||
function DashPlaceDisplay() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DashPlaceDisplay.prototype = {
|
||||
_init: function() {
|
||||
|
||||
// Places is divided semi-arbitrarily into left and right; a grid would
|
||||
// look better in that there would be an even number of items left+right,
|
||||
// but it seems like we want some sort of differentiation between actions
|
||||
// like "Connect to server..." and regular folders
|
||||
this.actor = new St.Table({ style_class: 'places-section',
|
||||
homogeneous: true });
|
||||
|
||||
this._defaultsList = [];
|
||||
this._bookmarksList = [];
|
||||
this._mountsList = [];
|
||||
|
||||
Main.placesManager.connect('defaults-updated', Lang.bind(this, this._updateDefaults));
|
||||
Main.placesManager.connect('bookmarks-updated', Lang.bind(this, this._updateBookmarks));
|
||||
Main.placesManager.connect('mounts-updated', Lang.bind(this, this._updateMounts));
|
||||
|
||||
this._updateDefaults();
|
||||
this._updateMounts();
|
||||
this._updateBookmarks();
|
||||
},
|
||||
|
||||
_updateDefaults: function() {
|
||||
for (let i = 0; i < this._defaultsList.length; i++)
|
||||
this._defaultsList[i].destroy();
|
||||
|
||||
this._defaultsList = [];
|
||||
let places = Main.placesManager.getDefaultPlaces();
|
||||
for (let i = 0; i < places.length; i++) {
|
||||
this._defaultsList[i] = new DashPlaceDisplayItem(places[i]).actor;
|
||||
this.actor.add(this._defaultsList[i], {row: i, col: 0});
|
||||
}
|
||||
this._updateMounts();
|
||||
},
|
||||
|
||||
_updateMounts: function() {
|
||||
for (let i = 0; i < this._mountsList.length; i++)
|
||||
this._mountsList[i].destroy();
|
||||
|
||||
this._mountsList = [];
|
||||
let places = Main.placesManager.getMounts();
|
||||
for (let i = 0; i < places.length; i++) {
|
||||
this._mountsList[i] = new DashPlaceDisplayItem(places[i]).actor;
|
||||
this.actor.add(this._mountsList[i], {row: this._defaultsList.length + i, col: 0});
|
||||
}
|
||||
},
|
||||
|
||||
_updateBookmarks: function() {
|
||||
for (let i = 0; i < this._bookmarksList.length; i++)
|
||||
this._bookmarksList[i].destroy();
|
||||
|
||||
this._bookmarksList = [];
|
||||
let places = Main.placesManager.getBookmarks();
|
||||
for (let i = 0; i < places.length; i ++) {
|
||||
this._bookmarksList[i] = new DashPlaceDisplayItem(places[i]).actor;
|
||||
this.actor.add(this._bookmarksList[i], {row: i, col: 1});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(DashPlaceDisplay.prototype);
|
||||
|
||||
function PlaceSearchProvider() {
|
||||
this._init();
|
||||
@ -386,15 +564,12 @@ PlaceSearchProvider.prototype = {
|
||||
return null;
|
||||
return { 'id': resultId,
|
||||
'name': placeInfo.name,
|
||||
'createIcon': function(size) {
|
||||
return placeInfo.iconFactory(size);
|
||||
}
|
||||
};
|
||||
'icon': placeInfo.iconFactory(Search.RESULT_ICON_SIZE) };
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
activateResult: function(id) {
|
||||
let placeInfo = Main.placesManager.lookupPlaceById(id);
|
||||
placeInfo.launch(params);
|
||||
placeInfo.launch();
|
||||
},
|
||||
|
||||
_compareResultMeta: function (idA, idB) {
|
||||
|
@ -1,412 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
*
|
||||
* Copyright 2010 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, 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: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const St = imports.gi.St;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Polkit = imports.gi.Polkit;
|
||||
const PolkitAgent = imports.gi.PolkitAgent;
|
||||
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
|
||||
function AuthenticationDialog(actionId, message, cookie, userNames) {
|
||||
this._init(actionId, message, cookie, userNames);
|
||||
}
|
||||
|
||||
AuthenticationDialog.prototype = {
|
||||
__proto__: ModalDialog.ModalDialog.prototype,
|
||||
|
||||
_init: function(actionId, message, cookie, userNames) {
|
||||
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'polkit-dialog' });
|
||||
|
||||
this.actionId = actionId;
|
||||
this.message = message;
|
||||
this.userNames = userNames;
|
||||
this._wasDismissed = false;
|
||||
this._completed = false;
|
||||
|
||||
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
|
||||
vertical: false });
|
||||
this.contentLayout.add(mainContentBox,
|
||||
{ x_fill: true,
|
||||
y_fill: true });
|
||||
|
||||
let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
|
||||
mainContentBox.add(icon,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
|
||||
vertical: true });
|
||||
mainContentBox.add(messageBox,
|
||||
{ y_align: St.Align.START });
|
||||
|
||||
this._subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
|
||||
text: _("Authentication Required") });
|
||||
|
||||
messageBox.add(this._subjectLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
|
||||
text: message });
|
||||
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._descriptionLabel.clutter_text.line_wrap = true;
|
||||
|
||||
messageBox.add(this._descriptionLabel,
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
if (userNames.length > 1) {
|
||||
log('polkitAuthenticationAgent: Received ' + userNames.length +
|
||||
' identities that can be used for authentication. Only ' +
|
||||
'considering the first one.');
|
||||
}
|
||||
|
||||
let userName = userNames[0];
|
||||
|
||||
this._user = Gdm.UserManager.ref_default().get_user(userName);
|
||||
let userRealName = this._user.get_real_name()
|
||||
this._userLoadedId = this._user.connect('notify::is_loaded',
|
||||
Lang.bind(this, this._onUserChanged));
|
||||
this._userChangedId = this._user.connect('changed',
|
||||
Lang.bind(this, this._onUserChanged));
|
||||
|
||||
// Special case 'root'
|
||||
let userIsRoot = false;
|
||||
if (userName == 'root') {
|
||||
userIsRoot = true;
|
||||
userRealName = _("Administrator");
|
||||
}
|
||||
|
||||
if (userIsRoot) {
|
||||
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label',
|
||||
text: userRealName }));
|
||||
messageBox.add(userLabel);
|
||||
} else {
|
||||
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
|
||||
vertical: false });
|
||||
messageBox.add(userBox);
|
||||
this._userIcon = new St.Icon();
|
||||
this._userIcon.hide();
|
||||
userBox.add(this._userIcon,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-label',
|
||||
text: userRealName }));
|
||||
userBox.add(userLabel,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.MIDDLE });
|
||||
}
|
||||
|
||||
this._onUserChanged();
|
||||
|
||||
this._passwordBox = new St.BoxLayout({ vertical: false });
|
||||
messageBox.add(this._passwordBox);
|
||||
this._passwordLabel = new St.Label(({ style_class: 'polkit-dialog-password-label' }));
|
||||
this._passwordBox.add(this._passwordLabel);
|
||||
this._passwordEntry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
|
||||
text: "",
|
||||
can_focus: true});
|
||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
|
||||
this._passwordBox.add(this._passwordEntry,
|
||||
{expand: true });
|
||||
this._passwordBox.hide();
|
||||
|
||||
this._errorMessageLabel = new St.Label({ style_class: 'polkit-dialog-error-label' });
|
||||
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._errorMessageLabel.clutter_text.line_wrap = true;
|
||||
messageBox.add(this._errorMessageLabel);
|
||||
this._errorMessageLabel.hide();
|
||||
|
||||
this._infoMessageLabel = new St.Label({ style_class: 'polkit-dialog-info-label' });
|
||||
this._infoMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._infoMessageLabel.clutter_text.line_wrap = true;
|
||||
messageBox.add(this._infoMessageLabel);
|
||||
this._infoMessageLabel.hide();
|
||||
|
||||
/* text is intentionally non-blank otherwise the height is not the same as for
|
||||
* infoMessage and errorMessageLabel - but it is still invisible because
|
||||
* gnome-shell.css sets the color to be transparent
|
||||
*/
|
||||
this._nullMessageLabel = new St.Label({ style_class: 'polkit-dialog-null-label',
|
||||
text: 'abc'});
|
||||
this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._nullMessageLabel.clutter_text.line_wrap = true;
|
||||
messageBox.add(this._nullMessageLabel);
|
||||
this._nullMessageLabel.show();
|
||||
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
action: Lang.bind(this, this.cancel),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _("Authenticate"),
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed)
|
||||
}]);
|
||||
|
||||
this._doneEmitted = false;
|
||||
|
||||
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
|
||||
this._cookie = cookie;
|
||||
|
||||
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
|
||||
cookie: this._cookie });
|
||||
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
|
||||
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
|
||||
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
|
||||
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
|
||||
|
||||
// Delay focus grab to avoid ModalDialog stealing focus with
|
||||
// its buttons
|
||||
this.connect('opened',
|
||||
Lang.bind(this, function() {
|
||||
this._passwordEntry.grab_key_focus();
|
||||
}));
|
||||
},
|
||||
|
||||
startAuthentication: function() {
|
||||
this._session.initiate();
|
||||
},
|
||||
|
||||
_ensureOpen: function() {
|
||||
// NOTE: ModalDialog.open() is safe to call if the dialog is
|
||||
// already open - it just returns true without side-effects
|
||||
if (!this.open(global.get_current_time())) {
|
||||
// This can fail if e.g. unable to get input grab
|
||||
//
|
||||
// In an ideal world this wouldn't happen (because the
|
||||
// Shell is in complete control of the session) but that's
|
||||
// just not how things work right now.
|
||||
//
|
||||
// One way to make this happen is by running 'sleep 3;
|
||||
// pkexec bash' and then opening a popup menu.
|
||||
//
|
||||
// We could add retrying if this turns out to be a problem
|
||||
|
||||
log('polkitAuthenticationAgent: Failed to show modal dialog.' +
|
||||
' Dismissing authentication request for action-id ' + this.actionId +
|
||||
' cookie ' + this._cookie);
|
||||
this._emitDone(false, true);
|
||||
}
|
||||
},
|
||||
|
||||
_emitDone: function(keepVisible, dismissed) {
|
||||
if (!this._doneEmitted) {
|
||||
this._doneEmitted = true;
|
||||
this.emit('done', keepVisible, dismissed);
|
||||
}
|
||||
},
|
||||
|
||||
_onEntryActivate: function() {
|
||||
let response = this._passwordEntry.get_text();
|
||||
this._session.response(response);
|
||||
// When the user responds, dismiss already shown info and
|
||||
// error texts (if any)
|
||||
this._errorMessageLabel.hide();
|
||||
this._infoMessageLabel.hide();
|
||||
this._nullMessageLabel.show();
|
||||
},
|
||||
|
||||
_onAuthenticateButtonPressed: function() {
|
||||
this._onEntryActivate();
|
||||
},
|
||||
|
||||
_onSessionCompleted: function(session, gainedAuthorization) {
|
||||
if (this._completed)
|
||||
return;
|
||||
|
||||
this._completed = true;
|
||||
|
||||
if (!gainedAuthorization) {
|
||||
/* Unless we are showing an existing error message from the PAM
|
||||
* module (the PAM module could be reporting the authentication
|
||||
* error providing authentication-method specific information),
|
||||
* show "Sorry, that didn't work. Please try again."
|
||||
*/
|
||||
if (!this._errorMessageLabel.visible && !this._wasDismissed) {
|
||||
/* Translators: "that didn't work" refers to the fact that the
|
||||
* requested authentication was not gained; this can happen
|
||||
* because of an authentication error (like invalid password),
|
||||
* for instance. */
|
||||
this._errorMessageLabel.set_text(_("Sorry, that didn\'t work. Please try again."));
|
||||
this._errorMessageLabel.show();
|
||||
this._infoMessageLabel.hide();
|
||||
this._nullMessageLabel.hide();
|
||||
}
|
||||
}
|
||||
this._emitDone(!gainedAuthorization, false);
|
||||
},
|
||||
|
||||
_onSessionRequest: function(session, request, echo_on) {
|
||||
// Cheap localization trick
|
||||
if (request == 'Password:')
|
||||
this._passwordLabel.set_text(_("Password:"));
|
||||
else
|
||||
this._passwordLabel.set_text(request);
|
||||
|
||||
if (echo_on)
|
||||
this._passwordEntry.clutter_text.set_password_char('');
|
||||
else
|
||||
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||
|
||||
this._passwordBox.show();
|
||||
this._passwordEntry.set_text('');
|
||||
this._passwordEntry.grab_key_focus();
|
||||
this._ensureOpen();
|
||||
},
|
||||
|
||||
_onSessionShowError: function(session, text) {
|
||||
this._passwordEntry.set_text('');
|
||||
this._errorMessageLabel.set_text(text);
|
||||
this._errorMessageLabel.show();
|
||||
this._infoMessageLabel.hide();
|
||||
this._nullMessageLabel.hide();
|
||||
this._ensureOpen();
|
||||
},
|
||||
|
||||
_onSessionShowInfo: function(session, text) {
|
||||
this._passwordEntry.set_text('');
|
||||
this._infoMessageLabel.set_text(text);
|
||||
this._infoMessageLabel.show();
|
||||
this._errorMessageLabel.hide();
|
||||
this._nullMessageLabel.hide();
|
||||
this._ensureOpen();
|
||||
},
|
||||
|
||||
destroySession: function() {
|
||||
if (this._session) {
|
||||
if (!this._completed)
|
||||
this._session.cancel();
|
||||
this._session = null;
|
||||
}
|
||||
},
|
||||
|
||||
_onUserChanged: function() {
|
||||
if (this._user.is_loaded) {
|
||||
if (this._userIcon) {
|
||||
let iconFileName = this._user.get_icon_file();
|
||||
let iconFile = Gio.file_new_for_path(iconFileName);
|
||||
let icon;
|
||||
if (iconFile.query_exists(null)) {
|
||||
icon = new Gio.FileIcon({file: iconFile});
|
||||
} else {
|
||||
icon = new Gio.ThemedIcon({name: 'avatar-default'});
|
||||
}
|
||||
this._userIcon.set_gicon (icon);
|
||||
this._userIcon.show();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this._wasDismissed = true;
|
||||
this.close(global.get_current_time());
|
||||
this._emitDone(false, true);
|
||||
},
|
||||
|
||||
};
|
||||
Signals.addSignalMethods(AuthenticationDialog.prototype);
|
||||
|
||||
function AuthenticationAgent() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
AuthenticationAgent.prototype = {
|
||||
_init: function() {
|
||||
this._native = new Shell.PolkitAuthenticationAgent();
|
||||
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
||||
this._native.connect('cancel', Lang.bind(this, this._onCancel));
|
||||
this._currentDialog = null;
|
||||
this._isCompleting = false;
|
||||
},
|
||||
|
||||
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
||||
this._currentDialog = new AuthenticationDialog(actionId, message, cookie, userNames);
|
||||
|
||||
// We actually don't want to open the dialog until we know for
|
||||
// sure that we're going to interact with the user. For
|
||||
// example, if the password for the identity to auth is blank
|
||||
// (which it will be on a live CD) then there will be no
|
||||
// conversation at all... of course, we don't *know* that
|
||||
// until we actually try it.
|
||||
//
|
||||
// See https://bugzilla.gnome.org/show_bug.cgi?id=643062 for more
|
||||
// discussion.
|
||||
|
||||
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
|
||||
this._currentDialog.startAuthentication();
|
||||
},
|
||||
|
||||
_onCancel: function(nativeAgent) {
|
||||
this._completeRequest(false, false);
|
||||
},
|
||||
|
||||
_onDialogDone: function(dialog, keepVisible, dismissed) {
|
||||
this._completeRequest(keepVisible, dismissed);
|
||||
},
|
||||
|
||||
_reallyCompleteRequest: function(dismissed) {
|
||||
this._currentDialog.close();
|
||||
this._currentDialog.destroySession();
|
||||
this._currentDialog = null;
|
||||
this._isCompleting = false;
|
||||
|
||||
this._native.complete(dismissed)
|
||||
},
|
||||
|
||||
_completeRequest: function(keepVisible, wasDismissed) {
|
||||
if (this._isCompleting)
|
||||
return;
|
||||
|
||||
this._isCompleting = true;
|
||||
|
||||
if (keepVisible) {
|
||||
// Give the user 2 seconds to read 'Authentication Failure' before
|
||||
// dismissing the dialog
|
||||
Mainloop.timeout_add(2000,
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
this._reallyCompleteRequest(wasDismissed);
|
||||
}));
|
||||
} else {
|
||||
this._reallyCompleteRequest(wasDismissed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
let agent = new AuthenticationAgent();
|
||||
}
|
1140
js/ui/popupMenu.js
@ -11,21 +11,16 @@ const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Util = imports.misc.util;
|
||||
const History = imports.misc.history;
|
||||
|
||||
const MAX_FILE_DELETED_BEFORE_INVALID = 10;
|
||||
|
||||
const HISTORY_KEY = 'command-history';
|
||||
const HISTORY_LIMIT = 512;
|
||||
|
||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||
const DISABLE_COMMAND_LINE_KEY = 'disable-command-line';
|
||||
|
||||
const DIALOG_GROW_TIME = 0.1;
|
||||
const DIALOG_FADE_TIME = 0.1;
|
||||
|
||||
function CommandCompleter() {
|
||||
this._init();
|
||||
@ -67,6 +62,25 @@ CommandCompleter.prototype = {
|
||||
this._update(0);
|
||||
},
|
||||
|
||||
_onGetEnumerateComplete : function(obj, res) {
|
||||
this._enumerator = obj.enumerate_children_finish(res);
|
||||
this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete));
|
||||
},
|
||||
|
||||
_onNextFileComplete : function(obj, res) {
|
||||
let files = obj.next_files_finish(res);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
this._childs[this._i].push(files[i].get_name());
|
||||
}
|
||||
if (files.length) {
|
||||
this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete));
|
||||
} else {
|
||||
this._enumerator.close(null);
|
||||
this._enumerator = null;
|
||||
this._update(this._i + 1);
|
||||
}
|
||||
},
|
||||
|
||||
update : function() {
|
||||
if (this._valid)
|
||||
return;
|
||||
@ -86,12 +100,7 @@ CommandCompleter.prototype = {
|
||||
}
|
||||
let file = Gio.file_new_for_path(this._paths[i]);
|
||||
this._childs[this._i] = [];
|
||||
FileUtils.listDirAsync(file, Lang.bind(this, function (files) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
this._childs[this._i].push(files[i].get_name());
|
||||
}
|
||||
this._update(this._i + 1);
|
||||
}));
|
||||
file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onGetEnumerateComplete));
|
||||
},
|
||||
|
||||
_onChanged : function(m, f, of, type) {
|
||||
@ -166,16 +175,22 @@ function RunDialog() {
|
||||
}
|
||||
|
||||
RunDialog.prototype = {
|
||||
__proto__: ModalDialog.ModalDialog.prototype,
|
||||
_init : function() {
|
||||
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'run-dialog' });
|
||||
this._isOpen = false;
|
||||
|
||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||
global.settings.connect('changed::development-tools', Lang.bind(this, function () {
|
||||
this._enableInternalCommands = global.settings.get_boolean('development-tools');
|
||||
}));
|
||||
this._enableInternalCommands = global.settings.get_boolean('development-tools');
|
||||
|
||||
this._history = global.settings.get_strv(HISTORY_KEY);
|
||||
this._historyIndex = -1;
|
||||
|
||||
global.settings.connect('changed::' + HISTORY_KEY, Lang.bind(this, function() {
|
||||
this._history = global.settings.get_strv(HISTORY_KEY);
|
||||
this._historyIndex = this._history.length;
|
||||
}));
|
||||
|
||||
this._internalCommands = { 'lg':
|
||||
Lang.bind(this, function() {
|
||||
Main.createLookingGlass().open();
|
||||
@ -192,66 +207,75 @@ __proto__: ModalDialog.ModalDialog.prototype,
|
||||
|
||||
'debugexit': Lang.bind(this, function() {
|
||||
Meta.exit(Meta.ExitCode.ERROR);
|
||||
}),
|
||||
|
||||
// rt is short for "reload theme"
|
||||
'rt': Lang.bind(this, function() {
|
||||
Main.loadTheme();
|
||||
})
|
||||
};
|
||||
|
||||
// All actors are inside _group. We create it initially
|
||||
// hidden then show it in show()
|
||||
this._group = new Clutter.Group({ visible: false,
|
||||
x: 0, y: 0 });
|
||||
Main.uiGroup.add_actor(this._group);
|
||||
|
||||
this._lightbox = new Lightbox.Lightbox(this._group,
|
||||
{ inhibitEvents: true });
|
||||
|
||||
this._box = new St.Bin({ x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
this._group.add_actor(this._box);
|
||||
this._lightbox.highlight(this._box);
|
||||
|
||||
let dialogBox = new St.BoxLayout({ style_class: 'run-dialog', vertical: true });
|
||||
|
||||
this._box.set_child(dialogBox);
|
||||
|
||||
let label = new St.Label({ style_class: 'run-dialog-label',
|
||||
text: _("Please enter a command:") });
|
||||
|
||||
this.contentLayout.add(label, { y_align: St.Align.START });
|
||||
dialogBox.add(label, { expand: true, y_fill: false });
|
||||
|
||||
let entry = new St.Entry({ style_class: 'run-dialog-entry' });
|
||||
|
||||
this._entryText = entry.clutter_text;
|
||||
this.contentLayout.add(entry, { y_align: St.Align.START });
|
||||
this.setInitialKeyFocus(this._entryText);
|
||||
dialogBox.add(entry, { expand: true });
|
||||
|
||||
this._errorBox = new St.BoxLayout({ style_class: 'run-dialog-error-box' });
|
||||
this._errorBox = new St.BoxLayout();
|
||||
|
||||
this.contentLayout.add(this._errorBox, { expand: true });
|
||||
dialogBox.add(this._errorBox, { expand: true });
|
||||
|
||||
let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' });
|
||||
let errorIcon = new St.Button({ style_class: 'run-dialog-error-icon' });
|
||||
|
||||
this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });
|
||||
this._errorBox.add(errorIcon);
|
||||
|
||||
this._commandError = false;
|
||||
|
||||
this._errorMessage = new St.Label({ style_class: 'run-dialog-error-label' });
|
||||
this._errorMessage.clutter_text.line_wrap = true;
|
||||
|
||||
this._errorBox.add(this._errorMessage, { expand: true,
|
||||
y_align: St.Align.MIDDLE,
|
||||
y_fill: false });
|
||||
this._errorBox.add(this._errorMessage, { expand: true });
|
||||
|
||||
this._errorBox.hide();
|
||||
|
||||
this._pathCompleter = new Gio.FilenameCompleter();
|
||||
this._commandCompleter = new CommandCompleter();
|
||||
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
|
||||
|
||||
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
|
||||
entry: this._entryText });
|
||||
this._entryText.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Down) {
|
||||
this._setCommandFromHistory(this._historyIndex++);
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.Up) {
|
||||
this._setCommandFromHistory(this._historyIndex--);
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
|
||||
this.popModal();
|
||||
if (Shell.get_event_state(e) & Clutter.ModifierType.CONTROL_MASK)
|
||||
this._run(o.get_text(), true);
|
||||
else
|
||||
this._run(o.get_text(), false);
|
||||
if (!this._commandError)
|
||||
this.close();
|
||||
else {
|
||||
if (!this.pushModal())
|
||||
this.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.Escape) {
|
||||
this.close();
|
||||
@ -297,10 +321,22 @@ __proto__: ModalDialog.ModalDialog.prototype,
|
||||
}
|
||||
},
|
||||
|
||||
_saveHistory : function() {
|
||||
if (this._history.length > HISTORY_LIMIT) {
|
||||
this._history.splice(0, this._history.length - HISTORY_LIMIT);
|
||||
}
|
||||
global.settings.set_strv(HISTORY_KEY, this._history);
|
||||
},
|
||||
|
||||
_run : function(input, inTerminal) {
|
||||
let command = input;
|
||||
|
||||
this._history.addItem(input);
|
||||
if (this._history.length == 0 ||
|
||||
this._history[this._history.length - 1] != input) {
|
||||
this._history.push(input);
|
||||
this._saveHistory();
|
||||
}
|
||||
|
||||
this._commandError = false;
|
||||
let f;
|
||||
if (this._enableInternalCommands)
|
||||
@ -313,7 +349,9 @@ __proto__: ModalDialog.ModalDialog.prototype,
|
||||
try {
|
||||
if (inTerminal)
|
||||
command = 'gnome-terminal -x ' + input;
|
||||
Util.trySpawnCommandLine(command);
|
||||
let [ok, len, args] = GLib.shell_parse_argv(command);
|
||||
let p = new Shell.Process({ 'args' : args });
|
||||
p.run();
|
||||
} catch (e) {
|
||||
// Mmmh, that failed - see if @input matches an existing file
|
||||
let path = null;
|
||||
@ -331,40 +369,84 @@ __proto__: ModalDialog.ModalDialog.prototype,
|
||||
global.create_app_launch_context());
|
||||
} else {
|
||||
this._commandError = true;
|
||||
// The exception contains an error string like:
|
||||
// Error invoking Shell.run: Failed to execute child
|
||||
// process "foo" (No such file or directory)
|
||||
// We are only interested in the actual error, so parse
|
||||
//that out.
|
||||
let m = /.+\((.+)\)/.exec(e);
|
||||
let errorStr = _("Execution of '%s' failed:").format(command) + '\n' + m[1];
|
||||
this._errorMessage.set_text(errorStr);
|
||||
|
||||
this._errorMessage.set_text(e.message);
|
||||
|
||||
if (!this._errorBox.visible) {
|
||||
let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
|
||||
|
||||
let parentActor = this._errorBox.get_parent();
|
||||
Tweener.addTween(parentActor,
|
||||
{ height: parentActor.height + errorBoxNaturalHeight,
|
||||
time: DIALOG_GROW_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
parentActor.set_height(-1);
|
||||
this._errorBox.show();
|
||||
})
|
||||
});
|
||||
}
|
||||
this._errorBox.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
open: function() {
|
||||
this._history.lastItem();
|
||||
this._errorBox.hide();
|
||||
this._entryText.set_text('');
|
||||
this._commandError = false;
|
||||
_setCommandFromHistory: function(lastI) {
|
||||
if (this._historyIndex < 0)
|
||||
this._historyIndex = 0;
|
||||
if (this._historyIndex > this._history.length)
|
||||
this._historyIndex = this._history.length;
|
||||
|
||||
if (this._lockdownSettings.get_boolean(DISABLE_COMMAND_LINE_KEY))
|
||||
return;
|
||||
|
||||
ModalDialog.ModalDialog.prototype.open.call(this);
|
||||
let text = this._entryText.get_text();
|
||||
if (text) {
|
||||
this._history[lastI] = text;
|
||||
}
|
||||
if (this._history[this._historyIndex]) {
|
||||
this._entryText.set_text(this._history[this._historyIndex]);
|
||||
} else
|
||||
this._entryText.set_text('');
|
||||
},
|
||||
|
||||
open : function() {
|
||||
if (this._isOpen) // Already shown
|
||||
return;
|
||||
|
||||
if (!Main.pushModal(this._group))
|
||||
return;
|
||||
|
||||
// Position the dialog on the current monitor
|
||||
let monitor = global.get_focus_monitor();
|
||||
|
||||
this._historyIndex = this._history.length;
|
||||
|
||||
this._box.set_position(monitor.x, monitor.y);
|
||||
this._box.set_size(monitor.width, monitor.height);
|
||||
|
||||
this._isOpen = true;
|
||||
this._lightbox.show();
|
||||
this._group.opacity = 0;
|
||||
this._group.show();
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 255,
|
||||
time: DIALOG_FADE_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
global.stage.set_key_focus(this._entryText);
|
||||
},
|
||||
|
||||
close : function() {
|
||||
if (!this._isOpen)
|
||||
return;
|
||||
|
||||
this._isOpen = false;
|
||||
this._commandError = false;
|
||||
|
||||
Main.popModal(this._group);
|
||||
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
time: DIALOG_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._errorBox.hide();
|
||||
this._group.hide();
|
||||
this._entryText.set_text('');
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(RunDialog.prototype);
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const DBus = imports.dbus;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
@ -69,104 +68,6 @@ function waitLeisure() {
|
||||
};
|
||||
}
|
||||
|
||||
const PerfHelperIface = {
|
||||
name: 'org.gnome.Shell.PerfHelper',
|
||||
methods: [{ name: 'CreateWindow', inSignature: 'iibb', outSignature: '' },
|
||||
{ name: 'WaitWindows', inSignature: '', outSignature: '' },
|
||||
{ name: 'DestroyWindows', inSignature: '', outSignature: ''}]
|
||||
};
|
||||
|
||||
const PerfHelper = function () {
|
||||
this._init();
|
||||
};
|
||||
|
||||
PerfHelper.prototype = {
|
||||
_init: function() {
|
||||
DBus.session.proxifyObject(this, 'org.gnome.Shell.PerfHelper', '/org/gnome/Shell/PerfHelper');
|
||||
}
|
||||
};
|
||||
|
||||
DBus.proxifyPrototype(PerfHelper.prototype, PerfHelperIface);
|
||||
|
||||
let _perfHelper = null;
|
||||
function _getPerfHelper() {
|
||||
if (_perfHelper == null)
|
||||
_perfHelper = new PerfHelper();
|
||||
|
||||
return _perfHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* createTestWindow:
|
||||
* @width: width of window, in pixels
|
||||
* @height: height of window, in pixels
|
||||
* @alpha: whether the window should be alpha transparent
|
||||
* @maximized: whethe the window should be created maximized
|
||||
*
|
||||
* Creates a window using gnome-shell-perf-helper for testing purposes.
|
||||
* While this function can be used with yield in an automation
|
||||
* script to pause until the D-Bus call to the helper process returns,
|
||||
* because of the normal X asynchronous mapping process, to actually wait
|
||||
* until the window has been mapped and exposed, use waitTestWindows().
|
||||
*/
|
||||
function createTestWindow(width, height, alpha, maximized) {
|
||||
let cb;
|
||||
let perfHelper = _getPerfHelper();
|
||||
|
||||
perfHelper.CreateWindowRemote(width, height, alpha, maximized,
|
||||
function(result, excp) {
|
||||
if (cb)
|
||||
cb();
|
||||
});
|
||||
|
||||
return function(callback) {
|
||||
cb = callback;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* waitTestWindows:
|
||||
*
|
||||
* Used within an automation script to pause until all windows previously
|
||||
* created with createTestWindow have been mapped and exposed.
|
||||
*/
|
||||
function waitTestWindows() {
|
||||
let cb;
|
||||
let perfHelper = _getPerfHelper();
|
||||
|
||||
perfHelper.WaitWindowsRemote(function(result, excp) {
|
||||
if (cb)
|
||||
cb();
|
||||
});
|
||||
|
||||
return function(callback) {
|
||||
cb = callback;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* destroyTestWindows:
|
||||
*
|
||||
* Destroys all windows previously created with createTestWindow().
|
||||
* While this function can be used with yield in an automation
|
||||
* script to pause until the D-Bus call to the helper process returns,
|
||||
* this doesn't guarantee that Mutter has actually finished the destroy
|
||||
* process because of normal X asynchronicity.
|
||||
*/
|
||||
function destroyTestWindows() {
|
||||
let cb;
|
||||
let perfHelper = _getPerfHelper();
|
||||
|
||||
perfHelper.DestroyWindowsRemote(function(result, excp) {
|
||||
if (cb)
|
||||
cb();
|
||||
});
|
||||
|
||||
return function(callback) {
|
||||
cb = callback;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* defineScriptEvent
|
||||
* @name: The event will be called script.<name>
|
||||
@ -246,8 +147,8 @@ function _collect(scriptModule, outputFile) {
|
||||
Shell.write_string_to_stream(out, '"events":\n');
|
||||
Shell.PerfLog.get_default().dump_events(out);
|
||||
|
||||
let monitors = global.get_monitors();
|
||||
let primary = global.get_primary_monitor();
|
||||
let monitors = global.get_monitors()
|
||||
let primary = global.get_primary_monitor()
|
||||
Shell.write_string_to_stream(out, ',\n"monitors":\n[');
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
let monitor = monitors[i];
|
||||
@ -266,21 +167,7 @@ function _collect(scriptModule, outputFile) {
|
||||
Shell.write_string_to_stream(out, ',\n"metrics":\n[ ');
|
||||
let first = true;
|
||||
for (let name in scriptModule.METRICS) {
|
||||
let metric = scriptModule.METRICS[name];
|
||||
// Extra checks here because JSON.stringify generates
|
||||
// invalid JSON for undefined values
|
||||
if (metric.description == null) {
|
||||
log("Error: No description found for metric " + name);
|
||||
continue;
|
||||
}
|
||||
if (metric.units == null) {
|
||||
log("Error: No units found for metric " + name);
|
||||
continue;
|
||||
}
|
||||
if (metric.value == null) {
|
||||
log("Error: No value found for metric " + name);
|
||||
continue;
|
||||
}
|
||||
let metric = scriptModule.METRICS[name];
|
||||
|
||||
if (!first)
|
||||
Shell.write_string_to_stream(out, ',\n ');
|
||||
|
132
js/ui/search.js
@ -1,19 +1,8 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers';
|
||||
const RESULT_ICON_SIZE = 48;
|
||||
|
||||
// Not currently referenced by the search API, but
|
||||
// this enumeration can be useful for provider
|
||||
@ -163,9 +152,8 @@ SearchProvider.prototype = {
|
||||
* getResultInfo:
|
||||
* @id: Result identifier string
|
||||
*
|
||||
* Return an object with 'id', 'name', (both strings) and 'createIcon'
|
||||
* (function(size) returning a Clutter.Texture) properties which describe
|
||||
* the given search result.
|
||||
* Return an object with 'id', 'name', (both strings) and 'icon' (Clutter.Texture)
|
||||
* properties which describe the given search result.
|
||||
*/
|
||||
getResultMeta: function(id) {
|
||||
throw new Error('Not implemented');
|
||||
@ -208,111 +196,21 @@ SearchProvider.prototype = {
|
||||
*/
|
||||
activateResult: function(id) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
/**
|
||||
* expandSearch:
|
||||
*
|
||||
* Called when the user clicks on the header for this
|
||||
* search section. Should typically launch an external program
|
||||
* displaying search results for that item type.
|
||||
*/
|
||||
expandSearch: function(terms) {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(SearchProvider.prototype);
|
||||
|
||||
function OpenSearchSystem() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
OpenSearchSystem.prototype = {
|
||||
_init: function() {
|
||||
this._providers = [];
|
||||
global.settings.connect('changed::' + DISABLED_OPEN_SEARCH_PROVIDERS_KEY, Lang.bind(this, this._refresh));
|
||||
this._refresh();
|
||||
},
|
||||
|
||||
getProviders: function() {
|
||||
let res = [];
|
||||
for (let i = 0; i < this._providers.length; i++)
|
||||
res.push({ id: i, name: this._providers[i].name });
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
setSearchTerms: function(terms) {
|
||||
this._terms = terms;
|
||||
},
|
||||
|
||||
_checkSupportedProviderLanguage: function(provider) {
|
||||
if (provider.url.search(/{language}/) == -1)
|
||||
return true;
|
||||
|
||||
let langs = GLib.get_language_names();
|
||||
|
||||
langs.push('en');
|
||||
let lang = null;
|
||||
for (let i = 0; i < langs.length; i++) {
|
||||
for (let k = 0; k < provider.langs.length; k++) {
|
||||
if (langs[i] == provider.langs[k])
|
||||
lang = langs[i];
|
||||
}
|
||||
if (lang)
|
||||
break;
|
||||
}
|
||||
provider.lang = lang;
|
||||
return lang != null;
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
let searchTerms = this._terms.join(' ');
|
||||
|
||||
let url = this._providers[id].url.replace('{searchTerms}', encodeURIComponent(searchTerms));
|
||||
if (url.match('{language}'))
|
||||
url = url.replace('{language}', this._providers[id].lang);
|
||||
|
||||
try {
|
||||
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
|
||||
} catch (e) {
|
||||
// TODO: remove this after glib will be removed from moduleset
|
||||
// In the default jhbuild, gio is in our prefix but gvfs is not
|
||||
Util.spawn(['gvfs-open', url])
|
||||
}
|
||||
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
_addProvider: function(fileName) {
|
||||
let file = Gio.file_new_for_path(global.datadir + '/search_providers/' + fileName);
|
||||
let source = '';
|
||||
|
||||
file.load_contents_async(null, Lang.bind(this, function (obj, res) {
|
||||
let [success, source] = file.load_contents_finish(res);
|
||||
if (source) {
|
||||
let [success, name, url, langs, icon_uri] = global.parse_search_provider(source);
|
||||
let provider ={ name: name,
|
||||
url: url,
|
||||
id: this._providers.length,
|
||||
icon_uri: icon_uri,
|
||||
langs: langs };
|
||||
if (this._checkSupportedProviderLanguage(provider)) {
|
||||
this._providers.push(provider);
|
||||
this.emit('changed');
|
||||
}
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_refresh: function() {
|
||||
this._providers = [];
|
||||
let names = global.settings.get_strv(DISABLED_OPEN_SEARCH_PROVIDERS_KEY);
|
||||
let file = Gio.file_new_for_path(global.datadir + '/search_providers');
|
||||
FileUtils.listDirAsync(file, Lang.bind(this, function(files) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let enabled = true;
|
||||
let name = files[i].get_name();
|
||||
for (let k = 0; k < names.length; k++)
|
||||
if (names[k] == name)
|
||||
enabled = false;
|
||||
if (enabled)
|
||||
this._addProvider(name);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(OpenSearchSystem.prototype);
|
||||
|
||||
function SearchSystem() {
|
||||
this._init();
|
||||
}
|
||||
@ -343,7 +241,7 @@ SearchSystem.prototype = {
|
||||
updateSearch: function(searchString) {
|
||||
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
if (searchString == '')
|
||||
return [];
|
||||
return null;
|
||||
|
||||
let terms = searchString.split(/\s+/);
|
||||
let isSubSearch = terms.length == this._previousTerms.length;
|
||||
|
@ -5,16 +5,14 @@ const Lang = imports.lang;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const MAX_SEARCH_RESULTS_ROWS = 1;
|
||||
const MAX_SEARCH_RESULTS_ROWS = 2;
|
||||
|
||||
|
||||
function SearchResult(provider, metaInfo, terms) {
|
||||
@ -25,10 +23,10 @@ SearchResult.prototype = {
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.provider = provider;
|
||||
this.metaInfo = metaInfo;
|
||||
this.actor = new St.Button({ style_class: 'search-result',
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: true });
|
||||
this.actor = new St.Clickable({ style_class: 'search-result',
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
let content = provider.createResultActor(metaInfo, terms);
|
||||
@ -37,7 +35,9 @@ SearchResult.prototype = {
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
||||
{ createIcon: this.metaInfo['createIcon'] });
|
||||
{ createIcon: Lang.bind(this, function(size) {
|
||||
return this.metaInfo['icon'];
|
||||
})});
|
||||
content.set_child(icon.actor);
|
||||
}
|
||||
this._content = content;
|
||||
@ -50,10 +50,6 @@ SearchResult.prototype = {
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.beginItemDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-cancelled',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.cancelledItemDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
@ -72,7 +68,7 @@ SearchResult.prototype = {
|
||||
Main.overview.toggle();
|
||||
},
|
||||
|
||||
_onResultClicked: function(actor) {
|
||||
_onResultClicked: function(actor, event) {
|
||||
this.activate();
|
||||
},
|
||||
|
||||
@ -84,11 +80,11 @@ SearchResult.prototype = {
|
||||
return new Clutter.Clone({ source: this.metaInfo['icon'] });
|
||||
},
|
||||
|
||||
shellWorkspaceLaunch: function(params) {
|
||||
shellWorkspaceLaunch: function() {
|
||||
if (this.provider.dragActivateResult)
|
||||
this.provider.dragActivateResult(this.metaInfo.id, params);
|
||||
this.provider.dragActivateResult(this.metaInfo.id);
|
||||
else
|
||||
this.provider.activateResult(this.metaInfo.id, params);
|
||||
this.provider.activateResult(this.metaInfo.id);
|
||||
}
|
||||
};
|
||||
|
||||
@ -105,30 +101,8 @@ GridSearchResults.prototype = {
|
||||
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
this.actor = new St.Bin({ x_align: St.Align.START });
|
||||
|
||||
this.actor.set_child(this._grid.actor);
|
||||
this.selectionIndex = -1;
|
||||
this._width = 0;
|
||||
this.actor.connect('notify::width', Lang.bind(this, function() {
|
||||
this._width = this.actor.width;
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||
this._tryAddResults();
|
||||
}));
|
||||
}));
|
||||
this._notDisplayedResult = [];
|
||||
this._terms = [];
|
||||
},
|
||||
|
||||
_tryAddResults: function() {
|
||||
let canDisplay = this._grid.childrenInRow(this._width) * MAX_SEARCH_RESULTS_ROWS
|
||||
- this._grid.visibleItemsCount();
|
||||
|
||||
for (let i = Math.min(this._notDisplayedResult.length, canDisplay); i > 0; i--) {
|
||||
let result = this._notDisplayedResult.shift();
|
||||
let meta = this.provider.getResultMeta(result);
|
||||
let display = new SearchResult(this.provider, meta, this._terms);
|
||||
this._grid.addItem(display.actor);
|
||||
}
|
||||
},
|
||||
|
||||
getVisibleResultCount: function() {
|
||||
@ -136,15 +110,15 @@ GridSearchResults.prototype = {
|
||||
},
|
||||
|
||||
renderResults: function(results, terms) {
|
||||
// copy the lists
|
||||
this._notDisplayedResult = results.slice(0);
|
||||
this._terms = terms.slice(0);
|
||||
this._tryAddResults();
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
let result = results[i];
|
||||
let meta = this.provider.getResultMeta(result);
|
||||
let display = new SearchResult(this.provider, meta, terms);
|
||||
this._grid.addItem(display.actor);
|
||||
}
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this._terms = [];
|
||||
this._notDisplayedResult = [];
|
||||
this._grid.removeAll();
|
||||
this.selectionIndex = -1;
|
||||
},
|
||||
@ -175,41 +149,28 @@ GridSearchResults.prototype = {
|
||||
};
|
||||
|
||||
|
||||
function SearchResults(searchSystem, openSearchSystem) {
|
||||
this._init(searchSystem, openSearchSystem);
|
||||
function SearchResults(searchSystem) {
|
||||
this._init(searchSystem);
|
||||
}
|
||||
|
||||
SearchResults.prototype = {
|
||||
_init: function(searchSystem, openSearchSystem) {
|
||||
_init: function(searchSystem) {
|
||||
this._searchSystem = searchSystem;
|
||||
this._openSearchSystem = openSearchSystem;
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'searchResults',
|
||||
vertical: true });
|
||||
|
||||
this.actor = new St.Bin({ name: 'searchResults',
|
||||
y_align: St.Align.START,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
this._content = new St.BoxLayout({ name: 'searchResultsContent',
|
||||
vertical: true });
|
||||
|
||||
let scrollView = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
vfade: true });
|
||||
vshadows: true });
|
||||
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
scrollView.add_actor(this._content);
|
||||
|
||||
this.actor.add(scrollView, { x_fill: true,
|
||||
y_fill: false,
|
||||
expand: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||
function() {
|
||||
if (!this.actor.mapped)
|
||||
return;
|
||||
|
||||
let adjustment = scrollView.vscroll.adjustment;
|
||||
let direction = Overview.SwipeScrollDirection.VERTICAL;
|
||||
Main.overview.setScrollAdjustment(adjustment, direction);
|
||||
}));
|
||||
this.actor.set_child(scrollView);
|
||||
|
||||
this._statusText = new St.Label({ style_class: 'search-statustext' });
|
||||
this._content.add(this._statusText);
|
||||
@ -218,59 +179,23 @@ SearchResults.prototype = {
|
||||
this._providerMeta = [];
|
||||
for (let i = 0; i < this._providers.length; i++)
|
||||
this.createProviderMeta(this._providers[i]);
|
||||
|
||||
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
|
||||
this.actor.add(this._searchProvidersBox);
|
||||
|
||||
this._openSearchProviders = [];
|
||||
this._openSearchSystem.connect('changed', Lang.bind(this, this._updateOpenSearchProviderButtons));
|
||||
this._updateOpenSearchProviderButtons();
|
||||
},
|
||||
|
||||
_updateOpenSearchProviderButtons: function() {
|
||||
this._selectedOpenSearchButton = -1;
|
||||
for (let i = 0; i < this._openSearchProviders.length; i++)
|
||||
this._openSearchProviders[i].actor.destroy();
|
||||
this._openSearchProviders = this._openSearchSystem.getProviders();
|
||||
for (let i = 0; i < this._openSearchProviders.length; i++)
|
||||
this._createOpenSearchProviderButton(this._openSearchProviders[i]);
|
||||
},
|
||||
|
||||
_updateOpenSearchButtonState: function() {
|
||||
for (let i = 0; i < this._openSearchProviders.length; i++) {
|
||||
if (i == this._selectedOpenSearchButton)
|
||||
this._openSearchProviders[i].actor.add_style_pseudo_class('selected');
|
||||
else
|
||||
this._openSearchProviders[i].actor.remove_style_pseudo_class('selected');
|
||||
}
|
||||
},
|
||||
|
||||
_createOpenSearchProviderButton: function(provider) {
|
||||
let button = new St.Button({ style_class: 'dash-search-button',
|
||||
reactive: true,
|
||||
x_fill: true,
|
||||
y_align: St.Align.MIDDLE });
|
||||
let bin = new St.Bin({ x_fill: false,
|
||||
x_align:St.Align.MIDDLE });
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
this._openSearchSystem.activateResult(provider.id);
|
||||
}));
|
||||
let title = new St.Label({ text: provider.name,
|
||||
style_class: 'dash-search-button-label' });
|
||||
|
||||
bin.set_child(title);
|
||||
button.set_child(bin);
|
||||
provider.actor = button;
|
||||
|
||||
this._searchProvidersBox.add(button);
|
||||
},
|
||||
|
||||
createProviderMeta: function(provider) {
|
||||
let providerBox = new St.BoxLayout({ style_class: 'search-section',
|
||||
vertical: true });
|
||||
let title = new St.Label({ style_class: 'search-section-header',
|
||||
text: provider.title });
|
||||
providerBox.add(title);
|
||||
let titleButton = new St.Button({ style_class: 'search-section-header',
|
||||
reactive: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
titleButton.connect('clicked', Lang.bind(this, function () { this._onHeaderClicked(provider); }));
|
||||
providerBox.add(titleButton);
|
||||
let titleBox = new St.BoxLayout();
|
||||
titleButton.set_child(titleBox);
|
||||
let title = new St.Label({ text: provider.title });
|
||||
let count = new St.Label();
|
||||
titleBox.add(title, { expand: true });
|
||||
titleBox.add(count);
|
||||
|
||||
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
|
||||
x_fill: true,
|
||||
@ -283,7 +208,8 @@ SearchResults.prototype = {
|
||||
resultDisplayBin.set_child(resultDisplay.actor);
|
||||
|
||||
this._providerMeta.push({ actor: providerBox,
|
||||
resultDisplay: resultDisplay });
|
||||
resultDisplay: resultDisplay,
|
||||
count: count });
|
||||
this._content.add(providerBox);
|
||||
},
|
||||
|
||||
@ -301,8 +227,6 @@ SearchResults.prototype = {
|
||||
this._searchSystem.reset();
|
||||
this._statusText.hide();
|
||||
this._clearDisplay();
|
||||
this._selectedOpenSearchButton = -1;
|
||||
this._updateOpenSearchButtonState();
|
||||
},
|
||||
|
||||
startingSearch: function() {
|
||||
@ -323,28 +247,30 @@ SearchResults.prototype = {
|
||||
if (results.length == 0) {
|
||||
this._statusText.set_text(_("No matching results."));
|
||||
this._statusText.show();
|
||||
return true;
|
||||
} else {
|
||||
this._selectedOpenSearchButton = -1;
|
||||
this._updateOpenSearchButtonState();
|
||||
this._statusText.hide();
|
||||
}
|
||||
|
||||
let terms = this._searchSystem.getTerms();
|
||||
this._openSearchSystem.setSearchTerms(terms);
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
let [provider, providerResults] = results[i];
|
||||
let meta = this._metaForProvider(provider);
|
||||
meta.actor.show();
|
||||
meta.resultDisplay.renderResults(providerResults, terms);
|
||||
meta.count.set_text('' + providerResults.length);
|
||||
}
|
||||
|
||||
if (this._selectedOpenSearchButton == -1)
|
||||
this.selectDown(false);
|
||||
this.selectDown(false);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_onHeaderClicked: function(provider) {
|
||||
provider.expandSearch(this._searchSystem.getTerms());
|
||||
},
|
||||
|
||||
_modifyActorSelection: function(resultDisplay, up) {
|
||||
let success;
|
||||
let index = resultDisplay.getSelectionIndex();
|
||||
@ -358,26 +284,16 @@ SearchResults.prototype = {
|
||||
},
|
||||
|
||||
selectUp: function(recursing) {
|
||||
if (this._selectedOpenSearchButton == -1) {
|
||||
for (let i = this._selectedProvider; i >= 0; i--) {
|
||||
let meta = this._providerMeta[i];
|
||||
if (!meta.actor.visible)
|
||||
continue;
|
||||
let success = this._modifyActorSelection(meta.resultDisplay, true);
|
||||
if (success) {
|
||||
this._selectedProvider = i;
|
||||
return;
|
||||
}
|
||||
for (let i = this._selectedProvider; i >= 0; i--) {
|
||||
let meta = this._providerMeta[i];
|
||||
if (!meta.actor.visible)
|
||||
continue;
|
||||
let success = this._modifyActorSelection(meta.resultDisplay, true);
|
||||
if (success) {
|
||||
this._selectedProvider = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._selectedOpenSearchButton == -1)
|
||||
this._selectedOpenSearchButton = this._openSearchProviders.length;
|
||||
this._selectedOpenSearchButton--;
|
||||
this._updateOpenSearchButtonState();
|
||||
if (this._selectedOpenSearchButton >= 0)
|
||||
return;
|
||||
|
||||
if (this._providerMeta.length > 0 && !recursing) {
|
||||
this._selectedProvider = this._providerMeta.length - 1;
|
||||
this.selectUp(true);
|
||||
@ -386,30 +302,18 @@ SearchResults.prototype = {
|
||||
|
||||
selectDown: function(recursing) {
|
||||
let current = this._selectedProvider;
|
||||
if (this._selectedOpenSearchButton == -1) {
|
||||
if (current == -1)
|
||||
current = 0;
|
||||
for (let i = current; i < this._providerMeta.length; i++) {
|
||||
let meta = this._providerMeta[i];
|
||||
if (!meta.actor.visible)
|
||||
continue;
|
||||
let success = this._modifyActorSelection(meta.resultDisplay, false);
|
||||
if (success) {
|
||||
this._selectedProvider = i;
|
||||
return;
|
||||
}
|
||||
if (current == -1)
|
||||
current = 0;
|
||||
for (let i = current; i < this._providerMeta.length; i++) {
|
||||
let meta = this._providerMeta[i];
|
||||
if (!meta.actor.visible)
|
||||
continue;
|
||||
let success = this._modifyActorSelection(meta.resultDisplay, false);
|
||||
if (success) {
|
||||
this._selectedProvider = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._selectedOpenSearchButton++;
|
||||
|
||||
if (this._selectedOpenSearchButton < this._openSearchProviders.length) {
|
||||
this._updateOpenSearchButtonState();
|
||||
return;
|
||||
}
|
||||
|
||||
this._selectedOpenSearchButton = -1;
|
||||
this._updateOpenSearchButtonState();
|
||||
|
||||
if (this._providerMeta.length > 0 && !recursing) {
|
||||
this._selectedProvider = 0;
|
||||
this.selectDown(true);
|
||||
@ -417,13 +321,6 @@ SearchResults.prototype = {
|
||||
},
|
||||
|
||||
activateSelected: function() {
|
||||
if (this._selectedOpenSearchButton != -1) {
|
||||
let provider = this._openSearchProviders[this._selectedOpenSearchButton];
|
||||
this._openSearchSystem.activateResult(provider.id);
|
||||
Main.overview.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
let current = this._selectedProvider;
|
||||
if (current < 0)
|
||||
return;
|
||||
|
@ -10,20 +10,24 @@ const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
|
||||
const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable';
|
||||
const KEY_SLOW_KEYS_ENABLED = 'slowkeys-enable';
|
||||
const KEY_MOUSE_KEYS_ENABLED = 'mousekeys-enable';
|
||||
const A11Y_SCHEMA = "org.gnome.desktop.a11y.keyboard";
|
||||
const KEY_STICKY_KEYS_ENABLED = "stickykeys-enable";
|
||||
const KEY_BOUNCE_KEYS_ENABLED = "bouncekeys-enable";
|
||||
const KEY_SLOW_KEYS_ENABLED = "slowkeys-enable";
|
||||
const KEY_MOUSE_KEYS_ENABLED = "mousekeys-enable";
|
||||
|
||||
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
const AT_SCREEN_KEYBOARD_SCHEMA = "org.gnome.desktop.default-applications.at.mobility";
|
||||
const AT_SCREEN_READER_SCHEMA = "org.gnome.desktop.default-applications.at.visual";
|
||||
|
||||
const XSETTINGS_SCHEMA = "org.gnome.settings-daemon.plugins.xsettings";
|
||||
const KEY_DPI = "dpi";
|
||||
|
||||
const DPI_LOW_REASONABLE_VALUE = 50;
|
||||
const DPI_HIGH_REASONABLE_VALUE = 500;
|
||||
@ -31,16 +35,32 @@ const DPI_HIGH_REASONABLE_VALUE = 500;
|
||||
const DPI_FACTOR_LARGE = 1.25;
|
||||
const DPI_FACTOR_LARGER = 1.5;
|
||||
const DPI_FACTOR_LARGEST = 2.0;
|
||||
const DPI_DEFAULT = 96;
|
||||
|
||||
const KEY_META_DIR = '/apps/metacity/general';
|
||||
const KEY_VISUAL_BELL = KEY_META_DIR + '/visual_bell';
|
||||
const KEY_META_DIR = "/apps/metacity/general";
|
||||
const KEY_VISUAL_BELL = KEY_META_DIR + "/visual_bell";
|
||||
|
||||
const DESKTOP_INTERFACE_SCHEMA = 'org.gnome.desktop.interface';
|
||||
const KEY_GTK_THEME = 'gtk-theme';
|
||||
const KEY_ICON_THEME = 'icon-theme';
|
||||
const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor';
|
||||
const DESKTOP_INTERFACE_SCHEMA = "org.gnome.desktop.interface";
|
||||
const KEY_GTK_THEME = "gtk-theme";
|
||||
const KEY_ICON_THEME = "icon-theme";
|
||||
|
||||
const HIGH_CONTRAST_THEME = 'HighContrast';
|
||||
const HIGH_CONTRAST_THEME = "HighContrast";
|
||||
|
||||
function getDPIFromX() {
|
||||
let screen = global.get_gdk_screen();
|
||||
if (screen) {
|
||||
let width_dpi = (screen.get_width() / (screen.get_width_mm() / 25.4));
|
||||
let height_dpi = (screen.get_height() / (screen.get_height_mm() / 25.4));
|
||||
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)
|
||||
return DPI_DEFAULT;
|
||||
else
|
||||
return (width_dpi + height_dpi) / 2;
|
||||
}
|
||||
return DPI_DEFAULT;
|
||||
}
|
||||
|
||||
function ATIndicator() {
|
||||
this._init.apply(this, arguments);
|
||||
@ -59,20 +79,17 @@ ATIndicator.prototype = {
|
||||
let highContrast = this._buildHCItem();
|
||||
this.menu.addMenuItem(highContrast);
|
||||
|
||||
let magnifier = this._buildItem(_("Zoom"), APPLICATIONS_SCHEMA,
|
||||
'screen-magnifier-enabled');
|
||||
let magnifier = this._buildMagItem();
|
||||
this.menu.addMenuItem(magnifier);
|
||||
|
||||
let textZoom = this._buildFontItem();
|
||||
this.menu.addMenuItem(textZoom);
|
||||
|
||||
// let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
|
||||
// 'screen-reader-enabled');
|
||||
// this.menu.addMenuItem(screenReader);
|
||||
let screenReader = this._buildItem(_("Screen Reader"), AT_SCREEN_READER_SCHEMA, 'startup');
|
||||
this.menu.addMenuItem(screenReader);
|
||||
|
||||
// let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
|
||||
// 'screen-keyboard-enabled');
|
||||
// this.menu.addMenuItem(screenKeyboard);
|
||||
let screenKeyboard = this._buildItem(_("Screen Keyboard"), AT_SCREEN_KEYBOARD_SCHEMA, 'startup');
|
||||
this.menu.addMenuItem(screenKeyboard);
|
||||
|
||||
let visualBell = this._buildItemGConf(_("Visual Alerts"), client, KEY_VISUAL_BELL);
|
||||
this.menu.addMenuItem(visualBell);
|
||||
@ -91,8 +108,8 @@ ATIndicator.prototype = {
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Universal Access Settings"), function() {
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-universal-access-panel.desktop');
|
||||
app.activate(-1);
|
||||
let p = new Shell.Process({ args: ['gnome-control-center','universal-access'] });
|
||||
p.run();
|
||||
});
|
||||
},
|
||||
|
||||
@ -150,12 +167,9 @@ ATIndicator.prototype = {
|
||||
if (enabled) {
|
||||
settings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME);
|
||||
settings.set_string(KEY_ICON_THEME, HIGH_CONTRAST_THEME);
|
||||
} else if(!hasHC) {
|
||||
} else {
|
||||
settings.set_string(KEY_GTK_THEME, gtkTheme);
|
||||
settings.set_string(KEY_ICON_THEME, iconTheme);
|
||||
} else {
|
||||
settings.reset(KEY_GTK_THEME);
|
||||
settings.reset(KEY_ICON_THEME);
|
||||
}
|
||||
});
|
||||
settings.connect('changed::' + KEY_GTK_THEME, function() {
|
||||
@ -176,23 +190,42 @@ ATIndicator.prototype = {
|
||||
},
|
||||
|
||||
_buildFontItem: function() {
|
||||
let settings = new Gio.Settings({ schema: DESKTOP_INTERFACE_SCHEMA });
|
||||
let settings = new Gio.Settings({ schema: XSETTINGS_SCHEMA });
|
||||
|
||||
let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
|
||||
let initial_setting = (factor > 1.0);
|
||||
// we assume this never changes (which is not true if resolution
|
||||
// is changed, but we would need XRandR events for that)
|
||||
let x_value = getDPIFromX();
|
||||
let user_value;
|
||||
function on_get() {
|
||||
user_value = settings.get_double(KEY_DPI);
|
||||
return (user_value - (DPI_FACTOR_LARGE * x_value) > -1);
|
||||
}
|
||||
let initial_setting = on_get();
|
||||
let default_value = initial_setting ? x_value : user_value;
|
||||
let widget = this._buildItemExtended(_("Large Text"),
|
||||
initial_setting,
|
||||
settings.is_writable(KEY_TEXT_SCALING_FACTOR),
|
||||
settings.is_writable(KEY_DPI),
|
||||
function (enabled) {
|
||||
if (enabled)
|
||||
settings.set_double(KEY_TEXT_SCALING_FACTOR,
|
||||
DPI_FACTOR_LARGE);
|
||||
settings.set_double(KEY_DPI, DPI_FACTOR_LARGE * default_value);
|
||||
else
|
||||
settings.reset(KEY_TEXT_SCALING_FACTOR);
|
||||
settings.set_double(KEY_DPI, default_value);
|
||||
});
|
||||
settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, function() {
|
||||
let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
|
||||
let active = (factor > 1.0);
|
||||
settings.connect('changed::' + KEY_DPI, function() {
|
||||
let active = on_get();
|
||||
default_value = active ? x_value : user_value;
|
||||
widget.setToggleState(active);
|
||||
});
|
||||
return widget;
|
||||
},
|
||||
|
||||
_buildMagItem: function() {
|
||||
let mag = Main.magnifier;
|
||||
let widget = this._buildItemExtended(_("Zoom"),
|
||||
mag.isActive(),
|
||||
true,
|
||||
Lang.bind(mag, mag.setActive));
|
||||
mag.connect('active-changed', function(magnifier, active) {
|
||||
widget.setToggleState(active);
|
||||
});
|
||||
return widget;
|
||||
|
@ -1,494 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const ConnectionState = {
|
||||
DISCONNECTED: 0,
|
||||
CONNECTED: 1,
|
||||
DISCONNECTING: 2,
|
||||
CONNECTING: 3
|
||||
}
|
||||
|
||||
function Indicator() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
Indicator.prototype = {
|
||||
__proto__: PanelMenu.SystemStatusButton.prototype,
|
||||
|
||||
_init: function() {
|
||||
PanelMenu.SystemStatusButton.prototype._init.call(this, 'bluetooth-disabled', null);
|
||||
|
||||
GLib.spawn_command_line_sync ('pkill -f "^bluetooth-applet$"');
|
||||
this._applet = new GnomeBluetoothApplet.Applet();
|
||||
|
||||
this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
|
||||
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
|
||||
this._killswitch.connect('toggled', Lang.bind(this, function() {
|
||||
let current_state = this._applet.killswitch_state;
|
||||
if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED &&
|
||||
current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) {
|
||||
this._applet.killswitch_state = this._killswitch.state ?
|
||||
GnomeBluetoothApplet.KillswitchState.UNBLOCKED:
|
||||
GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED;
|
||||
} else
|
||||
this._killswitch.setToggleState(false);
|
||||
}));
|
||||
|
||||
this._discoverable = new PopupMenu.PopupSwitchMenuItem(_("Visibility"), this._applet.discoverable);
|
||||
this._applet.connect('notify::discoverable', Lang.bind(this, function() {
|
||||
this._discoverable.setToggleState(this._applet.discoverable);
|
||||
}));
|
||||
this._discoverable.connect('toggled', Lang.bind(this, function() {
|
||||
this._applet.discoverable = this._discoverable.state;
|
||||
}));
|
||||
|
||||
this._updateKillswitch();
|
||||
this.menu.addMenuItem(this._killswitch);
|
||||
this.menu.addMenuItem(this._discoverable);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
|
||||
new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
|
||||
new PopupMenu.PopupMenuItem(_("Setup a New Device...")),
|
||||
new PopupMenu.PopupSeparatorMenuItem()];
|
||||
this._hasDevices = false;
|
||||
this._deviceSep = this._fullMenuItems[0]; // hidden if no device exists
|
||||
|
||||
this._fullMenuItems[1].connect('activate', function() {
|
||||
GLib.spawn_command_line_async('bluetooth-sendto');
|
||||
});
|
||||
this._fullMenuItems[2].connect('activate', function() {
|
||||
GLib.spawn_command_line_async('bluetooth-wizard');
|
||||
});
|
||||
|
||||
for (let i = 0; i < this._fullMenuItems.length; i++) {
|
||||
let item = this._fullMenuItems[i];
|
||||
this.menu.addMenuItem(item);
|
||||
}
|
||||
|
||||
this._deviceItemPosition = 3;
|
||||
this._deviceItems = [];
|
||||
this._applet.connect('devices-changed', Lang.bind(this, this._updateDevices));
|
||||
this._updateDevices();
|
||||
|
||||
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
|
||||
this._updateFullMenu();
|
||||
|
||||
this.menu.addAction(_("Bluetooth Settings"), function() {
|
||||
let app = Shell.AppSystem.get_default().get_app('bluetooth-properties.desktop');
|
||||
app.activate(-1);
|
||||
});
|
||||
|
||||
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
|
||||
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
|
||||
this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
|
||||
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
},
|
||||
|
||||
_updateKillswitch: function() {
|
||||
let current_state = this._applet.killswitch_state;
|
||||
let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED;
|
||||
let has_adapter = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER;
|
||||
let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER &&
|
||||
current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
|
||||
|
||||
this._killswitch.setToggleState(on);
|
||||
this._killswitch.actor.reactive = can_toggle;
|
||||
|
||||
if (has_adapter)
|
||||
this.actor.show();
|
||||
else
|
||||
this.actor.hide();
|
||||
|
||||
if (on) {
|
||||
this._discoverable.actor.show();
|
||||
this.setIcon('bluetooth-active');
|
||||
} else {
|
||||
this._discoverable.actor.hide();
|
||||
this.setIcon('bluetooth-disabled');
|
||||
}
|
||||
},
|
||||
|
||||
_deviceCompare: function(d1, d2) {
|
||||
return d1.device_path == d2.device_path &&
|
||||
d1.bdaddr == d2.bdaddr &&
|
||||
d1.can_connect == d2.can_connect &&
|
||||
d1.capabilities == d2.capabilities;
|
||||
},
|
||||
|
||||
_updateDevices: function() {
|
||||
let devices = this._applet.get_devices();
|
||||
|
||||
let newlist = [ ];
|
||||
for (let i = 0; i < this._deviceItems.length; i++) {
|
||||
let item = this._deviceItems[i];
|
||||
let destroy = true;
|
||||
for (let j = 0; j < devices.length; j++) {
|
||||
// we need to deep compare because BluetoothSimpleDevice is a boxed type
|
||||
// (but we take advantage of that, because _skip will disappear the next
|
||||
// time get_devices() is called)
|
||||
if (this._deviceCompare(item._device, devices[j])) {
|
||||
item.label.text = devices[j].alias;
|
||||
devices[j]._skip = true;
|
||||
destroy = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (destroy)
|
||||
item.destroy();
|
||||
else
|
||||
newlist.push(item);
|
||||
}
|
||||
|
||||
this._deviceItems = newlist;
|
||||
this._hasDevices = newlist.length > 0;
|
||||
for (let i = 0; i < devices.length; i++) {
|
||||
let d = devices[i];
|
||||
if (d._skip)
|
||||
continue;
|
||||
let item = this._createDeviceItem(d);
|
||||
if (item) {
|
||||
this.menu.addMenuItem(item, this._deviceItemPosition + this._deviceItems.length);
|
||||
this._deviceItems.push(item);
|
||||
this._hasDevices = true;
|
||||
}
|
||||
}
|
||||
if (this._hasDevices)
|
||||
this._deviceSep.actor.show();
|
||||
else
|
||||
this._deviceSep.actor.hide();
|
||||
},
|
||||
|
||||
_createDeviceItem: function(device) {
|
||||
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
|
||||
return null;
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(device.alias);
|
||||
item._device = device;
|
||||
|
||||
if (device.can_connect) {
|
||||
item._connected = device.connected;
|
||||
let menuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
|
||||
|
||||
menuitem.connect('toggled', Lang.bind(this, function() {
|
||||
if (item._connected > ConnectionState.CONNECTED) {
|
||||
// operation already in progress, revert
|
||||
menuitem.setToggleState(menuitem.state);
|
||||
}
|
||||
if (item._connected) {
|
||||
item._connected = ConnectionState.DISCONNECTING;
|
||||
this._applet.disconnect_device(item._device.device_path, function(applet, success) {
|
||||
if (success) { // apply
|
||||
item._connected = ConnectionState.DISCONNECTED;
|
||||
menuitem.setToggleState(false);
|
||||
} else { // revert
|
||||
item._connected = ConnectionState.CONNECTED;
|
||||
menuitem.setToggleState(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
item._connected = ConnectionState.CONNECTING;
|
||||
this._applet.connect_device(item._device.device_path, function(applet, success) {
|
||||
if (success) { // apply
|
||||
item._connected = ConnectionState.CONNECTED;
|
||||
menuitem.setToggleState(true);
|
||||
} else { // revert
|
||||
item._connected = ConnectionState.DISCONNECTED;
|
||||
menuitem.setToggleState(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
item.menu.addMenuItem(menuitem);
|
||||
}
|
||||
|
||||
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
|
||||
item.menu.addAction(_("Send Files..."), Lang.bind(this, function() {
|
||||
this._applet.send_to_address(device.bdaddr, device.alias);
|
||||
}));
|
||||
}
|
||||
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_FILE_TRANSFER) {
|
||||
item.menu.addAction(_("Browse Files..."), Lang.bind(this, function(event) {
|
||||
this._applet.browse_address(device.bdaddr, event.get_time(),
|
||||
Lang.bind(this, function(applet, result) {
|
||||
try {
|
||||
applet.browse_address_finish(result);
|
||||
} catch (e) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new MessageTray.Notification(this._source,
|
||||
_("Bluetooth"),
|
||||
_("Error browsing device"),
|
||||
{ body: _("The requested device cannot be browsed, error is '%s'").format(e) }));
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
switch (device.type) {
|
||||
case GnomeBluetoothApplet.Type.KEYBOARD:
|
||||
item.menu.addAction(_("Keyboard Settings"), function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center keyboard');
|
||||
});
|
||||
break;
|
||||
case GnomeBluetoothApplet.Type.MOUSE:
|
||||
item.menu.addAction(_("Mouse Settings"), function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center mouse');
|
||||
});
|
||||
break;
|
||||
case GnomeBluetoothApplet.Type.HEADSET:
|
||||
case GnomeBluetoothApplet.Type.HEADPHONES:
|
||||
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
|
||||
item.menu.addAction(_("Sound Settings"), function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center sound');
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
_updateFullMenu: function() {
|
||||
if (this._applet.show_full_menu) {
|
||||
this._showAll(this._fullMenuItems);
|
||||
if (this._hasDevices)
|
||||
this._showAll(this._deviceItems);
|
||||
else
|
||||
this._deviceSep.actor.hide();
|
||||
} else {
|
||||
this._hideAll(this._fullMenuItems);
|
||||
this._hideAll(this._deviceItems);
|
||||
}
|
||||
},
|
||||
|
||||
_showAll: function(items) {
|
||||
for (let i = 0; i < items.length; i++)
|
||||
items[i].actor.show();
|
||||
},
|
||||
|
||||
_hideAll: function(items) {
|
||||
for (let i = 0; i < items.length; i++)
|
||||
items[i].actor.hide();
|
||||
},
|
||||
|
||||
_destroyAll: function(items) {
|
||||
for (let i = 0; i < items.length; i++)
|
||||
items[i].destroy();
|
||||
},
|
||||
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new Source();
|
||||
Main.messageTray.add(this._source);
|
||||
}
|
||||
},
|
||||
|
||||
_authRequest: function(applet, device_path, name, long_name, uuid) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name, uuid));
|
||||
},
|
||||
|
||||
_confirmRequest: function(applet, device_path, name, long_name, pin) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new ConfirmNotification(this._source, this._applet, device_path, name, long_name, pin));
|
||||
},
|
||||
|
||||
_pinRequest: function(applet, device_path, name, long_name, numeric) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new PinNotification(this._source, this._applet, device_path, name, long_name, numeric));
|
||||
},
|
||||
|
||||
_cancelRequest: function() {
|
||||
this._source.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
function Source() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
Source.prototype = {
|
||||
__proto__: MessageTray.Source.prototype,
|
||||
|
||||
_init: function() {
|
||||
MessageTray.Source.prototype._init.call(this, _("Bluetooth"));
|
||||
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
},
|
||||
|
||||
notify: function(notification) {
|
||||
this._private_destroyId = notification.connect('destroy', Lang.bind(this, function(notification) {
|
||||
if (this.notification == notification) {
|
||||
// the destroyed notification is the last for this source
|
||||
this.notification.disconnect(this._private_destroyId);
|
||||
this.destroy();
|
||||
}
|
||||
}));
|
||||
|
||||
MessageTray.Source.prototype.notify.call(this, notification);
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ icon_name: 'bluetooth-active',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this.ICON_SIZE });
|
||||
}
|
||||
}
|
||||
|
||||
function AuthNotification() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
AuthNotification.prototype = {
|
||||
__proto__: MessageTray.Notification.prototype,
|
||||
|
||||
_init: function(source, applet, device_path, name, long_name, uuid) {
|
||||
MessageTray.Notification.prototype._init.call(this,
|
||||
source,
|
||||
_("Bluetooth"),
|
||||
_("Authorization request from %s").format(name),
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this._applet = applet;
|
||||
this._devicePath = device_path;
|
||||
this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid));
|
||||
|
||||
this.addButton('always-grant', _("Always grant access"));
|
||||
this.addButton('grant', _("Grant this time only"));
|
||||
this.addButton('reject', _("Reject"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'always-grant':
|
||||
this._applet.agent_reply_auth(this._devicePath, true, true);
|
||||
break;
|
||||
case 'grant':
|
||||
this._applet.agent_reply_auth(this._devicePath, true, false);
|
||||
break;
|
||||
case 'reject':
|
||||
default:
|
||||
this._applet.agent_reply_auth(this._devicePath, false, false);
|
||||
}
|
||||
this.destroy();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function ConfirmNotification() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
ConfirmNotification.prototype = {
|
||||
__proto__: MessageTray.Notification.prototype,
|
||||
|
||||
_init: function(source, applet, device_path, name, long_name, pin) {
|
||||
MessageTray.Notification.prototype._init.call(this,
|
||||
source,
|
||||
_("Bluetooth"),
|
||||
_("Pairing confirmation for %s").format(name),
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this._applet = applet;
|
||||
this._devicePath = device_path;
|
||||
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
||||
this.addBody(_("Please confirm whether the PIN '%s' matches the one on the device.").format(pin));
|
||||
|
||||
this.addButton('matches', _("Matches"));
|
||||
this.addButton('does-not-match', _("Does not match"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
if (action == 'matches')
|
||||
this._applet.agent_reply_confirm(this._devicePath, true);
|
||||
else
|
||||
this._applet.agent_reply_confirm(this._devicePath, false);
|
||||
this.destroy();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function PinNotification() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
PinNotification.prototype = {
|
||||
__proto__: MessageTray.Notification.prototype,
|
||||
|
||||
_init: function(source, applet, device_path, name, long_name, numeric) {
|
||||
MessageTray.Notification.prototype._init.call(this,
|
||||
source,
|
||||
_("Bluetooth"),
|
||||
_("Pairing request for %s").format(name),
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this._applet = applet;
|
||||
this._devicePath = device_path;
|
||||
this._numeric = numeric;
|
||||
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
||||
this.addBody(_("Please enter the PIN mentioned on the device."));
|
||||
|
||||
this._entry = new St.Entry();
|
||||
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
|
||||
let key = event.get_key_symbol();
|
||||
if (key == Clutter.KEY_Return) {
|
||||
this.emit('action-invoked', 'ok');
|
||||
return true;
|
||||
} else if (key == Clutter.KEY_Escape) {
|
||||
this.emit('action-invoked', 'cancel');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
this.addActor(this._entry);
|
||||
|
||||
this.addButton('ok', _("OK"));
|
||||
this.addButton('cancel', _("Cancel"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
if (action == 'ok') {
|
||||
if (this._numeric) {
|
||||
let num = parseInt(this._entry.text);
|
||||
if (isNaN(num)) {
|
||||
// user reply was empty, or was invalid
|
||||
// cancel the operation
|
||||
num = -1;
|
||||
}
|
||||
this._applet.agent_reply_passkey(this._devicePath, num);
|
||||
} else
|
||||
this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
|
||||
} else {
|
||||
if (this._numeric)
|
||||
this._applet.agent_reply_passkey(this._devicePath, -1);
|
||||
else
|
||||
this._applet.agent_reply_pincode(this._devicePath, null);
|
||||
}
|
||||
this.destroy();
|
||||
}));
|
||||
},
|
||||
|
||||
grabFocus: function(lockTray) {
|
||||
MessageTray.Notification.prototype.grabFocus.call(this, lockTray);
|
||||
global.stage.set_key_focus(this._entry);
|
||||
}
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const Gkbd = imports.gi.Gkbd;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
function LayoutMenuItem() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
LayoutMenuItem.prototype = {
|
||||
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
|
||||
|
||||
_init: function(config, id, indicator, long_name) {
|
||||
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
|
||||
|
||||
this._config = config;
|
||||
this._id = id;
|
||||
this.label = new St.Label({ text: long_name });
|
||||
this.indicator = indicator;
|
||||
this.addActor(this.label);
|
||||
this.addActor(this.indicator);
|
||||
},
|
||||
|
||||
activate: function(event) {
|
||||
PopupMenu.PopupBaseMenuItem.prototype.activate.call(this);
|
||||
this._config.lock_group(this._id);
|
||||
}
|
||||
};
|
||||
|
||||
function XKBIndicator() {
|
||||
this._init.call(this);
|
||||
}
|
||||
|
||||
XKBIndicator.prototype = {
|
||||
__proto__: PanelMenu.Button.prototype,
|
||||
|
||||
_init: function() {
|
||||
PanelMenu.Button.prototype._init.call(this, St.Align.START);
|
||||
|
||||
this._container = new Shell.GenericContainer();
|
||||
this._container.connect('get-preferred-width', Lang.bind(this, this._get_preferred_width));
|
||||
this._container.connect('get-preferred-height', Lang.bind(this, this._get_preferred_height));
|
||||
this._container.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this.actor.set_child(this._container);
|
||||
|
||||
this._iconActor = new St.Icon({ icon_name: 'keyboard', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
|
||||
this._container.add_actor(this._iconActor);
|
||||
this._labelActors = [ ];
|
||||
this._layoutItems = [ ];
|
||||
|
||||
this._showFlags = false;
|
||||
this._config = Gkbd.Configuration.get();
|
||||
this._config.connect('changed', Lang.bind(this, this._sync_config));
|
||||
this._config.connect('group-changed', Lang.bind(this, this._sync_group));
|
||||
this._config.start_listen();
|
||||
|
||||
this._sync_config();
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Show Keyboard Layout..."), Lang.bind(this, function() {
|
||||
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
|
||||
}));
|
||||
this.menu.addAction(_("Localization Settings"), function() {
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-region-panel.desktop');
|
||||
app.activate(-1);
|
||||
});
|
||||
},
|
||||
|
||||
_sync_config: function() {
|
||||
this._showFlags = this._config.if_flags_shown();
|
||||
if (this._showFlags) {
|
||||
this._container.set_skip_paint(this._iconActor, false);
|
||||
} else {
|
||||
this._container.set_skip_paint(this._iconActor, true);
|
||||
}
|
||||
|
||||
let groups = this._config.get_group_names();
|
||||
if (groups.length > 1) {
|
||||
this.actor.show();
|
||||
} else {
|
||||
this.menu.close();
|
||||
this.actor.hide();
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._layoutItems.length; i++)
|
||||
this._layoutItems[i].destroy();
|
||||
|
||||
for (let i = 0; i < this._labelActors.length; i++)
|
||||
this._labelActors[i].destroy();
|
||||
|
||||
let short_names = this._config.get_short_group_names();
|
||||
|
||||
this._selectedLayout = null;
|
||||
this._layoutItems = [ ];
|
||||
this._selectedLabel = null;
|
||||
this._labelActors = [ ];
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
let icon_name = this._config.get_group_name(i);
|
||||
let actor;
|
||||
if (this._showFlags)
|
||||
actor = new St.Icon({ icon_name: icon_name, icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon' });
|
||||
else
|
||||
actor = new St.Label({ text: short_names[i] });
|
||||
let item = new LayoutMenuItem(this._config, i, actor, groups[i]);
|
||||
item._short_group_name = short_names[i];
|
||||
item._icon_name = icon_name;
|
||||
this._layoutItems.push(item);
|
||||
this.menu.addMenuItem(item, i);
|
||||
|
||||
let shortLabel = new St.Label({ text: short_names[i] });
|
||||
this._labelActors.push(shortLabel);
|
||||
this._container.add_actor(shortLabel);
|
||||
this._container.set_skip_paint(shortLabel, true);
|
||||
}
|
||||
|
||||
this._sync_group();
|
||||
},
|
||||
|
||||
_sync_group: function() {
|
||||
let selected = this._config.get_current_group();
|
||||
|
||||
if (this._selectedLayout) {
|
||||
this._selectedLayout.setShowDot(false);
|
||||
this._selectedLayout = null;
|
||||
}
|
||||
|
||||
if (this._selectedLabel) {
|
||||
this._container.set_skip_paint(this._selectedLabel, true);
|
||||
this._selectedLabel = null;
|
||||
}
|
||||
|
||||
let item = this._layoutItems[selected];
|
||||
item.setShowDot(true);
|
||||
|
||||
this._iconActor.icon_name = item._icon_name;
|
||||
this._selectedLabel = this._labelActors[selected];
|
||||
this._container.set_skip_paint(this._selectedLabel, this._showFlags);
|
||||
|
||||
this._selectedLayout = item;
|
||||
},
|
||||
|
||||
_get_preferred_width: function(container, for_height, alloc) {
|
||||
/* Here, and in _get_preferred_height, we need to query for the
|
||||
height of all children, but we ignore the results for those
|
||||
we don't actually display. */
|
||||
let max_min_width = 0, max_natural_width = 0;
|
||||
if (this._showFlags)
|
||||
[max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height);
|
||||
|
||||
for (let i = 0; i < this._labelActors.length; i++) {
|
||||
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
|
||||
if (!this._showFlags) {
|
||||
max_min_width = Math.max(max_min_width, min_width);
|
||||
max_natural_width = Math.max(max_natural_width, natural_width);
|
||||
}
|
||||
}
|
||||
|
||||
alloc.min_size = max_min_width;
|
||||
alloc.natural_size = max_natural_width;
|
||||
},
|
||||
|
||||
_get_preferred_height: function(container, for_width, alloc) {
|
||||
let max_min_height = 0, max_natural_height = 0;
|
||||
if (this._showFlags)
|
||||
[max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width);
|
||||
|
||||
for (let i = 0; i < this._labelActors.length; i++) {
|
||||
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
|
||||
if (!this._showFlags) {
|
||||
max_min_height = Math.max(max_min_height, min_height);
|
||||
max_natural_height = Math.max(max_natural_height, natural_height);
|
||||
}
|
||||
}
|
||||
|
||||
alloc.min_size = max_min_height;
|
||||
alloc.natural_size = max_natural_height;
|
||||
},
|
||||
|
||||
_allocate: function(container, box, flags) {
|
||||
// translate box to (0, 0)
|
||||
box.x2 -= box.x1;
|
||||
box.x1 = 0;
|
||||
box.y2 -= box.y1;
|
||||
box.y1 = 0;
|
||||
|
||||
this._iconActor.allocate_align_fill(box, 0.5, 0, false, false, flags);
|
||||
for (let i = 0; i < this._labelActors.length; i++)
|
||||
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
|
||||
}
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const DBus = imports.dbus;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
@ -9,7 +10,6 @@ const St = imports.gi.St;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
@ -26,10 +26,7 @@ const UPDeviceType = {
|
||||
MOUSE: 5,
|
||||
KEYBOARD: 6,
|
||||
PDA: 7,
|
||||
PHONE: 8,
|
||||
MEDIA_PLAYER: 9,
|
||||
TABLET: 10,
|
||||
COMPUTER: 11
|
||||
PHONE: 8
|
||||
};
|
||||
|
||||
const UPDeviceState = {
|
||||
@ -49,7 +46,7 @@ const PowerManagerInterface = {
|
||||
{ name: 'GetPrimaryDevice', inSignature: '', outSignature: '(susbut)' },
|
||||
],
|
||||
signals: [
|
||||
{ name: 'Changed', inSignature: '' },
|
||||
{ name: 'Changed', outSignature: '' },
|
||||
],
|
||||
properties: [
|
||||
{ name: 'Icon', signature: 's', access: 'read' },
|
||||
@ -71,20 +68,17 @@ Indicator.prototype = {
|
||||
this._deviceItems = [ ];
|
||||
this._hasPrimary = false;
|
||||
this._primaryDeviceId = null;
|
||||
|
||||
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._primaryPercentage = new St.Label();
|
||||
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
||||
this._batteryItem = new PopupMenu.PopupMenuItem('');
|
||||
this.menu.addMenuItem(this._batteryItem);
|
||||
|
||||
this._deviceSep = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(this._deviceSep);
|
||||
this._otherDevicePosition = 2;
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this.menu.addAction(_("What's using power..."),function() {
|
||||
GLib.spawn_command_line_async('gnome-power-statistics --device wakeups');
|
||||
});
|
||||
this.menu.addAction(_("Power Settings"),function() {
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-power-panel.desktop');
|
||||
app.activate(-1);
|
||||
GLib.spawn_command_line_async('gnome-control-center power');
|
||||
});
|
||||
|
||||
this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
|
||||
@ -101,32 +95,10 @@ Indicator.prototype = {
|
||||
this._deviceSep.actor.hide();
|
||||
return;
|
||||
}
|
||||
let [device_id, device_type, icon, percentage, state, seconds] = device;
|
||||
let [device_id, device_type, summary, percentage, state, time] = device;
|
||||
if (device_type == UPDeviceType.BATTERY) {
|
||||
this._hasPrimary = true;
|
||||
let time = Math.round(seconds / 60);
|
||||
if (time == 0) {
|
||||
// 0 is reported when UPower does not have enough data
|
||||
// to estimate battery life
|
||||
this._batteryItem.label.text = _("Estimating...");
|
||||
} else {
|
||||
let minutes = time % 60;
|
||||
let hours = Math.floor(time / 60);
|
||||
let timestring;
|
||||
if (time > 60) {
|
||||
if (minutes == 0) {
|
||||
timestring = Gettext.ngettext("%d hour remaining", "%d hours remaining", hours).format(hours);
|
||||
} else {
|
||||
/* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */
|
||||
let template = _("%d %s %d %s remaining");
|
||||
|
||||
timestring = template.format (hours, Gettext.ngettext("hour", "hours", hours), minutes, Gettext.ngettext("minute", "minutes", minutes));
|
||||
}
|
||||
} else
|
||||
timestring = Gettext.ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
|
||||
this._batteryItem.label.text = timestring;
|
||||
}
|
||||
this._primaryPercentage.text = Math.round(percentage) + '%';
|
||||
this._batteryItem.label.text = summary;
|
||||
this._batteryItem.actor.show();
|
||||
if (this._deviceItems.length > 0)
|
||||
this._deviceSep.actor.show();
|
||||
@ -158,6 +130,10 @@ Indicator.prototype = {
|
||||
continue;
|
||||
|
||||
let item = new DeviceItem (devices[i]);
|
||||
item.connect('activate', function() {
|
||||
let p = new Shell.Process({ args: ['gnome-power-statistics', '--device', device_id] });
|
||||
p.run();
|
||||
});
|
||||
this._deviceItems.push(item);
|
||||
this.menu.addMenuItem(item, this._otherDevicePosition + position);
|
||||
position++;
|
||||
@ -188,8 +164,8 @@ Indicator.prototype = {
|
||||
|
||||
_checkError: function(error) {
|
||||
if (!this._restarted && error && error.message.match(/org\.freedesktop\.DBus\.Error\.(UnknownMethod|InvalidArgs)/)) {
|
||||
Util.killall('gnome-power-manager');
|
||||
Util.spawn(['gnome-power-manager']);
|
||||
GLib.spawn_command_line_sync('pkill -f "^gnome-power-manager$"');
|
||||
GLib.spawn_command_line_async('gnome-power-manager');
|
||||
this._restarted = true;
|
||||
}
|
||||
}
|
||||
@ -203,14 +179,26 @@ DeviceItem.prototype = {
|
||||
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
|
||||
|
||||
_init: function(device) {
|
||||
PopupMenu.PopupBaseMenuItem.prototype._init.call(this, { reactive: false });
|
||||
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
|
||||
|
||||
let [device_id, device_type, icon, percentage, state, time] = device;
|
||||
let [device_id, device_type, summary, percentage, state, time] = device;
|
||||
|
||||
this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' });
|
||||
this._label = new St.Label({ text: this._deviceTypeToString(device_type) });
|
||||
this._label = new St.Label({ text: summary });
|
||||
|
||||
this._icon = new St.Icon({ gicon: Shell.util_icon_from_string(icon),
|
||||
let icon;
|
||||
switch (state) {
|
||||
case UPDeviceState.FULLY_CHARGED:
|
||||
icon = 'battery-full-charged';
|
||||
break;
|
||||
case UPDeviceState.UNKNOWN:
|
||||
icon = 'battery-missing';
|
||||
break;
|
||||
default:
|
||||
icon = this._percentageToIcon(percentage) + (state == UPDeviceState.CHARGING ? '-charging' : '');
|
||||
}
|
||||
|
||||
this._icon = new St.Icon({ icon_name: icon,
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
style_class: 'popup-menu-icon' });
|
||||
|
||||
@ -218,36 +206,21 @@ DeviceItem.prototype = {
|
||||
this._box.add_actor(this._label);
|
||||
this.addActor(this._box);
|
||||
|
||||
let percentBin = new St.Bin({ x_align: St.Align.END });
|
||||
let percentLabel = new St.Label({ text: '%d%%'.format(Math.round(percentage)) });
|
||||
this.addActor(percentLabel, { align: St.Align.END });
|
||||
percentBin.child = percentLabel;
|
||||
this.addActor(percentBin);
|
||||
},
|
||||
|
||||
_deviceTypeToString: function(type) {
|
||||
switch (type) {
|
||||
case UPDeviceType.AC_POWER:
|
||||
return _("AC adapter");
|
||||
case UPDeviceType.BATTERY:
|
||||
return _("Laptop battery");
|
||||
case UPDeviceType.UPS:
|
||||
return _("UPS");
|
||||
case UPDeviceType.MONITOR:
|
||||
return _("Monitor");
|
||||
case UPDeviceType.MOUSE:
|
||||
return _("Mouse");
|
||||
case UPDeviceType.KEYBOARD:
|
||||
return _("Keyboard");
|
||||
case UPDeviceType.PDA:
|
||||
return _("PDA");
|
||||
case UPDeviceType.PHONE:
|
||||
return _("Cell phone");
|
||||
case UPDeviceType.MEDIA_PLAYER:
|
||||
return _("Media player");
|
||||
case UPDeviceType.TABLET:
|
||||
return _("Tablet");
|
||||
case UPDeviceType.COMPUTER:
|
||||
return _("Computer");
|
||||
default:
|
||||
return _("Unknown");
|
||||
}
|
||||
_percentageToIcon: function(p) {
|
||||
if (p > 60)
|
||||
return 'battery-full';
|
||||
if (p > 30)
|
||||
return 'battery-good';
|
||||
if (p > 10)
|
||||
return 'battery-low';
|
||||
if (p > 0)
|
||||
return 'battery-caution';
|
||||
return 'battery-empty';
|
||||
}
|
||||
}
|
||||
|
@ -11,15 +11,13 @@ const St = imports.gi.St;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const VOLUME_MAX = 65536.0; /* PA_VOLUME_NORM */
|
||||
const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
|
||||
|
||||
const VOLUME_NOTIFY_ID = 1;
|
||||
|
||||
function Indicator() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
@ -36,17 +34,16 @@ Indicator.prototype = {
|
||||
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
|
||||
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
||||
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
|
||||
this._volumeMax = this._control.get_vol_max_norm();
|
||||
this._volumeMaxAmplified = this._control.get_vol_max_amplified();
|
||||
|
||||
this._output = null;
|
||||
this._outputVolumeId = 0;
|
||||
this._outputMutedId = 0;
|
||||
this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
|
||||
this._outputSwitch = new PopupMenu.PopupSwitchMenuItem(_("Volume: Muted"), false);
|
||||
this._outputSwitch.connect('toggled', Lang.bind(this, this._switchToggled, '_output'));
|
||||
this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
|
||||
this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
this.menu.addMenuItem(this._outputTitle);
|
||||
this.menu.addMenuItem(this._outputSwitch);
|
||||
this.menu.addMenuItem(this._outputSlider);
|
||||
|
||||
this._separator = new PopupMenu.PopupSeparatorMenuItem();
|
||||
@ -55,52 +52,36 @@ Indicator.prototype = {
|
||||
this._input = null;
|
||||
this._inputVolumeId = 0;
|
||||
this._inputMutedId = 0;
|
||||
this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false });
|
||||
this._inputSwitch = new PopupMenu.PopupSwitchMenuItem(_("Microphone: Muted"), false);
|
||||
this._inputSwitch.connect('toggled', Lang.bind(this, this._switchToggled, '_input'));
|
||||
this._inputSlider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input'));
|
||||
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
this.menu.addMenuItem(this._inputTitle);
|
||||
this.menu.addMenuItem(this._inputSwitch);
|
||||
this.menu.addMenuItem(this._inputSlider);
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Sound Settings"), function() {
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-sound-panel.desktop');
|
||||
app.activate(-1);
|
||||
let p = new Shell.Process({ args: ['gnome-control-center', 'sound'] });
|
||||
p.run();
|
||||
});
|
||||
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
this._control.open();
|
||||
},
|
||||
|
||||
_getMaxVolume: function(property) {
|
||||
if (this[property].get_can_decibel())
|
||||
return this._volumeMaxAmplified;
|
||||
else
|
||||
return this._volumeMax;
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
let currentVolume = this._output.volume;
|
||||
let maxVolume = this._getMaxVolume('_output');
|
||||
|
||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
let prev_muted = this._output.is_muted;
|
||||
this._output.volume = Math.max(0, currentVolume - maxVolume * VOLUME_ADJUSTMENT_STEP);
|
||||
if (this._output.volume < 1) {
|
||||
this._output.volume = 0;
|
||||
if (!prev_muted)
|
||||
this._output.change_is_muted(true);
|
||||
}
|
||||
this._output.volume = Math.max(0, currentVolume - VOLUME_MAX * VOLUME_ADJUSTMENT_STEP);
|
||||
this._output.push_volume();
|
||||
}
|
||||
else if (direction == Clutter.ScrollDirection.UP) {
|
||||
this._output.volume = Math.min(maxVolume, currentVolume + maxVolume * VOLUME_ADJUSTMENT_STEP);
|
||||
this._output.change_is_muted(false);
|
||||
this._output.volume = Math.min(VOLUME_MAX, currentVolume + VOLUME_MAX * VOLUME_ADJUSTMENT_STEP);
|
||||
this._output.push_volume();
|
||||
}
|
||||
|
||||
this._notifyVolumeChange();
|
||||
},
|
||||
|
||||
_onControlReady: function() {
|
||||
@ -122,7 +103,8 @@ Indicator.prototype = {
|
||||
this._mutedChanged (null, null, '_output');
|
||||
this._volumeChanged (null, null, '_output');
|
||||
} else {
|
||||
this._outputSlider.setValue(0);
|
||||
this._outputSwitch.label.text = _("Volume: Muted");
|
||||
this._outputSwitch.setToggleState(false);
|
||||
this.setIcon('audio-volume-muted-symbolic');
|
||||
}
|
||||
},
|
||||
@ -142,7 +124,7 @@ Indicator.prototype = {
|
||||
this._volumeChanged (null, null, '_input');
|
||||
} else {
|
||||
this._separator.actor.hide();
|
||||
this._inputTitle.actor.hide();
|
||||
this._inputSwitch.actor.hide();
|
||||
this._inputSlider.actor.hide();
|
||||
}
|
||||
},
|
||||
@ -165,24 +147,23 @@ Indicator.prototype = {
|
||||
}
|
||||
if (showInput) {
|
||||
this._separator.actor.show();
|
||||
this._inputTitle.actor.show();
|
||||
this._inputSwitch.actor.show();
|
||||
this._inputSlider.actor.show();
|
||||
} else {
|
||||
this._separator.actor.hide();
|
||||
this._inputTitle.actor.hide();
|
||||
this._inputSwitch.actor.hide();
|
||||
this._inputSlider.actor.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_volumeToIcon: function(volume) {
|
||||
let maxVolume = this._getMaxVolume('_output');
|
||||
if (volume <= 0) {
|
||||
return 'audio-volume-muted';
|
||||
} else {
|
||||
let n = Math.floor(3 * volume / maxVolume) + 1;
|
||||
if (n < 2)
|
||||
let v = volume / VOLUME_MAX;
|
||||
if (v < 0.33)
|
||||
return 'audio-volume-low';
|
||||
if (n >= 3)
|
||||
if (v > 0.8)
|
||||
return 'audio-volume-high';
|
||||
return 'audio-volume-medium';
|
||||
}
|
||||
@ -193,30 +174,28 @@ Indicator.prototype = {
|
||||
log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
|
||||
return;
|
||||
}
|
||||
let volume = value * this._getMaxVolume(property);
|
||||
let prev_muted = this[property].is_muted;
|
||||
if (volume < 1) {
|
||||
this[property].volume = 0;
|
||||
if (!prev_muted)
|
||||
this[property].change_is_muted(true);
|
||||
} else {
|
||||
this[property].volume = volume;
|
||||
if (prev_muted)
|
||||
this[property].change_is_muted(false);
|
||||
}
|
||||
this[property].volume = value * VOLUME_MAX;
|
||||
this[property].push_volume();
|
||||
},
|
||||
|
||||
_notifyVolumeChange: function() {
|
||||
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
|
||||
global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
|
||||
global.play_theme_sound('audio-volume-change');
|
||||
},
|
||||
|
||||
_switchToggled: function(switchItem, state, property) {
|
||||
if (this[property] == null) {
|
||||
log ('Volume mute switch toggled for %s, but %s does not exist'.format(property, property));
|
||||
return;
|
||||
}
|
||||
this[property].change_is_muted(!state);
|
||||
this._notifyVolumeChange();
|
||||
},
|
||||
|
||||
_mutedChanged: function(object, param_spec, property) {
|
||||
let muted = this[property].is_muted;
|
||||
let slider = this[property+'Slider'];
|
||||
let maxVolume = this._getMaxVolume(property);
|
||||
slider.setValue(muted ? 0 : (this[property].volume / maxVolume));
|
||||
let toggleSwitch = this[property+'Switch'];
|
||||
toggleSwitch.setToggleState(!muted);
|
||||
this._updateLabel(property);
|
||||
if (property == '_output') {
|
||||
if (muted)
|
||||
this.setIcon('audio-volume-muted');
|
||||
@ -226,9 +205,18 @@ Indicator.prototype = {
|
||||
},
|
||||
|
||||
_volumeChanged: function(object, param_spec, property) {
|
||||
let maxVolume = this._getMaxVolume(property);
|
||||
this[property+'Slider'].setValue(this[property].volume / maxVolume);
|
||||
this[property+'Slider'].setValue(this[property].volume / VOLUME_MAX);
|
||||
this._updateLabel(property);
|
||||
if (property == '_output' && !this._output.is_muted)
|
||||
this.setIcon(this._volumeToIcon(this._output.volume));
|
||||
},
|
||||
|
||||
_updateLabel: function(property) {
|
||||
let label;
|
||||
if (this[property].is_muted)
|
||||
label = (property == '_output' ? _("Volume: Muted") : _("Microphone: Muted"));
|
||||
else
|
||||
label = (property == '_output' ? _("Volume: %3.0f%%") : _("Microphone: %3.0f%%")).format(this[property].volume / VOLUME_MAX * 100);
|
||||
this[property+'Switch'].label.text = label;
|
||||
}
|
||||
};
|
||||
|
@ -6,7 +6,6 @@ const Signals = imports.signals;
|
||||
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||
'bluetooth-applet': 'bluetooth',
|
||||
@ -16,11 +15,7 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||
'nm-applet': 'network',
|
||||
'gnome-power-manager': 'battery',
|
||||
'keyboard': 'keyboard',
|
||||
'a11y-keyboard': 'a11y',
|
||||
'kbd-scrolllock': 'keyboard',
|
||||
'kbd-numlock': 'keyboard',
|
||||
'kbd-capslock': 'keyboard',
|
||||
'ibus-ui-gtk': 'input-method'
|
||||
'gnome-settings-daemon': 'display'
|
||||
};
|
||||
|
||||
function StatusIconDispatcher() {
|
||||
@ -32,16 +27,14 @@ StatusIconDispatcher.prototype = {
|
||||
this._traymanager = new Shell.TrayManager();
|
||||
this._traymanager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
this._traymanager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
this._traymanager.manage_stage(global.stage);
|
||||
|
||||
// Yet-another-Ubuntu-workaround - we have to kill their
|
||||
// app-indicators, so that applications fall back to normal
|
||||
// status icons
|
||||
// http://bugzilla.gnome.org/show_bug.cgi=id=621382
|
||||
Util.killall('indicator-application-service');
|
||||
},
|
||||
|
||||
start: function(themeWidget) {
|
||||
this._traymanager.manage_stage(global.stage, themeWidget);
|
||||
let p = new Shell.Process({ args: ['pkill', '-f', '^([^ ]*/)?indicator-application-service$']});
|
||||
p.run();
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon) {
|
||||
|
@ -1,14 +1,10 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const DBus = imports.dbus;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
@ -16,22 +12,6 @@ const GnomeSession = imports.misc.gnomeSession;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const BUS_NAME = 'org.gnome.ScreenSaver';
|
||||
const OBJECT_PATH = '/org/gnome/ScreenSaver';
|
||||
|
||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
|
||||
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
|
||||
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
|
||||
|
||||
const ScreenSaverInterface = {
|
||||
name: BUS_NAME,
|
||||
methods: [ { name: 'Lock', inSignature: '' } ]
|
||||
};
|
||||
|
||||
let ScreenSaverProxy = DBus.makeProxyClass(ScreenSaverInterface);
|
||||
|
||||
// Adapted from gdm/gui/user-switch-applet/applet.c
|
||||
//
|
||||
@ -46,24 +26,17 @@ StatusMenuButton.prototype = {
|
||||
__proto__: PanelMenu.Button.prototype,
|
||||
|
||||
_init: function() {
|
||||
PanelMenu.Button.prototype._init.call(this, 0.0);
|
||||
PanelMenu.Button.prototype._init.call(this, St.Align.START);
|
||||
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
|
||||
this.actor.set_child(box);
|
||||
|
||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||
|
||||
this._gdm = Gdm.UserManager.ref_default();
|
||||
this._gdm.queue_load();
|
||||
this._gdm.queue_load()
|
||||
|
||||
this._user = this._gdm.get_user(GLib.get_user_name());
|
||||
this._presence = new GnomeSession.Presence();
|
||||
this._presenceItems = {};
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
|
||||
this._account_mgr = Tp.AccountManager.dup()
|
||||
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
this._screenSaverProxy = new ScreenSaverProxy(DBus.session, BUS_NAME, OBJECT_PATH);
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._iconBox = new St.Bin();
|
||||
@ -87,17 +60,6 @@ StatusMenuButton.prototype = {
|
||||
this._gdm.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUser));
|
||||
this._gdm.connect('user-added', Lang.bind(this, this._updateSwitchUser));
|
||||
this._gdm.connect('user-removed', Lang.bind(this, this._updateSwitchUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
|
||||
Lang.bind(this, this._updateSwitchUser));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateLogout));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
|
||||
Lang.bind(this, this._updateLockScreen));
|
||||
this._updateSwitchUser();
|
||||
this._updateLogout();
|
||||
this._updateLockScreen();
|
||||
|
||||
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
@ -112,56 +74,11 @@ StatusMenuButton.prototype = {
|
||||
this._name.set_text("");
|
||||
},
|
||||
|
||||
_updateSessionSeparator: function() {
|
||||
let showSeparator = this._loginScreenItem.actor.visible ||
|
||||
this._logoutItem.actor.visible ||
|
||||
this._lockScreenItem.actor.visible;
|
||||
if (showSeparator)
|
||||
this._sessionSeparator.actor.show();
|
||||
else
|
||||
this._sessionSeparator.actor.hide();
|
||||
},
|
||||
|
||||
_updateSwitchUser: function() {
|
||||
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
|
||||
if (allowSwitch && this._gdm.can_switch ())
|
||||
if (this._gdm.can_switch ())
|
||||
this._loginScreenItem.actor.show();
|
||||
else
|
||||
this._loginScreenItem.actor.hide();
|
||||
this._updateSessionSeparator();
|
||||
},
|
||||
|
||||
_updateLogout: function() {
|
||||
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
|
||||
if (allowLogout)
|
||||
this._logoutItem.actor.show();
|
||||
else
|
||||
this._logoutItem.actor.hide();
|
||||
this._updateSessionSeparator();
|
||||
},
|
||||
|
||||
_updateLockScreen: function() {
|
||||
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
||||
if (allowLockScreen)
|
||||
this._lockScreenItem.actor.show();
|
||||
else
|
||||
this._lockScreenItem.actor.hide();
|
||||
this._updateSessionSeparator();
|
||||
},
|
||||
|
||||
_updateSuspendOrPowerOff: function() {
|
||||
this._haveSuspend = this._upClient.get_can_suspend();
|
||||
|
||||
if (!this._suspendOrPowerOffItem)
|
||||
return;
|
||||
|
||||
// If we can't suspend show Power Off... instead
|
||||
// and disable the alt key
|
||||
if (!this._haveSuspend) {
|
||||
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
|
||||
} else {
|
||||
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
|
||||
}
|
||||
},
|
||||
|
||||
_updatePresenceIcon: function(presence, status) {
|
||||
@ -181,12 +98,12 @@ StatusMenuButton.prototype = {
|
||||
_createSubMenu: function() {
|
||||
let item;
|
||||
|
||||
item = new PopupMenu.PopupImageMenuItem(_("Available"), 'user-available');
|
||||
item = new PopupMenu.PopupImageMenuItem(_("Available"), 'user-available', true);
|
||||
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.AVAILABLE));
|
||||
this.menu.addMenuItem(item);
|
||||
this._presenceItems[GnomeSession.PresenceStatus.AVAILABLE] = item;
|
||||
|
||||
item = new PopupMenu.PopupImageMenuItem(_("Busy"), 'user-busy');
|
||||
item = new PopupMenu.PopupImageMenuItem(_("Busy"), 'user-busy', true);
|
||||
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.BUSY));
|
||||
this.menu.addMenuItem(item);
|
||||
this._presenceItems[GnomeSession.PresenceStatus.BUSY] = item;
|
||||
@ -194,11 +111,11 @@ StatusMenuButton.prototype = {
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("My Account"));
|
||||
item = new PopupMenu.PopupMenuItem(_("My Account..."));
|
||||
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("System Settings"));
|
||||
item = new PopupMenu.PopupMenuItem(_("System Settings..."));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
@ -208,7 +125,6 @@ StatusMenuButton.prototype = {
|
||||
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
|
||||
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._lockScreenItem = item;
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Switch User"));
|
||||
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
|
||||
@ -218,41 +134,36 @@ StatusMenuButton.prototype = {
|
||||
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
|
||||
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._logoutItem = item;
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
this._sessionSeparator = item;
|
||||
|
||||
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
|
||||
_("Power Off..."));
|
||||
item = new PopupMenu.PopupMenuItem(_("Suspend"));
|
||||
item.connect('activate', Lang.bind(this, this._onShutDownActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Shut Down..."));
|
||||
item.connect('activate', Lang.bind(this, this._onShutDownActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
this._suspendOrPowerOffItem = item;
|
||||
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
|
||||
this._updateSuspendOrPowerOff();
|
||||
},
|
||||
|
||||
_setPresenceStatus: function(item, event, status) {
|
||||
this._presence.setStatus(status);
|
||||
|
||||
this._setIMStatus(status);
|
||||
},
|
||||
|
||||
_onMyAccountActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-user-accounts-panel.desktop');
|
||||
app.activate(-1);
|
||||
this._spawn(['gnome-about-me']);
|
||||
},
|
||||
|
||||
_onPreferencesActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().get_app('gnome-control-center.desktop');
|
||||
app.activate(-1);
|
||||
this._spawn(['gnome-control-center']);
|
||||
},
|
||||
|
||||
_onLockScreenActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._screenSaverProxy.LockRemote();
|
||||
this._spawn(['gnome-screensaver-command', '--lock']);
|
||||
},
|
||||
|
||||
_onLoginScreenActivate: function() {
|
||||
@ -263,45 +174,19 @@ StatusMenuButton.prototype = {
|
||||
|
||||
_onQuitSessionActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._session.LogoutRemote(0);
|
||||
this._spawn(['gnome-session-save', '--logout-dialog']);
|
||||
},
|
||||
|
||||
_onSuspendOrPowerOffActivate: function() {
|
||||
_onShutDownActivate: function() {
|
||||
Main.overview.hide();
|
||||
|
||||
if (this._haveSuspend &&
|
||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
||||
this._screenSaverProxy.LockRemote(Lang.bind(this, function() {
|
||||
this._upClient.suspend_sync(null);
|
||||
}));
|
||||
} else {
|
||||
this._session.ShutdownRemote();
|
||||
}
|
||||
this._spawn(['gnome-session-save', '--shutdown-dialog']);
|
||||
},
|
||||
|
||||
_setIMStatus: function(session_status) {
|
||||
let [presence_type, presence_status, msg] = this._account_mgr.get_most_available_presence();
|
||||
let type, status;
|
||||
|
||||
// We change the IM presence only if there are connected accounts
|
||||
if (presence_type == Tp.ConnectionPresenceType.UNSET ||
|
||||
presence_type == Tp.ConnectionPresenceType.OFFLINE ||
|
||||
presence_type == Tp.ConnectionPresenceType.UNKNOWN ||
|
||||
presence_type == Tp.ConnectionPresenceType.ERROR)
|
||||
return;
|
||||
|
||||
if (session_status == GnomeSession.PresenceStatus.AVAILABLE) {
|
||||
type = Tp.ConnectionPresenceType.AVAILABLE;
|
||||
status = "available";
|
||||
}
|
||||
else if (session_status == GnomeSession.PresenceStatus.BUSY) {
|
||||
type = Tp.ConnectionPresenceType.BUSY;
|
||||
status = "busy";
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
this._account_mgr.set_all_requested_presences(type, status, msg);
|
||||
_spawn: function(args) {
|
||||
// FIXME: once Shell.Process gets support for signalling
|
||||
// errors we should pop up an error dialog or something here
|
||||
// on failure
|
||||
let p = new Shell.Process({'args' : args});
|
||||
p.run();
|
||||
}
|
||||
};
|
||||
|
@ -62,16 +62,9 @@ function addTween(target, tweeningParameters) {
|
||||
function _wrapTweening(target, tweeningParameters) {
|
||||
let state = _getTweenState(target);
|
||||
|
||||
if (!state.destroyedId) {
|
||||
if (target instanceof Clutter.Actor) {
|
||||
state.actor = target;
|
||||
state.destroyedId = target.connect('destroy', _actorDestroyed);
|
||||
} else if (target.actor && target.actor instanceof Clutter.Actor) {
|
||||
state.actor = target.actor;
|
||||
state.destroyedId = target.actor.connect('destroy', function() { _actorDestroyed(target); });
|
||||
}
|
||||
}
|
||||
|
||||
if (target instanceof Clutter.Actor && !state.destroyedId)
|
||||
state.destroyedId = target.connect('destroy', _actorDestroyed);
|
||||
|
||||
_addHandler(target, tweeningParameters, 'onStart', _tweenStarted);
|
||||
_addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
|
||||
}
|
||||
@ -89,7 +82,7 @@ function _resetTweenState(target) {
|
||||
|
||||
if (state) {
|
||||
if (state.destroyedId)
|
||||
state.actor.disconnect(state.destroyedId);
|
||||
target.disconnect(state.destroyedId);
|
||||
if (state.idleCompletedId)
|
||||
Mainloop.source_remove(state.idleCompletedId);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
@ -16,12 +15,163 @@ const Search = imports.ui.search;
|
||||
const SearchDisplay = imports.ui.searchDisplay;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
function BaseTab(titleActor, pageActor, name, a11yIcon) {
|
||||
this._init(titleActor, pageActor, name, a11yIcon);
|
||||
|
||||
function SearchEntry() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
SearchEntry.prototype = {
|
||||
_init : function() {
|
||||
this.actor = new St.Entry({ name: 'searchEntry',
|
||||
hint_text: _("Search your computer") });
|
||||
this.entry = this.actor.clutter_text;
|
||||
|
||||
this.actor.clutter_text.connect('text-changed', Lang.bind(this,
|
||||
function() {
|
||||
if (this.isActive())
|
||||
this.actor.set_secondary_icon_from_file(global.imagedir +
|
||||
'close-black.svg');
|
||||
else
|
||||
this.actor.set_secondary_icon_from_file(null);
|
||||
}));
|
||||
this.actor.connect('secondary-icon-clicked', Lang.bind(this,
|
||||
function() {
|
||||
this.reset();
|
||||
}));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
global.stage.connect('notify::key-focus', Lang.bind(this, this._updateCursorVisibility));
|
||||
|
||||
this.pane = null;
|
||||
|
||||
this._capturedEventId = 0;
|
||||
},
|
||||
|
||||
_updateCursorVisibility: function() {
|
||||
let focus = global.stage.get_key_focus();
|
||||
if (focus == global.stage || focus == this.entry)
|
||||
this.entry.set_cursor_visible(true);
|
||||
else
|
||||
this.entry.set_cursor_visible(false);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (this._capturedEventId == 0)
|
||||
this._capturedEventId = global.stage.connect('captured-event',
|
||||
Lang.bind(this, this._onCapturedEvent));
|
||||
this.entry.set_cursor_visible(true);
|
||||
this.entry.set_selection(0, 0);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
if (this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
let actor = global.stage.get_actor_at_pos (Clutter.PickMode.REACTIVE,
|
||||
x, y);
|
||||
// this.actor is never hovered directly, only its clutter_text and icon
|
||||
let hovered = this.actor == actor.get_parent();
|
||||
|
||||
this.actor.set_hover(hovered);
|
||||
|
||||
this.entry.text = '';
|
||||
|
||||
// Return focus to the stage
|
||||
global.stage.set_key_focus(null);
|
||||
|
||||
this.entry.set_cursor_visible(true);
|
||||
this.entry.set_selection(0, 0);
|
||||
},
|
||||
|
||||
getText: function () {
|
||||
return this.entry.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
},
|
||||
|
||||
// some search term has been entered
|
||||
isActive: function() {
|
||||
return this.actor.get_text() != '';
|
||||
},
|
||||
|
||||
// the entry does not show the hint
|
||||
_isActivated: function() {
|
||||
return this.entry.text == this.actor.get_text();
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let source = event.get_source();
|
||||
let panelEvent = source && Main.panel.actor.contains(source);
|
||||
|
||||
switch (event.type()) {
|
||||
case Clutter.EventType.BUTTON_PRESS:
|
||||
// the user clicked outside after activating the entry, but
|
||||
// with no search term entered - cancel the search
|
||||
if (source != this.entry && this.entry.text == '') {
|
||||
this.reset();
|
||||
// allow only panel events to continue
|
||||
return !panelEvent;
|
||||
}
|
||||
return false;
|
||||
case Clutter.EventType.KEY_PRESS:
|
||||
// If neither the stage nor our entry have key focus, some
|
||||
// "special" actor grabbed the focus (run dialog, looking
|
||||
// glass); we don't want to interfere with that
|
||||
let focus = global.stage.get_key_focus();
|
||||
if (focus != global.stage && focus != this.entry)
|
||||
return false;
|
||||
|
||||
let sym = event.get_key_symbol();
|
||||
|
||||
// If we have an active search, Escape cancels it - if we
|
||||
// haven't, the key is ignored
|
||||
if (sym == Clutter.Escape)
|
||||
if (this._isActivated()) {
|
||||
this.reset();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore non-printable keys
|
||||
if (!Clutter.keysym_to_unicode(sym))
|
||||
return false;
|
||||
|
||||
// Search started - move the key focus to the entry and
|
||||
// "repeat" the event
|
||||
if (!this._isActivated()) {
|
||||
global.stage.set_key_focus(this.entry);
|
||||
this.entry.event(event, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
default:
|
||||
// Suppress all other events outside the panel while the entry
|
||||
// is activated and no search has been entered - any click
|
||||
// outside the entry will cancel the search
|
||||
return (this.entry.text == '' && !panelEvent);
|
||||
}
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
if (this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(SearchEntry.prototype);
|
||||
|
||||
|
||||
function BaseTab(titleActor, pageActor) {
|
||||
this._init(titleActor, pageActor);
|
||||
}
|
||||
|
||||
BaseTab.prototype = {
|
||||
_init: function(titleActor, pageActor, name, a11yIcon) {
|
||||
_init: function(titleActor, pageActor) {
|
||||
this.title = titleActor;
|
||||
this.page = new St.Bin({ child: pageActor,
|
||||
x_align: St.Align.START,
|
||||
@ -30,25 +180,14 @@ BaseTab.prototype = {
|
||||
y_fill: true,
|
||||
style_class: 'view-tab-page' });
|
||||
|
||||
if (this.title.can_focus) {
|
||||
Main.ctrlAltTabManager.addGroup(this.title, name, a11yIcon);
|
||||
} else {
|
||||
Main.ctrlAltTabManager.addGroup(this.page, name, a11yIcon,
|
||||
{ proxy: this.title,
|
||||
focusCallback: Lang.bind(this, this._a11yFocus) });
|
||||
}
|
||||
|
||||
this.visible = false;
|
||||
},
|
||||
|
||||
show: function(animate) {
|
||||
show: function() {
|
||||
this.visible = true;
|
||||
this.page.opacity = 0;
|
||||
this.page.show();
|
||||
|
||||
if (!animate)
|
||||
return;
|
||||
|
||||
this.page.opacity = 0;
|
||||
Tweener.addTween(this.page,
|
||||
{ opacity: 255,
|
||||
time: 0.1,
|
||||
@ -68,11 +207,6 @@ BaseTab.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
_a11yFocus: function() {
|
||||
this._activate();
|
||||
this.page.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
_activate: function() {
|
||||
this.emit('activated');
|
||||
}
|
||||
@ -80,21 +214,19 @@ BaseTab.prototype = {
|
||||
Signals.addSignalMethods(BaseTab.prototype);
|
||||
|
||||
|
||||
function ViewTab(id, label, pageActor, a11yIcon) {
|
||||
this._init(id, label, pageActor, a11yIcon);
|
||||
function ViewTab(label, pageActor) {
|
||||
this._init(label, pageActor);
|
||||
}
|
||||
|
||||
ViewTab.prototype = {
|
||||
__proto__: BaseTab.prototype,
|
||||
|
||||
_init: function(id, label, pageActor, a11yIcon) {
|
||||
this.id = id;
|
||||
|
||||
_init: function(label, pageActor) {
|
||||
let titleActor = new St.Button({ label: label,
|
||||
style_class: 'view-tab-title' });
|
||||
titleActor.connect('clicked', Lang.bind(this, this._activate));
|
||||
|
||||
BaseTab.prototype._init.call(this, titleActor, pageActor, label, a11yIcon);
|
||||
BaseTab.prototype._init.call(this, titleActor, pageActor);
|
||||
}
|
||||
};
|
||||
|
||||
@ -107,43 +239,21 @@ SearchTab.prototype = {
|
||||
__proto__: BaseTab.prototype,
|
||||
|
||||
_init: function() {
|
||||
this.active = false;
|
||||
this._searchActive = false;
|
||||
this._searchPending = false;
|
||||
this._keyPressId = 0;
|
||||
this._searchTimeoutId = 0;
|
||||
|
||||
this._searchSystem = new Search.SearchSystem();
|
||||
this._openSearchSystem = new Search.OpenSearchSystem();
|
||||
|
||||
this._entry = new St.Entry({ name: 'searchEntry',
|
||||
/* Translators: this is the text displayed
|
||||
in the search entry when no search is
|
||||
active; it should not exceed ~30
|
||||
characters. */
|
||||
hint_text: _("Type to search..."),
|
||||
track_hover: true,
|
||||
can_focus: true });
|
||||
this._text = this._entry.clutter_text;
|
||||
this._text.connect('key-press-event', Lang.bind(this, this._onKeyPress));
|
||||
|
||||
this._inactiveIcon = new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-find',
|
||||
icon_type: St.IconType.SYMBOLIC });
|
||||
this._activeIcon = new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-clear',
|
||||
icon_type: St.IconType.SYMBOLIC });
|
||||
this._entry.set_secondary_icon(this._inactiveIcon);
|
||||
|
||||
this._iconClickedId = 0;
|
||||
|
||||
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem, this._openSearchSystem);
|
||||
this._searchEntry = new SearchEntry();
|
||||
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
|
||||
BaseTab.prototype._init.call(this,
|
||||
this._entry,
|
||||
this._searchResults.actor,
|
||||
_("Search"),
|
||||
'edit-find');
|
||||
|
||||
this._text.connect('text-changed', Lang.bind(this, this._onTextChanged));
|
||||
this._text.connect('activate', Lang.bind(this, function (se) {
|
||||
this._searchEntry.actor,
|
||||
this._searchResults.actor);
|
||||
this._searchEntry.entry.connect('text-changed',
|
||||
Lang.bind(this, this._onTextChanged));
|
||||
this._searchEntry.entry.connect('activate', Lang.bind(this, function (se) {
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._doSearch();
|
||||
@ -151,51 +261,31 @@ SearchTab.prototype = {
|
||||
this._searchResults.activateSelected();
|
||||
return true;
|
||||
}));
|
||||
},
|
||||
|
||||
this._entry.connect('secondary-icon-clicked', Lang.bind(this,
|
||||
function() {
|
||||
this._reset();
|
||||
}));
|
||||
this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped));
|
||||
setFindAsYouType: function(enabled) {
|
||||
if (enabled)
|
||||
this._searchEntry.show();
|
||||
else
|
||||
this._searchEntry.hide();
|
||||
},
|
||||
|
||||
global.stage.connect('notify::key-focus', Lang.bind(this, this._updateCursorVisibility));
|
||||
show: function() {
|
||||
BaseTab.prototype.show.call(this);
|
||||
|
||||
this._capturedEventId = 0;
|
||||
if (this._keyPressId == 0)
|
||||
this._keyPressId = global.stage.connect('key-press-event',
|
||||
Lang.bind(this, this._onKeyPress));
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
BaseTab.prototype.hide.call(this);
|
||||
|
||||
this._reset();
|
||||
},
|
||||
|
||||
_reset: function () {
|
||||
this._text.text = '';
|
||||
|
||||
global.stage.set_key_focus(null);
|
||||
|
||||
this._text.set_cursor_visible(true);
|
||||
this._text.set_selection(0, 0);
|
||||
},
|
||||
|
||||
_updateCursorVisibility: function() {
|
||||
let focus = global.stage.get_key_focus();
|
||||
this._text.set_cursor_visible(focus == this._text);
|
||||
},
|
||||
|
||||
_onMapped: function() {
|
||||
if (this._entry.mapped) {
|
||||
// Enable 'find-as-you-type'
|
||||
this._capturedEventId = global.stage.connect('captured-event',
|
||||
Lang.bind(this, this._onCapturedEvent));
|
||||
this._text.set_cursor_visible(true);
|
||||
this._text.set_selection(0, 0);
|
||||
} else {
|
||||
// Disable 'find-as-you-type'
|
||||
if (this._capturedEventId > 0)
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
if (this._keyPressId > 0) {
|
||||
global.stage.disconnect(this._keyPressId);
|
||||
this._keyPressId = 0;
|
||||
}
|
||||
this._searchEntry.reset();
|
||||
},
|
||||
|
||||
addSearchProvider: function(provider) {
|
||||
@ -203,42 +293,19 @@ SearchTab.prototype = {
|
||||
this._searchResults.createProviderMeta(provider);
|
||||
},
|
||||
|
||||
startSearch: function(event) {
|
||||
global.stage.set_key_focus(this._text);
|
||||
this._text.event(event, false);
|
||||
},
|
||||
|
||||
// the entry does not show the hint
|
||||
_isActivated: function() {
|
||||
return this._text.text == this._entry.get_text();
|
||||
},
|
||||
|
||||
_onTextChanged: function (se, prop) {
|
||||
let searchPreviouslyActive = this.active;
|
||||
this.active = this._entry.get_text() != '';
|
||||
this._searchPending = this.active && !searchPreviouslyActive;
|
||||
let searchPreviouslyActive = this._searchActive;
|
||||
this._searchActive = this._searchEntry.isActive();
|
||||
this._searchPending = this._searchActive && !searchPreviouslyActive;
|
||||
if (this._searchPending) {
|
||||
this._searchResults.startingSearch();
|
||||
}
|
||||
if (this.active) {
|
||||
this._entry.set_secondary_icon(this._activeIcon);
|
||||
|
||||
if (this._iconClickedId == 0) {
|
||||
this._iconClickedId = this._entry.connect('secondary-icon-clicked',
|
||||
Lang.bind(this, function() {
|
||||
this.reset();
|
||||
}));
|
||||
}
|
||||
if (this._searchActive) {
|
||||
this._activate();
|
||||
} else {
|
||||
if (this._iconClickedId > 0)
|
||||
this._entry.disconnect(this._iconClickedId);
|
||||
this._iconClickedId = 0;
|
||||
|
||||
this._entry.set_secondary_icon(this._inactiveIcon);
|
||||
this.emit('search-cancelled');
|
||||
}
|
||||
if (!this.active) {
|
||||
if (!this._searchActive) {
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._searchTimeoutId = 0;
|
||||
@ -250,46 +317,34 @@ SearchTab.prototype = {
|
||||
this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
|
||||
},
|
||||
|
||||
_onKeyPress: function(entry, event) {
|
||||
_onKeyPress: function(stage, event) {
|
||||
// If neither the stage nor the search entry have key focus, some
|
||||
// "special" actor grabbed the focus (run dialog, looking glass);
|
||||
// we don't want to interfere with that
|
||||
let focus = stage.get_key_focus();
|
||||
if (focus != stage && focus != this._searchEntry.entry)
|
||||
return false;
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.Up) {
|
||||
if (!this.active)
|
||||
if (!this._searchActive)
|
||||
return true;
|
||||
this._searchResults.selectUp(false);
|
||||
|
||||
return true;
|
||||
} else if (symbol == Clutter.Down) {
|
||||
if (!this.active)
|
||||
if (!this._searchActive)
|
||||
return true;
|
||||
|
||||
this._searchResults.selectDown(false);
|
||||
return true;
|
||||
} else if (symbol == Clutter.Escape) {
|
||||
if (this._isActivated()) {
|
||||
this._reset();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||
let source = event.get_source();
|
||||
if (source != this._text && this._text.text == '') {
|
||||
// the user clicked outside after activating the entry, but
|
||||
// with no search term entered - cancel the search
|
||||
this._reset();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_doSearch: function () {
|
||||
this._searchTimeoutId = 0;
|
||||
let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
let text = this._searchEntry.getText();
|
||||
this._searchResults.updateSearch(text);
|
||||
|
||||
return false;
|
||||
@ -347,24 +402,9 @@ ViewSelector.prototype = {
|
||||
this._switchTab(this._activeTab);
|
||||
}));
|
||||
|
||||
Main.overview.connect('item-drag-begin',
|
||||
Lang.bind(this, this._switchDefaultTab));
|
||||
|
||||
this._stageKeyPressId = 0;
|
||||
Main.overview.connect('showing', Lang.bind(this,
|
||||
function () {
|
||||
this._switchDefaultTab();
|
||||
this._stageKeyPressId = global.stage.connect('key-press-event',
|
||||
Lang.bind(this, this._onStageKeyPress));
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this,
|
||||
function () {
|
||||
this._switchDefaultTab();
|
||||
if (this._stageKeyPressId != 0) {
|
||||
global.stage.disconnect(this._stageKeyPressId);
|
||||
this._stageKeyPressId = 0;
|
||||
}
|
||||
}));
|
||||
this._keyPressId = 0;
|
||||
this._itemDragBeginId = 0;
|
||||
this._overviewHidingId = 0;
|
||||
|
||||
// Public constraints which may be used to tie actors' height or
|
||||
// vertical position to the current tab's content; as the content's
|
||||
@ -388,16 +428,14 @@ ViewSelector.prototype = {
|
||||
}));
|
||||
},
|
||||
|
||||
addViewTab: function(id, title, pageActor, a11yIcon) {
|
||||
let viewTab = new ViewTab(id, title, pageActor, a11yIcon);
|
||||
addViewTab: function(title, pageActor) {
|
||||
let viewTab = new ViewTab(title, pageActor);
|
||||
this._tabs.push(viewTab);
|
||||
this._tabBox.add(viewTab.title);
|
||||
this._addTab(viewTab);
|
||||
},
|
||||
|
||||
_switchTab: function(tab) {
|
||||
let firstSwitch = this._activeTab == null;
|
||||
|
||||
if (this._activeTab && this._activeTab.visible) {
|
||||
if (this._activeTab == tab)
|
||||
return;
|
||||
@ -413,13 +451,11 @@ ViewSelector.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
// Only fade when switching between tabs,
|
||||
// not when setting the initially selected one.
|
||||
if (!tab.visible)
|
||||
tab.show(!firstSwitch);
|
||||
tab.show();
|
||||
|
||||
// Pull a Meg Ryan:
|
||||
if (!firstSwitch && Main.overview.workspaces) {
|
||||
if (Main.overview && Main.overview.workspaces) {
|
||||
if (tab != this._tabs[0]) {
|
||||
Tweener.addTween(Main.overview.workspaces.actor,
|
||||
{ opacity: 0,
|
||||
@ -442,14 +478,6 @@ ViewSelector.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
switchTab: function(id) {
|
||||
for (let i = 0; i < this._tabs.length; i++)
|
||||
if (this._tabs[i].id == id) {
|
||||
this._switchTab(this._tabs[i]);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_switchDefaultTab: function() {
|
||||
if (this._tabs.length > 0)
|
||||
this._switchTab(this._tabs[0]);
|
||||
@ -531,31 +559,70 @@ ViewSelector.prototype = {
|
||||
}));
|
||||
},
|
||||
|
||||
_onStageKeyPress: function(actor, event) {
|
||||
_onKeyPress: function(stage, event) {
|
||||
// Only process events if the stage has key focus - search is handled
|
||||
// by the search tab, and we do not want to interfere with "special"
|
||||
// actors grabbing focus (run dialog, looking glass, notifications).
|
||||
let focus = stage.get_key_focus();
|
||||
if (focus != stage)
|
||||
return false;
|
||||
|
||||
let modifiers = Shell.get_event_state(event);
|
||||
let symbol = event.get_key_symbol();
|
||||
|
||||
if (symbol == Clutter.Escape) {
|
||||
Main.overview.hide();
|
||||
return true;
|
||||
} else if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
|
||||
if (symbol == Clutter.Page_Up) {
|
||||
if (!this._searchTab.active)
|
||||
if (!this._searchActive)
|
||||
this._prevTab();
|
||||
return true;
|
||||
} else if (symbol == Clutter.Page_Down) {
|
||||
if (!this._searchTab.active)
|
||||
if (!this._searchActive)
|
||||
this._nextTab();
|
||||
return true;
|
||||
}
|
||||
} else if (Clutter.keysym_to_unicode(symbol)) {
|
||||
this._searchTab.startSearch(event);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
addSearchProvider: function(provider) {
|
||||
this._searchTab.addSearchProvider(provider);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this._searchTab.setFindAsYouType(true);
|
||||
|
||||
if (this._itemDragBeginId == 0)
|
||||
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
|
||||
Lang.bind(this, this._switchDefaultTab));
|
||||
if (this._overviewHidingId == 0)
|
||||
this._overviewHidingId = Main.overview.connect('hiding',
|
||||
Lang.bind(this, this._switchDefaultTab));
|
||||
if (this._keyPressId == 0)
|
||||
this._keyPressId = global.stage.connect('key-press-event',
|
||||
Lang.bind(this, this._onKeyPress));
|
||||
|
||||
this._switchDefaultTab();
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this._searchTab.setFindAsYouType(false);
|
||||
|
||||
if (this._keyPressId > 0) {
|
||||
global.stage.disconnect(this._keyPressId);
|
||||
this._keyPressId = 0;
|
||||
}
|
||||
|
||||
if (this._itemDragBeginId > 0) {
|
||||
Main.overview.disconnect(this._itemDragBeginId);
|
||||
this._itemDragBeginId = 0;
|
||||
}
|
||||
|
||||
if (this._overviewHidingId > 0) {
|
||||
Main.overview.disconnect(this._overviewHidingId);
|
||||
this._overviewHidingId = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ViewSelector.prototype);
|
||||
|
@ -15,11 +15,12 @@ function WindowAttentionHandler() {
|
||||
WindowAttentionHandler.prototype = {
|
||||
_init : function() {
|
||||
this._startupIds = {};
|
||||
this._tracker = Shell.WindowTracker.get_default();
|
||||
this._tracker.connect('startup-sequence-changed', Lang.bind(this, this._onStartupSequenceChanged));
|
||||
this._sources = {};
|
||||
|
||||
let display = global.screen.get_display();
|
||||
display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
tracker.connect('startup-sequence-changed', Lang.bind(this, this._onStartupSequenceChanged));
|
||||
},
|
||||
|
||||
_onStartupSequenceChanged : function(tracker) {
|
||||
@ -56,16 +57,28 @@ WindowAttentionHandler.prototype = {
|
||||
if (!window || window.has_focus() || window.is_skip_taskbar())
|
||||
return;
|
||||
|
||||
let app = this._tracker.get_window_app(window);
|
||||
let source = new Source(app, window);
|
||||
Main.messageTray.add(source);
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
let app = tracker.get_window_app(window);
|
||||
let appId = app.get_id();
|
||||
|
||||
let source = this._sources[appId];
|
||||
if (source == null) {
|
||||
source = new Source(app, window);
|
||||
this._sources[appId] = source;
|
||||
Main.messageTray.add(source);
|
||||
source.connect('destroy', Lang.bind(this, function() { delete this._sources[appId]; }));
|
||||
}
|
||||
|
||||
let notification = new MessageTray.Notification(source, this._getTitle(app, window), this._getBanner(app, window));
|
||||
source.notify(notification);
|
||||
|
||||
source.signalIDs.push(window.connect('notify::title', Lang.bind(this, function(win) {
|
||||
notification.update(this._getTitle(app, win), this._getBanner(app, win));
|
||||
})));
|
||||
window.connect('notify::title', Lang.bind(this, function(win) {
|
||||
notification.update(this._getTitle(app, win), this._getBanner(app, win));
|
||||
}));
|
||||
window.connect('notify::demands-attention', Lang.bind(this, function() { source.destroy(); }));
|
||||
window.connect('focus', Lang.bind(this, function() { source.destroy(); }));
|
||||
window.connect('unmanaged', Lang.bind(this, function() { source.destroy(); }));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@ -81,27 +94,13 @@ Source.prototype = {
|
||||
this._window = window;
|
||||
this._app = app;
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
|
||||
this.signalIDs = [];
|
||||
this.signalIDs.push(this._window.connect('notify::demands-attention', Lang.bind(this, function() { this.destroy(); })));
|
||||
this.signalIDs.push(this._window.connect('focus', Lang.bind(this, function() { this.destroy(); })));
|
||||
this.signalIDs.push(this._window.connect('unmanaged', Lang.bind(this, function() { this.destroy(); })));
|
||||
|
||||
this.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
for(let i = 0; i < this.signalIDs.length; i++) {
|
||||
this._window.disconnect(this.signalIDs[i]);
|
||||
}
|
||||
this.signalIDs = [];
|
||||
},
|
||||
|
||||
createNotificationIcon : function() {
|
||||
return this._app.create_icon_texture(this.ICON_SIZE);
|
||||
},
|
||||
|
||||
open : function(notification) {
|
||||
_notificationClicked : function(notification) {
|
||||
Main.activateWindow(this._window);
|
||||
this.destroy();
|
||||
}
|
||||
|
@ -93,8 +93,6 @@ WindowManager.prototype = {
|
||||
|
||||
this._dimmedWindows = [];
|
||||
|
||||
this._animationBlockCount = 0;
|
||||
|
||||
this._switchData = null;
|
||||
this._shellwm.connect('kill-switch-workspace', Lang.bind(this, this._switchWorkspaceDone));
|
||||
this._shellwm.connect('kill-window-effects', Lang.bind(this, function (shellwm, actor) {
|
||||
@ -118,16 +116,14 @@ WindowManager.prototype = {
|
||||
this.setKeybindingHandler('switch_to_workspace_up', Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setKeybindingHandler('switch_to_workspace_down', Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setKeybindingHandler('switch_windows', Lang.bind(this, this._startAppSwitcher));
|
||||
this.setKeybindingHandler('switch_group', Lang.bind(this, this._startAppSwitcher));
|
||||
this.setKeybindingHandler('switch_panels', Lang.bind(this, this._startA11ySwitcher));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
for (let i = 0; i < this._dimmedWindows.length; i++)
|
||||
this._undimWindow(this._dimmedWindows[i], true);
|
||||
this._undimParentWindow(this._dimmedWindows[i], true);
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||
for (let i = 0; i < this._dimmedWindows.length; i++)
|
||||
this._dimWindow(this._dimmedWindows[i], true);
|
||||
this._dimParentWindow(this._dimmedWindows[i], true);
|
||||
}));
|
||||
},
|
||||
|
||||
@ -141,16 +137,8 @@ WindowManager.prototype = {
|
||||
this._shellwm.connect('keybinding::' + keybinding, handler);
|
||||
},
|
||||
|
||||
blockAnimations: function() {
|
||||
this._animationBlockCount++;
|
||||
},
|
||||
|
||||
unblockAnimations: function() {
|
||||
this._animationBlockCount = Math.max(0, this._animationBlockCount - 1);
|
||||
},
|
||||
|
||||
_shouldAnimate : function(actor) {
|
||||
if (Main.overview.visible || this._animationsBlocked > 0)
|
||||
if (Main.overview.visible)
|
||||
return false;
|
||||
if (actor && (actor.meta_window.get_window_type() != Meta.WindowType.NORMAL))
|
||||
return false;
|
||||
@ -234,39 +222,41 @@ WindowManager.prototype = {
|
||||
_unmaximizeWindowDone : function(shellwm, actor) {
|
||||
},
|
||||
|
||||
_hasAttachedDialogs: function(window, ignoreWindow) {
|
||||
_parentHasOtherAttachedDialog: function(parent, self) {
|
||||
var count = 0;
|
||||
window.foreach_transient(function(win) {
|
||||
if (win != ignoreWindow && win.get_window_type() == Meta.WindowType.MODAL_DIALOG)
|
||||
parent.foreach_transient(function(win) {
|
||||
if (win.get_window_type() == Meta.WindowType.MODAL_DIALOG && win != self)
|
||||
count++;
|
||||
return false;
|
||||
});
|
||||
return count != 0;
|
||||
},
|
||||
|
||||
_checkDimming: function(window, ignoreWindow) {
|
||||
let shouldDim = Meta.prefs_get_attach_modal_dialogs() && this._hasAttachedDialogs(window, ignoreWindow);
|
||||
|
||||
if (shouldDim && !window._dimmed) {
|
||||
window._dimmed = true;
|
||||
this._dimmedWindows.push(window);
|
||||
if (!Main.overview.visible)
|
||||
this._dimWindow(window, true);
|
||||
} else if (!shouldDim && window._dimmed) {
|
||||
window._dimmed = false;
|
||||
this._dimmedWindows = this._dimmedWindows.filter(function(win) {
|
||||
return win != window;
|
||||
});
|
||||
if (!Main.overview.visible)
|
||||
this._undimWindow(window, true);
|
||||
_markParentWindowAsDimmable: function(actor, animate) {
|
||||
if (Meta.prefs_get_attach_modal_dialogs()) {
|
||||
this._dimmedWindows.push(actor);
|
||||
if (this._shouldAnimate())
|
||||
this._dimParentWindow(actor, animate);
|
||||
}
|
||||
},
|
||||
|
||||
_dimWindow: function(window, animate) {
|
||||
let actor = window.get_compositor_private();
|
||||
if (!actor)
|
||||
_unmarkParentWindowAsDimmable: function(actor, animate) {
|
||||
if (!Main.overview.visible)
|
||||
this._undimParentWindow(actor, true);
|
||||
this._dimmedWindows = this._dimmedWindows.filter(function(win) {
|
||||
return win != actor;
|
||||
});
|
||||
},
|
||||
|
||||
_dimParentWindow: function(actor, animate) {
|
||||
let meta = actor.get_meta_window();
|
||||
let parent = meta.get_transient_for();
|
||||
if (!parent)
|
||||
return;
|
||||
let texture = actor.get_texture();
|
||||
let parentActor = parent.get_compositor_private();
|
||||
if (!parentActor || this._parentHasOtherAttachedDialog(parent, meta))
|
||||
return;
|
||||
let texture = parentActor.get_texture();
|
||||
if (animate)
|
||||
Tweener.addTween(getWindowDimmer(texture),
|
||||
{ dimFraction: 1.0,
|
||||
@ -277,11 +267,15 @@ WindowManager.prototype = {
|
||||
getWindowDimmer(texture).dimFraction = 1.0;
|
||||
},
|
||||
|
||||
_undimWindow: function(window, animate) {
|
||||
let actor = window.get_compositor_private();
|
||||
if (!actor)
|
||||
_undimParentWindow: function(actor, animate) {
|
||||
let meta = actor.get_meta_window();
|
||||
let parent = meta.get_transient_for();
|
||||
if (!parent)
|
||||
return;
|
||||
let texture = actor.get_texture();
|
||||
let parentActor = parent.get_compositor_private();
|
||||
if (!parentActor || this._parentHasOtherAttachedDialog(parent, meta))
|
||||
return;
|
||||
let texture = parentActor.get_texture();
|
||||
if (animate)
|
||||
Tweener.addTween(getWindowDimmer(texture),
|
||||
{ dimFraction: 0.0,
|
||||
@ -298,19 +292,17 @@ WindowManager.prototype = {
|
||||
let type = actor.meta_window.get_window_type();
|
||||
if (type == actor._windowType)
|
||||
return;
|
||||
if (type == Meta.WindowType.MODAL_DIALOG ||
|
||||
actor._windowType == Meta.WindowType.MODAL_DIALOG) {
|
||||
let parent = actor.get_meta_window().get_transient_for();
|
||||
if (parent)
|
||||
this._checkDimming(parent);
|
||||
}
|
||||
if (type == Meta.WindowType.MODAL_DIALOG)
|
||||
this._markParentWindowAsDimmable(actor, true);
|
||||
else if (actor._windowType == Meta.WindowType.MODAL_DIALOG)
|
||||
this._unmarkParentWindowAsDimmable(actor, true);
|
||||
|
||||
actor._windowType = type;
|
||||
}));
|
||||
if (actor.meta_window.get_window_type() == Meta.WindowType.MODAL_DIALOG
|
||||
&& Meta.prefs_get_attach_modal_dialogs()
|
||||
&& actor.get_meta_window().get_transient_for()) {
|
||||
this._checkDimming(actor.get_meta_window().get_transient_for());
|
||||
this._markParentWindowAsDimmable(actor, true);
|
||||
if (this._shouldAnimate()) {
|
||||
actor.set_scale(1.0, 0.0);
|
||||
actor.show();
|
||||
@ -370,20 +362,14 @@ WindowManager.prototype = {
|
||||
},
|
||||
|
||||
_destroyWindow : function(shellwm, actor) {
|
||||
let window = actor.meta_window;
|
||||
let parent = window.get_transient_for();
|
||||
let parent = actor.meta_window.get_transient_for();
|
||||
if (actor._notifyWindowTypeSignalId) {
|
||||
window.disconnect(actor._notifyWindowTypeSignalId);
|
||||
actor.meta_window.disconnect(actor._notifyWindowTypeSignalId);
|
||||
actor._notifyWindowTypeSignalId = 0;
|
||||
}
|
||||
if (window._dimmed) {
|
||||
this._dimmedWindows = this._dimmedWindows.filter(function(win) {
|
||||
return win != window;
|
||||
});
|
||||
}
|
||||
while (window.get_window_type() == Meta.WindowType.MODAL_DIALOG
|
||||
while (actor.meta_window.get_window_type() == Meta.WindowType.MODAL_DIALOG
|
||||
&& parent) {
|
||||
this._checkDimming(parent, window);
|
||||
this._unmarkParentWindowAsDimmable(actor, true);
|
||||
if (!Meta.prefs_get_attach_modal_dialogs()
|
||||
|| !this._shouldAnimate())
|
||||
break;
|
||||
@ -511,8 +497,6 @@ WindowManager.prototype = {
|
||||
|
||||
for (let i = 0; i < switchData.windows.length; i++) {
|
||||
let w = switchData.windows[i];
|
||||
if (w.window.is_destroyed()) // Window gone
|
||||
continue;
|
||||
if (w.window.get_parent() == switchData.outGroup) {
|
||||
w.window.reparent(w.parent);
|
||||
w.window.hide();
|
||||
@ -534,88 +518,49 @@ WindowManager.prototype = {
|
||||
|
||||
let tabPopup = new AltTab.AltTabPopup();
|
||||
|
||||
if (!tabPopup.show(backwards, binding == 'switch_group'))
|
||||
if (!tabPopup.show(backwards))
|
||||
tabPopup.destroy();
|
||||
},
|
||||
|
||||
_startA11ySwitcher : function(shellwm, binding, window, backwards) {
|
||||
Main.ctrlAltTabManager.popup(backwards);
|
||||
},
|
||||
|
||||
_showWorkspaceSwitcher : function(shellwm, binding, window, backwards) {
|
||||
/* We don't support this kind of layout */
|
||||
if (binding == 'switch_to_workspace_up' || binding == 'switch_to_workspace_down')
|
||||
return;
|
||||
|
||||
if (global.screen.n_workspaces == 1)
|
||||
return;
|
||||
|
||||
if (this._workspaceSwitcherPopup == null)
|
||||
this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
|
||||
|
||||
if (binding == 'switch_to_workspace_up')
|
||||
this.actionMoveWorkspaceUp();
|
||||
else if (binding == 'switch_to_workspace_down')
|
||||
this.actionMoveWorkspaceDown();
|
||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
||||
// but that could be considered confusing.
|
||||
// else if (binding == 'switch_to_workspace_left')
|
||||
// this.actionMoveWorkspaceLeft();
|
||||
// else if (binding == 'switch_to_workspace_right')
|
||||
// this.actionMoveWorkspaceRight();
|
||||
if (binding == 'switch_to_workspace_left') {
|
||||
this.actionMoveWorkspaceLeft();
|
||||
}
|
||||
|
||||
if (binding == 'switch_to_workspace_right') {
|
||||
this.actionMoveWorkspaceRight();
|
||||
}
|
||||
},
|
||||
|
||||
actionMoveWorkspaceLeft: function() {
|
||||
let rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL);
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
|
||||
indexToActivate++;
|
||||
else if (!rtl && activeWorkspaceIndex > 0)
|
||||
indexToActivate--;
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
|
||||
if (activeWorkspaceIndex > 0) {
|
||||
global.screen.get_workspace_by_index(activeWorkspaceIndex - 1).activate(global.get_current_time());
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.LEFT, activeWorkspaceIndex - 1);
|
||||
} else if (!Main.overview.visible) {
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.LEFT, activeWorkspaceIndex);
|
||||
}
|
||||
},
|
||||
|
||||
actionMoveWorkspaceRight: function() {
|
||||
let rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL);
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (rtl && activeWorkspaceIndex > 0)
|
||||
indexToActivate--;
|
||||
else if (!rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
|
||||
indexToActivate++;
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
|
||||
},
|
||||
|
||||
actionMoveWorkspaceUp: function() {
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (activeWorkspaceIndex > 0)
|
||||
indexToActivate--;
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
|
||||
},
|
||||
|
||||
actionMoveWorkspaceDown: function() {
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (activeWorkspaceIndex < global.screen.n_workspaces - 1)
|
||||
indexToActivate++;
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
|
||||
if (activeWorkspaceIndex < global.screen.n_workspaces - 1) {
|
||||
global.screen.get_workspace_by_index(activeWorkspaceIndex + 1).activate(global.get_current_time());
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.RIGHT, activeWorkspaceIndex + 1);
|
||||
} else if (!Main.overview.visible) {
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.RIGHT, activeWorkspaceIndex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -12,8 +12,8 @@ const Tweener = imports.ui.tweener;
|
||||
const ANIMATION_TIME = 0.1;
|
||||
const DISPLAY_TIMEOUT = 600;
|
||||
|
||||
const UP = -1;
|
||||
const DOWN = 1;
|
||||
const LEFT = -1;
|
||||
const RIGHT = 1;
|
||||
|
||||
function WorkspaceSwitcherPopup() {
|
||||
this._init();
|
||||
@ -32,8 +32,6 @@ WorkspaceSwitcherPopup.prototype = {
|
||||
this._container = new St.BoxLayout({ style_class: 'workspace-switcher-container' });
|
||||
this._list = new Shell.GenericContainer({ style_class: 'workspace-switcher' });
|
||||
this._itemSpacing = 0;
|
||||
this._childHeight = 0;
|
||||
this._childWidth = 0;
|
||||
this._list.connect('style-changed', Lang.bind(this, function() {
|
||||
this._itemSpacing = this._list.get_theme_node().get_length('spacing');
|
||||
}));
|
||||
@ -49,59 +47,57 @@ WorkspaceSwitcherPopup.prototype = {
|
||||
|
||||
this._position();
|
||||
|
||||
this.actor.hide();
|
||||
|
||||
this.actor.show();
|
||||
this._timeoutId = Mainloop.timeout_add(DISPLAY_TIMEOUT, Lang.bind(this, this._onTimeout));
|
||||
},
|
||||
|
||||
_getPreferredHeight : function (actor, forWidth, alloc) {
|
||||
_getPreferredWidth : function (actor, forHeight, alloc) {
|
||||
let children = this._list.get_children();
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
let availHeight = primary.height;
|
||||
availHeight -= Main.panel.actor.height;
|
||||
availHeight -= this.actor.get_theme_node().get_vertical_padding();
|
||||
availHeight -= this._container.get_theme_node().get_vertical_padding();
|
||||
availHeight -= this._list.get_theme_node().get_vertical_padding();
|
||||
let availwidth = primary.width;
|
||||
availwidth -= this.actor.get_theme_node().get_horizontal_padding();
|
||||
availwidth -= this._container.get_theme_node().get_horizontal_padding();
|
||||
availwidth -= this._list.get_theme_node().get_horizontal_padding();
|
||||
|
||||
let height = 0;
|
||||
let width = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [childMinHeight, childNaturalHeight] = children[i].get_preferred_height(-1);
|
||||
let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(childNaturalHeight);
|
||||
height += childNaturalHeight * primary.width / primary.height;
|
||||
let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(-1);
|
||||
let [childMinHeight, childNaturalHeight] = children[i].get_preferred_height(childNaturalWidth);
|
||||
width += childNaturalHeight * primary.width / primary.height;
|
||||
}
|
||||
|
||||
let spacing = this._itemSpacing * (global.screen.n_workspaces - 1);
|
||||
height += spacing;
|
||||
height = Math.min(height, availHeight);
|
||||
width += spacing;
|
||||
width = Math.min(width, availwidth);
|
||||
|
||||
this._childHeight = (height - spacing) / global.screen.n_workspaces;
|
||||
this._childWidth = (width - spacing) / global.screen.n_workspaces;
|
||||
|
||||
alloc.min_size = height;
|
||||
alloc.natural_size = height;
|
||||
alloc.min_size = width;
|
||||
alloc.natural_size = width;
|
||||
},
|
||||
|
||||
_getPreferredWidth : function (actor, forHeight, alloc) {
|
||||
_getPreferredHeight : function (actor, forWidth, alloc) {
|
||||
let primary = global.get_primary_monitor();
|
||||
this._childWidth = Math.round(this._childHeight * primary.width / primary.height);
|
||||
this._childHeight = Math.round(this._childWidth * primary.height / primary.width);
|
||||
|
||||
alloc.min_size = this._childWidth;
|
||||
alloc.natural_size = this._childWidth;
|
||||
alloc.min_size = this._childHeight;
|
||||
alloc.natural_size = this._childHeight;
|
||||
},
|
||||
|
||||
_allocate : function (actor, box, flags) {
|
||||
let children = this._list.get_children();
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
let y = box.y1;
|
||||
let prevChildBoxY2 = box.y1 - this._itemSpacing;
|
||||
let x = box.x1;
|
||||
let prevChildBoxX2 = box.x1 - this._itemSpacing;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
childBox.x1 = box.x1;
|
||||
childBox.x2 = box.x1 + this._childWidth;
|
||||
childBox.y1 = prevChildBoxY2 + this._itemSpacing;
|
||||
childBox.y2 = Math.round(y + this._childHeight);
|
||||
y += this._childHeight + this._itemSpacing;
|
||||
prevChildBoxY2 = childBox.y2;
|
||||
childBox.x1 = prevChildBoxX2 + this._itemSpacing;
|
||||
childBox.x2 = Math.round(x + this._childWidth);
|
||||
childBox.y1 = box.y1;
|
||||
childBox.y2 = box.y1 + this._childHeight;
|
||||
x += this._childWidth + this._itemSpacing;
|
||||
prevChildBoxX2 = childBox.x2;
|
||||
children[i].allocate(childBox, flags);
|
||||
}
|
||||
},
|
||||
@ -112,10 +108,10 @@ WorkspaceSwitcherPopup.prototype = {
|
||||
for (let i = 0; i < global.screen.n_workspaces; i++) {
|
||||
let indicator = null;
|
||||
|
||||
if (i == activeWorkspaceIndex && direction == UP)
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-active-up' });
|
||||
else if(i == activeWorkspaceIndex && direction == DOWN)
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-active-down' });
|
||||
if (i == activeWorkspaceIndex && direction == LEFT)
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-active-left' });
|
||||
else if(i == activeWorkspaceIndex && direction == RIGHT)
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-active-right' });
|
||||
else
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-box' });
|
||||
|
||||
@ -127,8 +123,7 @@ WorkspaceSwitcherPopup.prototype = {
|
||||
_position: function() {
|
||||
let primary = global.get_primary_monitor();
|
||||
this._container.x = primary.x + Math.floor((primary.width - this._container.width) / 2);
|
||||
this._container.y = primary.y + Main.panel.actor.height +
|
||||
Math.floor(((primary.height - Main.panel.actor.height) - this._container.height) / 2);
|
||||
this._container.y = primary.y + Math.floor((primary.height - this._container.height) / 2);
|
||||
},
|
||||
|
||||
_show : function() {
|
||||
|
@ -1,885 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Workspace = imports.ui.workspace;
|
||||
const WorkspacesView = imports.ui.workspacesView;
|
||||
|
||||
// The maximum size of a thumbnail is 1/8 the width and height of the screen
|
||||
let MAX_THUMBNAIL_SCALE = 1/8.;
|
||||
|
||||
const RESCALE_ANIMATION_TIME = 0.2;
|
||||
const SLIDE_ANIMATION_TIME = 0.2;
|
||||
|
||||
function WindowClone(realWindow) {
|
||||
this._init(realWindow);
|
||||
}
|
||||
|
||||
WindowClone.prototype = {
|
||||
_init : function(realWindow) {
|
||||
this.actor = new Clutter.Clone({ source: realWindow.get_texture(),
|
||||
reactive: true });
|
||||
this.actor._delegate = this;
|
||||
this.realWindow = realWindow;
|
||||
this.metaWindow = realWindow.meta_window;
|
||||
|
||||
this._positionChangedId = this.realWindow.connect('position-changed',
|
||||
Lang.bind(this, this._onPositionChanged));
|
||||
this._realWindowDestroyedId = this.realWindow.connect('destroy',
|
||||
Lang.bind(this, this._disconnectRealWindowSignals));
|
||||
this._onPositionChanged();
|
||||
|
||||
this.actor.connect('button-release-event',
|
||||
Lang.bind(this, this._onButtonRelease));
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._draggable = DND.makeDraggable(this.actor,
|
||||
{ restoreOnSuccess: true,
|
||||
dragActorMaxSize: Workspace.WINDOW_DND_SIZE,
|
||||
dragActorOpacity: Workspace.DRAGGING_WINDOW_OPACITY });
|
||||
this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
this.inDrag = false;
|
||||
},
|
||||
|
||||
setStackAbove: function (actor) {
|
||||
this._stackAbove = actor;
|
||||
if (this._stackAbove == null)
|
||||
this.actor.lower_bottom();
|
||||
else
|
||||
this.actor.raise(this._stackAbove);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onPositionChanged: function() {
|
||||
let rect = this.metaWindow.get_outer_rect();
|
||||
this.actor.set_position(this.realWindow.x, this.realWindow.y);
|
||||
},
|
||||
|
||||
_disconnectRealWindowSignals: function() {
|
||||
if (this._positionChangedId != 0) {
|
||||
this.realWindow.disconnect(this._positionChangedId);
|
||||
this._positionChangedId = 0;
|
||||
}
|
||||
|
||||
if (this._realWindowDestroyedId != 0) {
|
||||
this.realWindow.disconnect(this._realWindowDestroyedId);
|
||||
this._realWindowDestroyedId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._disconnectRealWindowSignals();
|
||||
|
||||
this.actor._delegate = null;
|
||||
|
||||
if (this.inDrag) {
|
||||
this.emit('drag-end');
|
||||
this.inDrag = false;
|
||||
}
|
||||
|
||||
this.disconnectAll();
|
||||
},
|
||||
|
||||
_onButtonRelease : function (actor, event) {
|
||||
this.emit('selected', event.get_time());
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_onDragBegin : function (draggable, time) {
|
||||
this.inDrag = true;
|
||||
this.emit('drag-begin');
|
||||
},
|
||||
|
||||
_onDragEnd : function (draggable, time, snapback) {
|
||||
this.inDrag = false;
|
||||
|
||||
// We may not have a parent if DnD completed successfully, in
|
||||
// which case our clone will shortly be destroyed and replaced
|
||||
// with a new one on the target workspace.
|
||||
if (this.actor.get_parent() != null) {
|
||||
if (this._stackAbove == null)
|
||||
this.actor.lower_bottom();
|
||||
else
|
||||
this.actor.raise(this._stackAbove);
|
||||
}
|
||||
|
||||
|
||||
this.emit('drag-end');
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(WindowClone.prototype);
|
||||
|
||||
|
||||
const ThumbnailState = {
|
||||
NEW : 0,
|
||||
ANIMATING_IN : 1,
|
||||
NORMAL: 2,
|
||||
REMOVING : 3,
|
||||
ANIMATING_OUT : 4,
|
||||
ANIMATED_OUT : 5,
|
||||
COLLAPSING : 6,
|
||||
DESTROYED : 7
|
||||
};
|
||||
|
||||
/**
|
||||
* @metaWorkspace: a #Meta.Workspace
|
||||
*/
|
||||
function WorkspaceThumbnail(metaWorkspace) {
|
||||
this._init(metaWorkspace);
|
||||
}
|
||||
|
||||
WorkspaceThumbnail.prototype = {
|
||||
_init : function(metaWorkspace) {
|
||||
this.metaWorkspace = metaWorkspace;
|
||||
this.monitorIndex = global.get_primary_monitor_index();
|
||||
|
||||
this.actor = new St.Group({ reactive: true,
|
||||
clip_to_allocation: true,
|
||||
style_class: 'workspace-thumbnail' });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._contents = new Clutter.Group();
|
||||
this.actor.add_actor(this._contents);
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this.actor.connect('button-press-event', Lang.bind(this,
|
||||
function(actor, event) {
|
||||
return true;
|
||||
}));
|
||||
this.actor.connect('button-release-event', Lang.bind(this,
|
||||
function(actor, event) {
|
||||
this._activate();
|
||||
return true;
|
||||
}));
|
||||
|
||||
this._background = new Clutter.Clone({ source: global.background_actor });
|
||||
this._contents.add_actor(this._background);
|
||||
|
||||
let monitor = global.get_primary_monitor();
|
||||
this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
|
||||
|
||||
let windows = global.get_window_actors().filter(this._isMyWindow, this);
|
||||
|
||||
// Create clones for windows that should be visible in the Overview
|
||||
this._windows = [];
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
if (this._isOverviewWindow(windows[i])) {
|
||||
this._addWindowClone(windows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Track window changes
|
||||
this._windowAddedId = this.metaWorkspace.connect('window-added',
|
||||
Lang.bind(this, this._windowAdded));
|
||||
this._windowRemovedId = this.metaWorkspace.connect('window-removed',
|
||||
Lang.bind(this, this._windowRemoved));
|
||||
this._windowEnteredMonitorId = global.screen.connect('window-entered-monitor',
|
||||
Lang.bind(this, this._windowEnteredMonitor));
|
||||
this._windowLeftMonitorId = global.screen.connect('window-left-monitor',
|
||||
Lang.bind(this, this._windowLeftMonitor));
|
||||
|
||||
this.state = ThumbnailState.NORMAL;
|
||||
this._slidePosition = 0; // Fully slid in
|
||||
this._collapseFraction = 0; // Not collapsed
|
||||
},
|
||||
|
||||
setPorthole: function(x, y, width, height) {
|
||||
this._portholeX = x;
|
||||
this._portholeY = y;
|
||||
this.actor.set_size(width, height);
|
||||
this._contents.set_position(-x, -y);
|
||||
},
|
||||
|
||||
_lookupIndex: function (metaWindow) {
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
if (this._windows[i].metaWindow == metaWindow) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
syncStacking: function(stackIndices) {
|
||||
this._windows.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; });
|
||||
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
let metaWindow = clone.metaWindow;
|
||||
if (i == 0) {
|
||||
clone.setStackAbove(this._background);
|
||||
} else {
|
||||
let previousClone = this._windows[i - 1];
|
||||
clone.setStackAbove(previousClone.actor);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
set slidePosition(slidePosition) {
|
||||
this._slidePosition = slidePosition;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get slidePosition() {
|
||||
return this._slidePosition;
|
||||
},
|
||||
|
||||
set collapseFraction(collapseFraction) {
|
||||
this._collapseFraction = collapseFraction;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get collapseFraction() {
|
||||
return this._collapseFraction;
|
||||
},
|
||||
|
||||
_doRemoveWindow : function(metaWin) {
|
||||
let win = metaWin.get_compositor_private();
|
||||
|
||||
// find the position of the window in our list
|
||||
let index = this._lookupIndex (metaWin);
|
||||
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
// Check if window still should be here
|
||||
if (win && this._isMyWindow(win))
|
||||
return;
|
||||
|
||||
let clone = this._windows[index];
|
||||
this._windows.splice(index, 1);
|
||||
clone.destroy();
|
||||
},
|
||||
|
||||
_doAddWindow : function(metaWin) {
|
||||
if (this.leavingOverview)
|
||||
return;
|
||||
|
||||
let win = metaWin.get_compositor_private();
|
||||
|
||||
if (!win) {
|
||||
// Newly-created windows are added to a workspace before
|
||||
// the compositor finds out about them...
|
||||
Mainloop.idle_add(Lang.bind(this,
|
||||
function () {
|
||||
if (this.actor && metaWin.get_compositor_private())
|
||||
this._doAddWindow(metaWin);
|
||||
return false;
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
// We might have the window in our list already if it was on all workspaces and
|
||||
// now was moved to this workspace
|
||||
if (this._lookupIndex (metaWin) != -1)
|
||||
return;
|
||||
|
||||
if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
|
||||
return;
|
||||
|
||||
let clone = this._addWindowClone(win);
|
||||
},
|
||||
|
||||
_windowAdded : function(metaWorkspace, metaWin) {
|
||||
this._doAddWindow(metaWin);
|
||||
},
|
||||
|
||||
_windowRemoved : function(metaWorkspace, metaWin) {
|
||||
this._doRemoveWindow(metaWin);
|
||||
},
|
||||
|
||||
_windowEnteredMonitor : function(metaScreen, monitorIndex, metaWin) {
|
||||
if (monitorIndex == this.monitorIndex) {
|
||||
this._doAddWindow(metaWin);
|
||||
}
|
||||
},
|
||||
|
||||
_windowLeftMonitor : function(metaScreen, monitorIndex, metaWin) {
|
||||
if (monitorIndex == this.monitorIndex) {
|
||||
this._doRemoveWindow(metaWin);
|
||||
}
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onDestroy: function(actor) {
|
||||
this.metaWorkspace.disconnect(this._windowAddedId);
|
||||
this.metaWorkspace.disconnect(this._windowRemovedId);
|
||||
global.screen.disconnect(this._windowEnteredMonitorId);
|
||||
global.screen.disconnect(this._windowLeftMonitorId);
|
||||
|
||||
this._windows = [];
|
||||
this.actor = null;
|
||||
},
|
||||
|
||||
// Tests if @win belongs to this workspace and monitor
|
||||
_isMyWindow : function (win) {
|
||||
return Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index()) &&
|
||||
(!win.get_meta_window() || win.get_meta_window().get_monitor() == this.monitorIndex);
|
||||
},
|
||||
|
||||
// Tests if @win should be shown in the Overview
|
||||
_isOverviewWindow : function (win) {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
return tracker.is_window_interesting(win.get_meta_window());
|
||||
},
|
||||
|
||||
// Create a clone of a (non-desktop) window and add it to the window list
|
||||
_addWindowClone : function(win) {
|
||||
let clone = new WindowClone(win);
|
||||
|
||||
clone.connect('selected',
|
||||
Lang.bind(this, this._activate));
|
||||
clone.connect('drag-begin',
|
||||
Lang.bind(this, function(clone) {
|
||||
Main.overview.beginWindowDrag();
|
||||
}));
|
||||
clone.connect('drag-end',
|
||||
Lang.bind(this, function(clone) {
|
||||
Main.overview.endWindowDrag();
|
||||
}));
|
||||
this._contents.add_actor(clone.actor);
|
||||
|
||||
if (this._windows.length == 0)
|
||||
clone.setStackAbove(this._background);
|
||||
else
|
||||
clone.setStackAbove(this._windows[this._windows.length - 1].actor);
|
||||
|
||||
this._windows.push(clone);
|
||||
|
||||
return clone;
|
||||
},
|
||||
|
||||
_activate : function (clone, time) {
|
||||
if (this.state > ThumbnailState.NORMAL)
|
||||
return;
|
||||
|
||||
// a click on the already current workspace should go back to the main view
|
||||
if (this.metaWorkspace == global.screen.get_active_workspace())
|
||||
Main.overview.hide();
|
||||
else
|
||||
this.metaWorkspace.activate(time);
|
||||
},
|
||||
|
||||
// Draggable target interface
|
||||
handleDragOver : function(source, actor, x, y, time) {
|
||||
if (source == Main.xdndHandler) {
|
||||
this.metaWorkspace.activate(time);
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
}
|
||||
|
||||
if (this.state > ThumbnailState.NORMAL)
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
if (source.realWindow && !this._isMyWindow(source.realWindow))
|
||||
return DND.DragMotionResult.MOVE_DROP;
|
||||
if (source.shellWorkspaceLaunch)
|
||||
return DND.DragMotionResult.COPY_DROP;
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
acceptDrop : function(source, actor, x, y, time) {
|
||||
if (this.state > ThumbnailState.NORMAL)
|
||||
return false;
|
||||
|
||||
if (source.realWindow) {
|
||||
let win = source.realWindow;
|
||||
if (this._isMyWindow(win))
|
||||
return false;
|
||||
|
||||
let metaWindow = win.get_meta_window();
|
||||
|
||||
// We need to move the window before changing the workspace, because
|
||||
// the move itself could cause a workspace change if the window enters
|
||||
// the primary monitor
|
||||
if (metaWindow.get_monitor() != this.monitorIndex)
|
||||
metaWindow.move_to_monitor(this.monitorIndex);
|
||||
|
||||
metaWindow.change_workspace_by_index(this.metaWorkspace.index(),
|
||||
false, // don't create workspace
|
||||
time);
|
||||
return true;
|
||||
} else if (source.shellWorkspaceLaunch) {
|
||||
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
|
||||
timestamp: time });
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(WorkspaceThumbnail.prototype);
|
||||
|
||||
|
||||
function ThumbnailsBox() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
ThumbnailsBox.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new Shell.GenericContainer({ style_class: 'workspace-thumbnails',
|
||||
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT });
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
|
||||
// When we animate the scale, we don't animate the requested size of the thumbnails, rather
|
||||
// we ask for our final size and then animate within that size. This slightly simplifies the
|
||||
// interaction with the main workspace windows (instead of constantly reallocating them
|
||||
// to a new size, they get a new size once, then use the standard window animation code
|
||||
// allocate the windows to their new positions), however it causes problems for drawing
|
||||
// the background and border wrapped around the thumbnail as we animate - we can't just pack
|
||||
// the container into a box and set style properties on the box since that box would wrap
|
||||
// around the final size not the animating size. So instead we fake the background with
|
||||
// an actor underneath the content and adjust the allocation of our children to leave space
|
||||
// for the border and padding of the background actor.
|
||||
this._background = new St.Bin({ style_class: 'workspace-thumbnails-background' });
|
||||
|
||||
this.actor.add_actor(this._background);
|
||||
|
||||
let indicator = new St.Bin({ style_class: 'workspace-thumbnail-indicator' });
|
||||
|
||||
// We don't want the indicator to affect drag-and-drop
|
||||
Shell.util_set_hidden_from_pick(indicator, true);
|
||||
|
||||
this._indicator = indicator;
|
||||
this.actor.add_actor(indicator);
|
||||
|
||||
this._targetScale = 0;
|
||||
this._scale = 0;
|
||||
this._pendingScaleUpdate = false;
|
||||
this._stateUpdateQueued = false;
|
||||
this._animatingIndicator = false;
|
||||
this._indicatorY = 0; // only used when _animatingIndicator is true
|
||||
|
||||
this._stateCounts = {};
|
||||
for (let key in ThumbnailState)
|
||||
this._stateCounts[ThumbnailState[key]] = 0;
|
||||
|
||||
this._thumbnails = [];
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this._switchWorkspaceNotifyId =
|
||||
global.window_manager.connect('switch-workspace',
|
||||
Lang.bind(this, this._activeWorkspaceChanged));
|
||||
|
||||
this._targetScale = 0;
|
||||
this._scale = 0;
|
||||
this._pendingScaleUpdate = false;
|
||||
this._stateUpdateQueued = false;
|
||||
|
||||
this._stateCounts = {};
|
||||
for (let key in ThumbnailState)
|
||||
this._stateCounts[ThumbnailState[key]] = 0;
|
||||
|
||||
// The "porthole" is the portion of the screen that we show in the workspaces
|
||||
let panelHeight = Main.panel.actor.height;
|
||||
let monitor = global.get_primary_monitor();
|
||||
this._porthole = {
|
||||
x: monitor.x,
|
||||
y: monitor.y + panelHeight,
|
||||
width: monitor.width,
|
||||
height: monitor.height - panelHeight
|
||||
};
|
||||
|
||||
this.addThumbnails(0, global.screen.n_workspaces);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
if (this._switchWorkspaceNotifyId > 0) {
|
||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||
this._switchWorkspaceNotifyId = 0;
|
||||
}
|
||||
|
||||
for (let w = 0; w < this._thumbnails.length; w++)
|
||||
this._thumbnails[w].destroy();
|
||||
this._thumbnails = [];
|
||||
},
|
||||
|
||||
addThumbnails: function(start, count) {
|
||||
for (let k = start; k < start + count; k++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(k);
|
||||
let thumbnail = new WorkspaceThumbnail(metaWorkspace);
|
||||
thumbnail.setPorthole(this._porthole.x, this._porthole.y,
|
||||
this._porthole.width, this._porthole.height);
|
||||
this._thumbnails.push(thumbnail);
|
||||
this.actor.add_actor(thumbnail.actor);
|
||||
|
||||
if (start > 0) { // not the initial fill
|
||||
thumbnail.state = ThumbnailState.NEW;
|
||||
thumbnail.slidePosition = 1; // start slid out
|
||||
this._haveNewThumbnails = true;
|
||||
} else {
|
||||
thumbnail.state = ThumbnailState.NORMAL;
|
||||
}
|
||||
|
||||
this._stateCounts[thumbnail.state]++;
|
||||
}
|
||||
|
||||
this._queueUpdateStates();
|
||||
|
||||
// The thumbnails indicator actually needs to be on top of the thumbnails
|
||||
this._indicator.raise_top();
|
||||
},
|
||||
|
||||
removeThumbmails: function(start, count) {
|
||||
let currentPos = 0;
|
||||
for (let k = 0; k < this._thumbnails.length; k++) {
|
||||
let thumbnail = this._thumbnails[k];
|
||||
|
||||
if (thumbnail.state > ThumbnailState.NORMAL)
|
||||
continue;
|
||||
|
||||
if (currentPos >= start && currentPos < start + count)
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.REMOVING);
|
||||
|
||||
currentPos++;
|
||||
}
|
||||
|
||||
this._queueUpdateStates();
|
||||
},
|
||||
|
||||
syncStacking: function(stackIndices) {
|
||||
for (let i = 0; i < this._thumbnails.length; i++)
|
||||
this._thumbnails[i].syncStacking(stackIndices);
|
||||
},
|
||||
|
||||
set scale(scale) {
|
||||
this._scale = scale;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get scale() {
|
||||
return this._scale;
|
||||
},
|
||||
|
||||
set indicatorY(indicatorY) {
|
||||
this._indicatorY = indicatorY;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get indicatorY() {
|
||||
return this._indicatorY;
|
||||
},
|
||||
|
||||
_setThumbnailState: function(thumbnail, state) {
|
||||
this._stateCounts[thumbnail.state]--;
|
||||
thumbnail.state = state;
|
||||
this._stateCounts[thumbnail.state]++;
|
||||
},
|
||||
|
||||
_iterateStateThumbnails: function(state, callback) {
|
||||
if (this._stateCounts[state] == 0)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < this._thumbnails.length; i++) {
|
||||
if (this._thumbnails[i].state == state)
|
||||
callback.call(this, this._thumbnails[i]);
|
||||
}
|
||||
},
|
||||
|
||||
_tweenScale: function() {
|
||||
Tweener.addTween(this,
|
||||
{ scale: this._targetScale,
|
||||
time: RESCALE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._queueUpdateStates,
|
||||
onCompleteScope: this });
|
||||
},
|
||||
|
||||
_updateStates: function() {
|
||||
this._stateUpdateQueued = false;
|
||||
|
||||
// If we are animating the indicator, wait
|
||||
if (this._animatingIndicator)
|
||||
return;
|
||||
|
||||
// Then slide out any thumbnails that have been destroyed
|
||||
this._iterateStateThumbnails(ThumbnailState.REMOVING,
|
||||
function(thumbnail) {
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_OUT);
|
||||
|
||||
Tweener.addTween(thumbnail,
|
||||
{ slidePosition: 1,
|
||||
time: SLIDE_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: function() {
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.ANIMATED_OUT);
|
||||
this._queueUpdateStates();
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
});
|
||||
|
||||
// As long as things are sliding out, don't proceed
|
||||
if (this._stateCounts[ThumbnailState.ANIMATING_OUT] > 0)
|
||||
return;
|
||||
|
||||
// Once that's complete, we can start scaling to the new size and collapse any removed thumbnails
|
||||
this._iterateStateThumbnails(ThumbnailState.ANIMATED_OUT,
|
||||
function(thumbnail) {
|
||||
this.actor.set_skip_paint(thumbnail.actor, true);
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.COLLAPSING);
|
||||
Tweener.addTween(thumbnail,
|
||||
{ collapseFraction: 1,
|
||||
time: RESCALE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this._stateCounts[thumbnail.state]--;
|
||||
thumbnail.state = ThumbnailState.DESTROYED;
|
||||
|
||||
let index = this._thumbnails.indexOf(thumbnail);
|
||||
this._thumbnails.splice(index, 1);
|
||||
thumbnail.destroy();
|
||||
|
||||
this._queueUpdateStates();
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
});
|
||||
|
||||
if (this._pendingScaleUpdate) {
|
||||
this._tweenScale();
|
||||
this._pendingScaleUpdate = false;
|
||||
}
|
||||
|
||||
// Wait until that's done
|
||||
if (this._scale != this._targetScale || this._stateCounts[ThumbnailState.COLLAPSING] > 0)
|
||||
return;
|
||||
|
||||
// And then slide in any new thumbnails
|
||||
this._iterateStateThumbnails(ThumbnailState.NEW,
|
||||
function(thumbnail) {
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_IN);
|
||||
Tweener.addTween(thumbnail,
|
||||
{ slidePosition: 0,
|
||||
time: SLIDE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.NORMAL);
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_queueUpdateStates: function() {
|
||||
if (this._stateUpdateQueued)
|
||||
return;
|
||||
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||||
Lang.bind(this, this._updateStates));
|
||||
|
||||
this._stateUpdateQueued = true;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
// See comment about this._background in _init()
|
||||
let themeNode = this._background.get_theme_node();
|
||||
|
||||
forWidth = themeNode.adjust_for_width(forWidth);
|
||||
|
||||
// Note that for getPreferredWidth/Height we cheat a bit and skip propagating
|
||||
// the size request to our children because we know how big they are and know
|
||||
// that the actors aren't depending on the virtual functions being called.
|
||||
|
||||
if (this._thumbnails.length == 0)
|
||||
return;
|
||||
|
||||
let spacing = this.actor.get_theme_node().get_length('spacing');
|
||||
let nWorkspaces = global.screen.n_workspaces;
|
||||
let totalSpacing = (nWorkspaces - 1) * spacing;
|
||||
|
||||
[alloc.min_size, alloc.natural_size] =
|
||||
themeNode.adjust_preferred_height(totalSpacing,
|
||||
totalSpacing + nWorkspaces * this._porthole.height * MAX_THUMBNAIL_SCALE);
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
// See comment about this._background in _init()
|
||||
let themeNode = this._background.get_theme_node();
|
||||
|
||||
if (this._thumbnails.length == 0)
|
||||
return;
|
||||
|
||||
// We don't animate our preferred width, which is always reported according
|
||||
// to the actual number of current workspaces, we just animate within that
|
||||
|
||||
let spacing = this.actor.get_theme_node().get_length('spacing');
|
||||
let nWorkspaces = global.screen.n_workspaces;
|
||||
let totalSpacing = (nWorkspaces - 1) * spacing;
|
||||
|
||||
let avail = forHeight - totalSpacing;
|
||||
|
||||
let scale = (avail / nWorkspaces) / this._porthole.height;
|
||||
scale = Math.min(scale, MAX_THUMBNAIL_SCALE);
|
||||
|
||||
let width = Math.round(this._porthole.width * scale);
|
||||
[alloc.min_size, alloc.natural_size] =
|
||||
themeNode.adjust_preferred_width(width, width);
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
|
||||
|
||||
// See comment about this._background in _init()
|
||||
let themeNode = this._background.get_theme_node();
|
||||
let contentBox = themeNode.get_content_box(box);
|
||||
|
||||
if (this._thumbnails.length == 0) // not visible
|
||||
return;
|
||||
|
||||
let portholeWidth = this._porthole.width;
|
||||
let portholeHeight = this._porthole.height;
|
||||
let spacing = this.actor.get_theme_node().get_length('spacing');
|
||||
|
||||
// Compute the scale we'll need once everything is updated
|
||||
let nWorkspaces = global.screen.n_workspaces;
|
||||
let totalSpacing = (nWorkspaces - 1) * spacing;
|
||||
let avail = (contentBox.y2 - contentBox.y1) - totalSpacing;
|
||||
|
||||
let newScale = (avail / nWorkspaces) / portholeHeight;
|
||||
newScale = Math.min(newScale, MAX_THUMBNAIL_SCALE);
|
||||
|
||||
if (newScale != this._targetScale) {
|
||||
if (this._targetScale > 0) {
|
||||
// We don't do the tween immediately because we need to observe the ordering
|
||||
// in queueUpdateStates - if workspaces have been removed we need to slide them
|
||||
// out as the first thing.
|
||||
this._targetScale = newScale;
|
||||
this._pendingScaleUpdate = true;
|
||||
} else {
|
||||
this._targetScale = this._scale = newScale;
|
||||
}
|
||||
|
||||
this._queueUpdateStates();
|
||||
}
|
||||
|
||||
let thumbnailHeight = portholeHeight * this._scale;
|
||||
let thumbnailWidth = Math.round(portholeWidth * this._scale);
|
||||
let roundedHScale = thumbnailWidth / portholeWidth;
|
||||
|
||||
let slideOffset; // X offset when thumbnail is fully slid offscreen
|
||||
if (rtl)
|
||||
slideOffset = - (thumbnailWidth + themeNode.get_padding(St.Side.LEFT));
|
||||
else
|
||||
slideOffset = thumbnailWidth + themeNode.get_padding(St.Side.RIGHT);
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
// The background is horizontally restricted to correspond to the current thumbnail size
|
||||
// but otherwise covers the entire allocation
|
||||
if (rtl) {
|
||||
childBox.x1 = box.x1;
|
||||
childBox.x2 = box.x2 - ((contentBox.x2 - contentBox.x1) - thumbnailWidth);
|
||||
} else {
|
||||
childBox.x1 = box.x1 + ((contentBox.x2 - contentBox.x1) - thumbnailWidth);
|
||||
childBox.x2 = box.x2;
|
||||
}
|
||||
childBox.y1 = box.y1;
|
||||
childBox.y2 = box.y2;
|
||||
this._background.allocate(childBox, flags);
|
||||
|
||||
let indicatorY = this._indicatorY;
|
||||
// when not animating, the workspace position overrides this._indicatorY
|
||||
let indicatorWorkspace = !this._animatingIndicator ? global.screen.get_active_workspace() : null;
|
||||
|
||||
let y = contentBox.y1;
|
||||
|
||||
for (let i = 0; i < this._thumbnails.length; i++) {
|
||||
let thumbnail = this._thumbnails[i];
|
||||
|
||||
if (i > 0)
|
||||
y += spacing - Math.round(thumbnail.collapseFraction * spacing);
|
||||
|
||||
// We might end up with thumbnailHeight being something like 99.33
|
||||
// pixels. To make this work and not end up with a gap at the bottom,
|
||||
// we need some thumbnails to be 99 pixels and some 100 pixels height;
|
||||
// we compute an actual scale separately for each thumbnail.
|
||||
let y1 = Math.round(y);
|
||||
let y2 = Math.round(y + thumbnailHeight);
|
||||
let roundedVScale = (y2 - y1) / portholeHeight;
|
||||
|
||||
let x1, x2;
|
||||
if (rtl) {
|
||||
x1 = contentBox.x1 + slideOffset * thumbnail.slidePosition;
|
||||
x2 = x1 + thumbnailWidth;
|
||||
} else {
|
||||
x1 = contentBox.x2 - thumbnailWidth + slideOffset * thumbnail.slidePosition;
|
||||
x2 = x1 + thumbnailWidth;
|
||||
}
|
||||
|
||||
if (thumbnail.metaWorkspace == indicatorWorkspace)
|
||||
indicatorY = y1;
|
||||
|
||||
// Allocating a scaled actor is funny - x1/y1 correspond to the origin
|
||||
// of the actor, but x2/y2 are increased by the *unscaled* size.
|
||||
childBox.x1 = x1;
|
||||
childBox.x2 = x1 + portholeWidth;
|
||||
childBox.y1 = y1;
|
||||
childBox.y2 = y1 + portholeHeight;
|
||||
|
||||
thumbnail.actor.set_scale(roundedHScale, roundedVScale);
|
||||
thumbnail.actor.allocate(childBox, flags);
|
||||
|
||||
// We round the collapsing portion so that we don't get thumbnails resizing
|
||||
// during an animation due to differences in rounded, but leave the uncollapsed
|
||||
// portion unrounded so that non-animating we end up with the right total
|
||||
y += thumbnailHeight - Math.round(thumbnailHeight * thumbnail.collapseFraction);
|
||||
}
|
||||
|
||||
if (rtl) {
|
||||
childBox.x1 = contentBox.x1;
|
||||
childBox.x2 = contentBox.x1 + thumbnailWidth;
|
||||
} else {
|
||||
childBox.x1 = contentBox.x2 - thumbnailWidth;
|
||||
childBox.x2 = contentBox.x2;
|
||||
}
|
||||
childBox.y1 = indicatorY;
|
||||
childBox.y2 = childBox.y1 + thumbnailHeight;
|
||||
this._indicator.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
_activeWorkspaceChanged: function(wm, from, to, direction) {
|
||||
let thumbnail;
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
for (let i = 0; i < this._thumbnails.length; i++) {
|
||||
if (this._thumbnails[i].metaWorkspace == activeWorkspace) {
|
||||
thumbnail = this._thumbnails[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this._animatingIndicator = true;
|
||||
this.indicatorY = this._indicator.allocation.y1;
|
||||
Tweener.addTween(this,
|
||||
{ indicatorY: thumbnail.actor.allocation.y1,
|
||||
time: WorkspacesView.WORKSPACE_SWITCH_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this._animatingIndicator = false;
|
||||
this._queueUpdateStates();
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
}
|
||||
};
|
@ -1,130 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const DND = imports.ui.dnd;
|
||||
|
||||
function XdndHandler() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
XdndHandler.prototype = {
|
||||
_init: function() {
|
||||
// Used to display a clone of the cursor window when the
|
||||
// window group is hidden (like it happens in the overview)
|
||||
this._cursorWindowClone = null;
|
||||
|
||||
// Used as a drag actor in case we don't have a cursor window clone
|
||||
this._dummy = new Clutter.Rectangle({ width: 1, height: 1, opacity: 0 });
|
||||
global.stage.add_actor(this._dummy);
|
||||
this._dummy.hide();
|
||||
|
||||
// Mutter delays the creation of the output window as long
|
||||
// as possible to avoid flicker. In case a plugin wants to
|
||||
// access it directly it has to connect to the stage's show
|
||||
// signal. (see comment in compositor.c:meta_compositor_manage_screen)
|
||||
global.stage.connect('show', function () {
|
||||
global.init_xdnd();
|
||||
return false;
|
||||
});
|
||||
|
||||
global.connect('xdnd-enter', Lang.bind(this, this._onEnter));
|
||||
global.connect('xdnd-position-changed', Lang.bind(this, this._onPositionChanged));
|
||||
global.connect('xdnd-leave', Lang.bind(this, this._onLeave));
|
||||
|
||||
this._windowGroupVisibilityHandlerId = 0;
|
||||
},
|
||||
|
||||
// Called when the user cancels the drag (i.e release the button)
|
||||
_onLeave: function() {
|
||||
if (this._windowGroupVisibilityHandlerId != 0) {
|
||||
global.window_group.disconnect(this._windowGroupVisibilityHandlerId);
|
||||
this._windowGroupVisibilityHandlerId = 0;
|
||||
}
|
||||
if (this._cursorWindowClone) {
|
||||
this._cursorWindowClone.destroy();
|
||||
this._cursorWindowClone = null;
|
||||
}
|
||||
|
||||
this.emit('drag-end');
|
||||
},
|
||||
|
||||
_onEnter: function() {
|
||||
this._windowGroupVisibilityHandlerId =
|
||||
global.window_group.connect('notify::visible',
|
||||
Lang.bind(this, this._onWindowGroupVisibilityChanged));
|
||||
|
||||
this.emit('drag-begin', global.get_current_time());
|
||||
},
|
||||
|
||||
_onWindowGroupVisibilityChanged: function() {
|
||||
if (!global.window_group.visible) {
|
||||
if (this._cursorWindowClone)
|
||||
return;
|
||||
|
||||
let windows = global.get_window_actors();
|
||||
let cursorWindow = windows[windows.length - 1];
|
||||
|
||||
// FIXME: more reliable way?
|
||||
if (!cursorWindow.is_override_redirect())
|
||||
return;
|
||||
|
||||
let constraint_position = new Clutter.BindConstraint({ coordinate : Clutter.BindCoordinate.POSITION,
|
||||
source: cursorWindow});
|
||||
|
||||
this._cursorWindowClone = new Clutter.Clone({ source: cursorWindow });
|
||||
global.overlay_group.add_actor(this._cursorWindowClone);
|
||||
Shell.util_set_hidden_from_pick(this._cursorWindowClone, true);
|
||||
|
||||
// Make sure that the clone has the same position as the source
|
||||
this._cursorWindowClone.add_constraint(constraint_position);
|
||||
} else {
|
||||
if (this._cursorWindowClone) {
|
||||
this._cursorWindowClone.destroy();
|
||||
this._cursorWindowClone = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onPositionChanged: function(obj, x, y) {
|
||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
|
||||
|
||||
// Make sure that the cursor window is on top
|
||||
if (this._cursorWindowClone)
|
||||
this._cursorWindowClone.raise_top();
|
||||
|
||||
let dragEvent = {
|
||||
x: x,
|
||||
y: y,
|
||||
dragActor: this._cursorWindowClone ? this._cursorWindowClone : this._dummy,
|
||||
source: this,
|
||||
targetActor: pickedActor
|
||||
};
|
||||
|
||||
for (let i = 0; i < DND.dragMonitors.length; i++) {
|
||||
let motionFunc = DND.dragMonitors[i].dragMotion;
|
||||
if (motionFunc) {
|
||||
let result = motionFunc(dragEvent);
|
||||
if (result != DND.DragMotionResult.CONTINUE)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (pickedActor) {
|
||||
if (pickedActor._delegate && pickedActor._delegate.handleDragOver) {
|
||||
let result = pickedActor._delegate.handleDragOver(this,
|
||||
dragEvent.dragActor,
|
||||
x,
|
||||
y,
|
||||
global.get_current_time());
|
||||
if (result != DND.DragMotionResult.CONTINUE)
|
||||
return;
|
||||
}
|
||||
pickedActor = pickedActor.get_parent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Signals.addSignalMethods(XdndHandler.prototype);
|
@ -1,7 +1,5 @@
|
||||
af
|
||||
ar
|
||||
bg
|
||||
bn_IN
|
||||
ca
|
||||
cs
|
||||
da
|
||||
@ -10,23 +8,17 @@ el
|
||||
en_GB
|
||||
es
|
||||
et
|
||||
fa
|
||||
fi
|
||||
fr
|
||||
ga
|
||||
gl
|
||||
gu
|
||||
he
|
||||
hi
|
||||
hu
|
||||
id
|
||||
it
|
||||
ja
|
||||
ko
|
||||
kn
|
||||
lt
|
||||
lv
|
||||
mr
|
||||
nb
|
||||
nl
|
||||
nn
|
||||
|
@ -1,40 +1,24 @@
|
||||
data/gnome-shell.desktop.in.in
|
||||
data/gnome-shell-clock-preferences.desktop.in.in
|
||||
data/org.gnome.shell.gschema.xml.in
|
||||
js/misc/util.js
|
||||
data/org.gnome.accessibility.magnifier.gschema.xml.in
|
||||
[type: gettext/glade]data/clock-preferences.ui
|
||||
js/ui/appDisplay.js
|
||||
js/ui/appFavorites.js
|
||||
js/ui/calendar.js
|
||||
js/ui/dash.js
|
||||
js/ui/dateMenu.js
|
||||
js/ui/docDisplay.js
|
||||
js/ui/endSessionDialog.js
|
||||
js/ui/lookingGlass.js
|
||||
js/ui/messageTray.js
|
||||
js/ui/overview.js
|
||||
js/ui/panel.js
|
||||
js/ui/placeDisplay.js
|
||||
js/ui/polkitAuthenticationAgent.js
|
||||
js/ui/popupMenu.js
|
||||
js/ui/runDialog.js
|
||||
js/ui/searchDisplay.js
|
||||
js/ui/statusMenu.js
|
||||
js/ui/status/accessibility.js
|
||||
js/ui/status/bluetooth.js
|
||||
js/ui/status/keyboard.js
|
||||
js/ui/status/network.js
|
||||
js/ui/status/power.js
|
||||
js/ui/status/volume.js
|
||||
js/ui/telepathyClient.js
|
||||
js/ui/viewSelector.js
|
||||
js/ui/windowAttentionHandler.js
|
||||
js/ui/workspacesView.js
|
||||
src/gvc/gvc-mixer-control.c
|
||||
src/gdmuser/gdm-user.c
|
||||
src/main.c
|
||||
src/shell-app.c
|
||||
src/shell-app-system.c
|
||||
src/shell-global.c
|
||||
src/shell-mobile-providers.c
|
||||
src/shell-polkit-authentication-agent.c
|
||||
src/shell-util.c
|
||||
|
||||
src/shell-uri-util.c
|
||||
|