Compare commits
175 Commits
overlay-de
...
2.27.0
Author | SHA1 | Date | |
---|---|---|---|
b0c7dac56b | |||
e5aa67421d | |||
fdd50f09dd | |||
26a074588b | |||
00cc32d95a | |||
df8f727bba | |||
de8084ed40 | |||
9fa88caded | |||
06d17e08c0 | |||
4830808d2f | |||
02ee6f69b3 | |||
5d0808e1c0 | |||
1f864fba7f | |||
cc83aee401 | |||
185ccecec1 | |||
f96193dc8c | |||
205c57d6af | |||
5c97d21889 | |||
e323593f4a | |||
3bae3fa203 | |||
9bd22dc033 | |||
5064d873bb | |||
4495f98dce | |||
5a75b44f71 | |||
ebd6f4bc8f | |||
687a87d3d0 | |||
464842ea36 | |||
91911da302 | |||
3c87d76741 | |||
ccafa53bd6 | |||
4e23f4cfc9 | |||
544a80fc6e | |||
4d52c10958 | |||
c23b9ee192 | |||
296e0c2054 | |||
5527fa2bc3 | |||
59532ab0c7 | |||
ad5a9d8f8b | |||
d243634602 | |||
e89d3c7b07 | |||
354112fb41 | |||
f7746ec3f6 | |||
03e0fe1e95 | |||
22c98e6240 | |||
90979faedd | |||
21858d928a | |||
be95ca553a | |||
c4b4248707 | |||
91353c6d60 | |||
31851cbc32 | |||
41f6e8ef86 | |||
85b4b97b7b | |||
2726fdb831 | |||
b1150eb147 | |||
898d76af53 | |||
7c26303b25 | |||
902956ca0d | |||
3429abff40 | |||
61b28c5c7d | |||
e84e842c1e | |||
0792f8d4a3 | |||
52ae75d4b8 | |||
224538c885 | |||
45dc34bfa2 | |||
0b801a0d2d | |||
d7f5fd5d24 | |||
026f014d32 | |||
a439a58f13 | |||
29ffa46d08 | |||
0a1aac862f | |||
f7fff83647 | |||
66e48da7cb | |||
93ea4b07c1 | |||
77c92d75d5 | |||
119516424d | |||
f3efbf432f | |||
04538c65b5 | |||
efcf8bae9d | |||
adfb419ceb | |||
09948ce033 | |||
ff6ee2c0c2 | |||
96cf9c739e | |||
9f4ccb83e3 | |||
978ab8a4dd | |||
2cc650e389 | |||
f24169735a | |||
75c875f073 | |||
6ea2822fa3 | |||
b4cf178cc5 | |||
a7c1ff3729 | |||
fdd9b85448 | |||
e12587619b | |||
8d9fc28872 | |||
2c5d520395 | |||
9e85d197fd | |||
2aea9c59a4 | |||
001af72727 | |||
9d594d7f26 | |||
2161e90cda | |||
f7a82d6400 | |||
10e30f7dc7 | |||
177edc5444 | |||
b45cd0a4eb | |||
8a56b990dd | |||
f353283a40 | |||
5d067ec718 | |||
1cb6fc004b | |||
3b603ef7e0 | |||
0971ba54b8 | |||
c136acc879 | |||
cc2d3fd56d | |||
72e6e7839f | |||
732573331a | |||
00407d6971 | |||
db630b2945 | |||
a15ee28177 | |||
92e9bc85a1 | |||
70c51beeeb | |||
9890887126 | |||
203ec385c5 | |||
5a0d8eca9f | |||
ae779c7f20 | |||
3852176e80 | |||
7a0ce6c57b | |||
15e862f974 | |||
a1908d9db1 | |||
8ee740e425 | |||
f44b3e0553 | |||
bddcb40237 | |||
66ea19fbfb | |||
e9966b4aff | |||
a71ae65f8b | |||
96a2747e0b | |||
8ef48ca33c | |||
8a0cebccdc | |||
5ae6239344 | |||
aaeb980688 | |||
77c325772a | |||
2eb0d20221 | |||
454ca09575 | |||
8f0bf5deae | |||
09d9d91297 | |||
a5e3227b64 | |||
8a790e1c38 | |||
db52e024e8 | |||
79c166c38d | |||
04fbaf4f27 | |||
b92263e80c | |||
12f78a08cd | |||
88c9a23866 | |||
94f92072c2 | |||
5ca4b41063 | |||
45c97862e5 | |||
3a7447dacc | |||
dc7b2b03e1 | |||
8b15724c2a | |||
587f04f807 | |||
9faf161aae | |||
3b56807e78 | |||
d5882c0cd3 | |||
c92268a615 | |||
e1fa61cd58 | |||
98c5f35324 | |||
3841e8da74 | |||
e3291aa5ba | |||
fab13dbb89 | |||
c0c79e59fc | |||
90b7d9a7fa | |||
662c995515 | |||
3c54893656 | |||
580794f3fb | |||
1154a1e8d7 | |||
837683004d | |||
14bb73220b | |||
f1a9ada5f0 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -16,6 +16,8 @@ config.log
|
||||
config.status
|
||||
config
|
||||
configure
|
||||
data/gnome-shell.desktop
|
||||
data/gnome-shell.desktop.in
|
||||
libtool
|
||||
scripts/launcher.pyc
|
||||
src/*.gir
|
||||
|
@ -7,7 +7,7 @@ EXTRA_DIST = \
|
||||
distcheck-hook:
|
||||
@echo "Checking disted javascript against files in git"
|
||||
@failed=false; \
|
||||
for f in `cd $(srcdir) && git-ls-files js` ; do \
|
||||
for f in `cd $(srcdir) && git ls-files js` ; do \
|
||||
if ! test -e $(distdir)/$$f ; then \
|
||||
echo File missing from distribution: $$f ; \
|
||||
failed=true ; \
|
||||
|
48
configure.ac
48
configure.ac
@ -1,10 +1,12 @@
|
||||
AC_INIT(gnome-shell, 0.0.1)
|
||||
AC_INIT(gnome-shell, 2.27.0)
|
||||
|
||||
AC_CONFIG_AUX_DIR(config)
|
||||
|
||||
AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip])
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],)
|
||||
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
|
||||
AC_DISABLE_STATIC
|
||||
@ -20,6 +22,7 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
||||
|
||||
PKG_PROG_PKG_CONFIG(0.16)
|
||||
|
||||
AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
|
||||
AM_GCONF_SOURCE_2
|
||||
|
||||
# We need at least this, since gst_plugin_register_static() was added
|
||||
@ -33,19 +36,18 @@ 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 xfixes"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-0.9)
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
|
||||
PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 dbus-glib-1 mutter-plugins gjs-gi-1.0 xscrnsaver libgnome-menu $recorder_modules gconf-2.0 gdk-x11-2.0 clutter-x11-0.9 clutter-glx-0.9)
|
||||
PKG_CHECK_MODULES(TIDY, clutter-0.9)
|
||||
PKG_CHECK_MODULES(BIG, clutter-0.9 gtk+-2.0 librsvg-2.0)
|
||||
PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugins gjs-gi-1.0 libgnome-menu $recorder_modules gconf-2.0 gdk-x11-2.0 clutter-x11-1.0 clutter-glx-1.0)
|
||||
PKG_CHECK_MODULES(TIDY, clutter-1.0)
|
||||
PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
|
||||
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
|
||||
PKG_CHECK_MODULES(TASKPANEL, libwnck-1.0 dbus-glib-1)
|
||||
# We require libgnomeui for generating thumbnails for recent files with GnomeThumbnailFactory.
|
||||
# We'll switch to using GnomeDesktopThumbnailFactory once the branch of gnome-desktop that contains
|
||||
# it becomes stable.
|
||||
@ -80,17 +82,31 @@ AC_SUBST(GIRDIR)
|
||||
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
|
||||
AC_SUBST(TYPELIBDIR)
|
||||
|
||||
changequote(,)dnl
|
||||
if test "x$GCC" = "xyes"; then
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Wall[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Wall" ;;
|
||||
esac
|
||||
# Stay command-line compatible with the gnome-common configure option. Here
|
||||
# minimum/yes/maximum are the same, however.
|
||||
AC_ARG_ENABLE(compile_warnings,
|
||||
AC_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],
|
||||
[Turn on compiler warnings]),,
|
||||
enable_compile_warnings=error)
|
||||
|
||||
case " $OBJCFLAGS " in
|
||||
*[\ \ ]-Wall[\ \ ]*) ;;
|
||||
*) OBJCFLAGS="$OBJCFLAGS -Wall" ;;
|
||||
esac
|
||||
changequote(,)dnl
|
||||
if test "$enable_compile_warnings" != no ; then
|
||||
if test "x$GCC" = "xyes"; then
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Wall[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Wall" ;;
|
||||
esac
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
|
||||
esac
|
||||
if test "$enable_compile_warnings" = error ; then
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Werror[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Werror" ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
changequote([,])dnl
|
||||
|
||||
|
@ -1,14 +1,38 @@
|
||||
desktopdir=$(datadir)/applications
|
||||
desktop_DATA = gnome-shell.desktop
|
||||
|
||||
# We substitute in bindir so it works as an autostart
|
||||
# file when built in a non-system prefix
|
||||
gnome-shell.desktop.in: gnome-shell.desktop.in.in
|
||||
$(AM_V_GEN) sed -e "s|@bindir[@]|$(bindir)|" \
|
||||
-e "s|@VERSION[@]|$(VERSION)|" \
|
||||
$< > $@ || rm $@
|
||||
|
||||
# Placeholder until we add intltool
|
||||
gnome-shell.desktop: gnome-shell.desktop.in
|
||||
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
|
||||
|
||||
imagedir = $(pkgdatadir)/images
|
||||
|
||||
dist_image_DATA = \
|
||||
add-workspace.svg \
|
||||
remove-workspace.svg
|
||||
close.svg \
|
||||
info.svg \
|
||||
remove-workspace.svg \
|
||||
view-more-activated.svg \
|
||||
view-more.svg
|
||||
|
||||
schemadir = @GCONF_SCHEMA_FILE_DIR@
|
||||
schema_DATA = gnome-shell.schemas
|
||||
|
||||
install-data-local:
|
||||
GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(top_builddir)/data/$(schema_DATA)
|
||||
GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(srcdir)/$(schema_DATA)
|
||||
|
||||
EXTRA_DIST = \
|
||||
EXTRA_DIST = \
|
||||
gnome-shell.desktop.in.in \
|
||||
$(schema_DATA)
|
||||
|
||||
CLEANFILES = \
|
||||
gnome-shell.desktop.in \
|
||||
$(desktop_DATA)
|
||||
|
||||
|
74
data/close.svg
Normal file
74
data/close.svg
Normal file
@ -0,0 +1,74 @@
|
||||
<?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.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"
|
||||
sodipodi:docname="x_circle_16.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" /></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" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3173"
|
||||
id="linearGradient3179"
|
||||
x1="7.844358"
|
||||
y1="16"
|
||||
x2="7.7198443"
|
||||
y2="-0.062256809"
|
||||
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
|
||||
inkscape:window-height="713"
|
||||
inkscape:window-width="1197"
|
||||
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="8"
|
||||
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:url(#linearGradient3179)" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
15
data/gnome-shell.desktop.in.in
Normal file
15
data/gnome-shell.desktop.in.in
Normal file
@ -0,0 +1,15 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
_Name=GNOME Shell
|
||||
_Comment=Window management and application launching
|
||||
Exec=@bindir@/gnome-shell
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
X-GNOME-Bugzilla-Component=general
|
||||
X-GNOME-Bugzilla-Version=@VERSION@
|
||||
Categories=GNOME;GTK;Utility;Core;
|
||||
OnlyShowIn=GNOME;
|
||||
NoDisplay=true
|
||||
X-GNOME-Autostart-Phase=WindowManager
|
||||
X-GNOME-Provides=panel;windowmanager;
|
||||
X-GNOME-Autostart-Notify=true
|
@ -15,6 +15,64 @@
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
<schema>
|
||||
<key>/schemas/desktop/gnome/shell/favorite_apps</key>
|
||||
<applyto>/desktop/gnome/shell/favorite_apps</applyto>
|
||||
<owner>gnome-shell</owner>
|
||||
<type>list</type>
|
||||
<list_type>string</list_type>
|
||||
<default>[mozilla-firefox.desktop,evolution.desktop,openoffice.org-writer.desktop]</default>
|
||||
<locale name="C">
|
||||
<short>List of desktop file IDs for favorite applications</short>
|
||||
<long>
|
||||
The applications corresponding to these identifiers will be displayed in the favorites area.
|
||||
</long>
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
<schema>
|
||||
<key>/schemas/desktop/gnome/shell/sidebar/visible</key>
|
||||
<applyto>/desktop/gnome/shell/sidebar/visible</applyto>
|
||||
<owner>gnome-shell</owner>
|
||||
<type>bool</type>
|
||||
<default>true</default>
|
||||
<locale name="C">
|
||||
<short>Whether or not to display the sidebar</short>
|
||||
<long>
|
||||
Determines whether or not the sidebar is visible.
|
||||
</long>
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
<schema>
|
||||
<key>/schemas/desktop/gnome/shell/sidebar/expanded</key>
|
||||
<applyto>/desktop/gnome/shell/sidebar/expanded</applyto>
|
||||
<owner>gnome-shell</owner>
|
||||
<type>bool</type>
|
||||
<default>true</default>
|
||||
<locale name="C">
|
||||
<short>Whether the sidebar should be in the expanded (wide) mode</short>
|
||||
<long>
|
||||
Controls the expanded/collapsed state of the sidebar.
|
||||
</long>
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
<schema>
|
||||
<key>/schemas/desktop/gnome/shell/sidebar/widgets</key>
|
||||
<applyto>/desktop/gnome/shell/sidebar/widgets</applyto>
|
||||
<owner>gnome-shell</owner>
|
||||
<type>list</type>
|
||||
<list_type>string</list_type>
|
||||
<default>[imports.ui.widget.ClockWidget,imports.ui.widget.AppsWidget,imports.ui.widget.RecentDocsWidget]</default>
|
||||
<locale name="C">
|
||||
<short>The widgets to display in the sidebar</short>
|
||||
<long>
|
||||
The widgets to display in the sidebar, in order from top to bottom. Each widget "name" is actually a JavaScript expression referring to a widget constructor object.
|
||||
</long>
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
</schemalist>
|
||||
|
||||
</gconfschemafile>
|
||||
</gconfschemafile>
|
||||
|
74
data/info.svg
Normal file
74
data/info.svg
Normal file
@ -0,0 +1,74 @@
|
||||
<?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.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"
|
||||
sodipodi:docname="info_16.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
||||
id="metadata2389"><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><defs
|
||||
id="defs2387"><linearGradient
|
||||
id="linearGradient3710"><stop
|
||||
style="stop-color:#c4c4c4;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3712" /><stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3714" /></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="perspective2391" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3710"
|
||||
id="linearGradient3716"
|
||||
x1="7.9066148"
|
||||
y1="15.937743"
|
||||
x2="7.9377432"
|
||||
y2="0.031128405"
|
||||
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
|
||||
inkscape:window-height="713"
|
||||
inkscape:window-width="722"
|
||||
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="8.154146"
|
||||
inkscape:window-x="20"
|
||||
inkscape:window-y="20"
|
||||
inkscape:current-layer="Foreground" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M7,3h2v2H7V3z M5.5,12H7V8H5.5V7H9v5h1.5v1h-5V12z M0,8c0-4.418,3.582-8,8-8 s8,3.582,8,8s-3.582,8-8,8S0,12.418,0,8z"
|
||||
id="path2384"
|
||||
style="fill-opacity:1;fill:url(#linearGradient3716)" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
66
data/view-more-activated.svg
Normal file
66
data/view-more-activated.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="29px"
|
||||
height="18px"
|
||||
viewBox="0 0 29 18"
|
||||
enable-background="new 0 0 29 18"
|
||||
xml:space="preserve"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.46"
|
||||
sodipodi:docname="search_2.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" /></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="0.0"
|
||||
guidetolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
objecttolerance="10.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
id="base"
|
||||
showgrid="false"
|
||||
inkscape:zoom="19.275862"
|
||||
inkscape:cx="14.5"
|
||||
inkscape:cy="9"
|
||||
inkscape:window-x="40"
|
||||
inkscape:window-y="40"
|
||||
inkscape:current-layer="Foreground"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2391" /></sodipodi:namedview>
|
||||
|
||||
<path
|
||||
style="fill:#4669a9;fill-opacity:1"
|
||||
id="path9"
|
||||
d="" />
|
||||
<path
|
||||
id="path3"
|
||||
style="fill:#3d5a93;fill-opacity:1"
|
||||
d="M 0,3 C 0,1.343 1.343,0 3,0 L 20,0 C 20.515,0 21.027,0.195 21.42,0.588 L 28.412,7.58 C 29.196,8.364 29.196,9.636 28.412,10.42 L 21.42,17.412 C 21.028,17.804 20.514,18 20,18 L 3,18 C 1.343,18 0,16.657 0,15 L 0,3 zM 13.5,3 C 11.015,3 9,5.015 9,7.5 C 9,8.2423219 9.1815696,8.9452421 9.5,9.5625 L 6.25,12.8125 C 5.931,13.1325 5.9310002,13.64975 6.25,13.96875 L 7.03125,14.78125 C 7.35025,15.10025 7.8674999,15.10025 8.1875,14.78125 L 11.46875,11.5 C 12.080227,11.810879 12.767137,12 13.5,12 C 15.985,12 18,9.985 18,7.5 C 18,5.015 15.985,3 13.5,3 z M 11.25,7.5 C 11.25,6.257 12.257,5.25 13.5,5.25 C 14.743,5.25 15.75,6.257 15.75,7.5 C 15.75,8.743 14.743,9.75 13.5,9.75 C 12.257,9.75 11.25,8.743 11.25,7.5 z" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
79
data/view-more.svg
Normal file
79
data/view-more.svg
Normal file
@ -0,0 +1,79 @@
|
||||
<?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="29px"
|
||||
height="18px"
|
||||
viewBox="0 0 29 18"
|
||||
enable-background="new 0 0 29 18"
|
||||
xml:space="preserve"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.46"
|
||||
sodipodi:docname="search_1.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" /></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="0.0"
|
||||
guidetolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
objecttolerance="10.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
id="base"
|
||||
showgrid="false"
|
||||
inkscape:zoom="19.275862"
|
||||
inkscape:cx="14.5"
|
||||
inkscape:cy="9"
|
||||
inkscape:window-x="40"
|
||||
inkscape:window-y="40"
|
||||
inkscape:current-layer="Foreground"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2391" /></sodipodi:namedview>
|
||||
<path
|
||||
d="M0,3c0-1.657,1.343-3,3-3h17c0.515,0,1.027,0.195,1.42,0.588l6.992,6.992c0.784,0.784,0.784,2.056,0,2.84l-6.992,6.992 C21.028,17.804,20.514,18,20,18H3c-1.657,0-3-1.343-3-3V3z"
|
||||
id="path3"
|
||||
style="fill:#151e2f;fill-opacity:1" />
|
||||
<g
|
||||
id="g5"
|
||||
style="fill:#4669a9;fill-opacity:1">
|
||||
<path
|
||||
fill="#FFFFFF"
|
||||
d="M6.246,13.98c-0.319-0.319-0.319-0.837,0-1.157l3.717-3.717c0.319-0.319,0.837-0.319,1.157,0l0.786,0.787 c0.32,0.319,0.32,0.837,0,1.157l-3.717,3.717c-0.32,0.319-0.838,0.319-1.157,0L6.246,13.98L6.246,13.98z"
|
||||
id="path7"
|
||||
style="fill:#4669a9;fill-opacity:1" />
|
||||
<path
|
||||
fill="#FFFFFF"
|
||||
d="M9.076,11.937"
|
||||
id="path9"
|
||||
style="fill:#4669a9;fill-opacity:1" />
|
||||
</g>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#FFFFFF"
|
||||
d="M11.25,7.5c0-1.243,1.007-2.25,2.25-2.25s2.25,1.007,2.25,2.25 s-1.007,2.25-2.25,2.25S11.25,8.743,11.25,7.5z M9,7.5C9,5.015,11.015,3,13.5,3S18,5.015,18,7.5S15.985,12,13.5,12S9,9.985,9,7.5z"
|
||||
id="path11"
|
||||
style="fill:#4669a9;fill-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
@ -1,5 +1,4 @@
|
||||
jsmiscdir = $(pkgdatadir)/js/misc
|
||||
|
||||
dist_jsmisc_DATA = \
|
||||
appInfo.js \
|
||||
docInfo.js
|
||||
|
@ -1,135 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
|
||||
// TODO - move this into GConf once we're not a plugin anymore
|
||||
// but have taken over metacity
|
||||
// This list is taken from GNOME Online popular applications
|
||||
// http://online.gnome.org/applications
|
||||
// but with nautilus removed (since it should already be running)
|
||||
// and evince, totem, and gnome-file-roller removed (since they're
|
||||
// usually started by opening documents, not by opening the app
|
||||
// directly)
|
||||
const DEFAULT_APPLICATIONS = [
|
||||
'mozilla-firefox.desktop',
|
||||
'gnome-terminal.desktop',
|
||||
'evolution.desktop',
|
||||
'gedit.desktop',
|
||||
'mozilla-thunderbird.desktop',
|
||||
'rhythmbox.desktop',
|
||||
'epiphany.desktop',
|
||||
'xchat.desktop',
|
||||
'openoffice.org-1.9-writer.desktop',
|
||||
'emacs.desktop',
|
||||
'gnome-system-monitor.desktop',
|
||||
'openoffice.org-1.9-calc.desktop',
|
||||
'eclipse.desktop',
|
||||
'openoffice.org-1.9-impress.desktop',
|
||||
'vncviewer.desktop'
|
||||
];
|
||||
|
||||
function AppInfo(appId) {
|
||||
this._init(appId);
|
||||
}
|
||||
|
||||
AppInfo.prototype = {
|
||||
_init : function(appId) {
|
||||
this.appId = appId;
|
||||
this._gAppInfo = Gio.DesktopAppInfo.new(appId);
|
||||
if (!this._gAppInfo)
|
||||
throw new Error('Unknown appId ' + appId);
|
||||
|
||||
this.id = this._gAppInfo.get_id();
|
||||
this.name = this._gAppInfo.get_name();
|
||||
this.description = this._gAppInfo.get_description();
|
||||
this.executable = this._gAppInfo.get_executable();
|
||||
|
||||
this._gicon = this._gAppInfo.get_icon();
|
||||
},
|
||||
|
||||
getIcon : function(size) {
|
||||
if (this._gicon)
|
||||
return Shell.TextureCache.get_default().load_gicon(this._gicon, size);
|
||||
else
|
||||
return new Clutter.Texture({ width: size, height: size });
|
||||
},
|
||||
|
||||
getIconPath : function(size) {
|
||||
if (this._gicon) {
|
||||
let iconTheme = Gtk.IconTheme.get_default();
|
||||
let previewIconInfo = iconTheme.lookup_by_gicon(this._gicon, size, 0);
|
||||
if (previewIconInfo)
|
||||
return previewIconInfo.get_filename();
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
launch : function() {
|
||||
this._gAppInfo.launch([], Main.createAppLaunchContext());
|
||||
}
|
||||
};
|
||||
|
||||
var _infos = {};
|
||||
|
||||
// getAppInfo:
|
||||
// @appId: an appId
|
||||
//
|
||||
// Gets an #AppInfo for @appId. This is preferable to calling
|
||||
// new AppInfo() directly, because it caches #AppInfos.
|
||||
//
|
||||
// Return value: the new or cached #AppInfo, or %null if @appId
|
||||
// doesn't point to a valid .desktop file
|
||||
function getAppInfo(appId) {
|
||||
let info = _infos[appId];
|
||||
if (info === undefined) {
|
||||
try {
|
||||
info = _infos[appId] = new AppInfo(appId);
|
||||
} catch (e) {
|
||||
info = _infos[appId] = null;
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
// getMostUsedApps:
|
||||
// @count: maximum number of apps to retrieve
|
||||
//
|
||||
// Gets a list of #AppInfos for the @count most-frequently-used
|
||||
// applications
|
||||
//
|
||||
// Return value: the list of #AppInfo
|
||||
function getMostUsedApps(count) {
|
||||
let appMonitor = Shell.AppMonitor.get_default();
|
||||
|
||||
// Ask for more apps than we need, since the list of recently used
|
||||
// apps might contain an app we don't have a desktop file for
|
||||
let apps = appMonitor.get_most_used_apps (0, Math.round(count * 1.5));
|
||||
let matches = [], alreadyAdded = {};
|
||||
|
||||
for (let i = 0; i < apps.length && matches.length <= count; i++) {
|
||||
let appId = apps[i] + ".desktop";
|
||||
let appInfo = getAppInfo(appId);
|
||||
if (appInfo) {
|
||||
matches.push(appInfo);
|
||||
alreadyAdded[appId] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the list with default applications it's not full yet
|
||||
for (let i = 0; i < DEFAULT_APPLICATIONS.length && matches.length <= count; i++) {
|
||||
let appId = DEFAULT_APPLICATIONS[i];
|
||||
if (alreadyAdded[appId])
|
||||
continue;
|
||||
|
||||
let appInfo = getAppInfo(appId);
|
||||
if (appInfo)
|
||||
matches.push(appInfo);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
@ -5,6 +5,8 @@ const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const THUMBNAIL_ICON_MARGIN = 2;
|
||||
@ -16,40 +18,17 @@ function DocInfo(recentInfo) {
|
||||
DocInfo.prototype = {
|
||||
_init : function(recentInfo) {
|
||||
this._recentInfo = recentInfo;
|
||||
// We actually used get_modified() instead of get_visited()
|
||||
// here, as GtkRecentInfo doesn't updated get_visited()
|
||||
// correctly. See http://bugzilla.gnome.org/show_bug.cgi?id=567094
|
||||
this.timestamp = recentInfo.get_modified().getTime() / 1000;
|
||||
this.name = recentInfo.get_display_name();
|
||||
this.uri = recentInfo.get_uri();
|
||||
this.mimeType = recentInfo.get_mime_type();
|
||||
},
|
||||
|
||||
getIcon : function(size) {
|
||||
let icon = new Clutter.Texture();
|
||||
let iconPixbuf;
|
||||
|
||||
if (this.uri.match("^file://"))
|
||||
iconPixbuf = Shell.get_thumbnail(this.uri, this.mimeType);
|
||||
|
||||
if (iconPixbuf) {
|
||||
// We calculate the width and height of the texture so as
|
||||
// to preserve the aspect ratio of the thumbnail. Because
|
||||
// the images generated based on thumbnails don't have an
|
||||
// internal padding like system icons do, we create a
|
||||
// slightly smaller texture and then create a group around
|
||||
// it for padding purposes
|
||||
|
||||
let scalingFactor = (size - THUMBNAIL_ICON_MARGIN * 2) / Math.max(iconPixbuf.get_width(), iconPixbuf.get_height());
|
||||
icon.set_width(Math.ceil(iconPixbuf.get_width() * scalingFactor));
|
||||
icon.set_height(Math.ceil(iconPixbuf.get_height() * scalingFactor));
|
||||
Shell.clutter_texture_set_from_pixbuf(icon, iconPixbuf);
|
||||
|
||||
let group = new Clutter.Group({ width: size,
|
||||
height: size });
|
||||
group.add_actor(icon);
|
||||
icon.set_position(THUMBNAIL_ICON_MARGIN, THUMBNAIL_ICON_MARGIN);
|
||||
return group;
|
||||
} else {
|
||||
Shell.clutter_texture_set_from_pixbuf(icon, this._recentInfo.get_icon(size));
|
||||
return icon;
|
||||
}
|
||||
createIcon : function(size) {
|
||||
return Shell.TextureCache.get_default().load_recent_thumbnail(size, this._recentInfo);
|
||||
},
|
||||
|
||||
launch : function() {
|
||||
@ -63,7 +42,7 @@ DocInfo.prototype = {
|
||||
if (appInfo != null) {
|
||||
appInfo.launch_uris([this.uri], Main.createAppLaunchContext());
|
||||
} else {
|
||||
log("Failed to get default application info for mime type " + mimeType +
|
||||
log("Failed to get default application info for mime type " + this.mimeType +
|
||||
". Will try to use the last application that registered the document.");
|
||||
let appName = this._recentInfo.last_application();
|
||||
let [success, appExec, count, time] = this._recentInfo.get_application_info(appName);
|
||||
@ -101,14 +80,60 @@ DocInfo.prototype = {
|
||||
|
||||
exists : function() {
|
||||
return this._recentInfo.exists();
|
||||
},
|
||||
|
||||
lastVisited : function() {
|
||||
// We actually used get_modified() instead of get_visited()
|
||||
// here, as GtkRecentInfo doesn't updated get_visited()
|
||||
// correctly. See
|
||||
// http://bugzilla.gnome.org/show_bug.cgi?id=567094
|
||||
|
||||
return this._recentInfo.get_modified();
|
||||
}
|
||||
};
|
||||
|
||||
var docManagerInstance = null;
|
||||
|
||||
function getDocManager(size) {
|
||||
if (docManagerInstance == null)
|
||||
docManagerInstance = new DocManager(size);
|
||||
return docManagerInstance;
|
||||
}
|
||||
|
||||
function DocManager(size) {
|
||||
this._init(size);
|
||||
}
|
||||
|
||||
DocManager.prototype = {
|
||||
_init: function(iconSize) {
|
||||
this._iconSize = iconSize;
|
||||
this._recentManager = Gtk.RecentManager.get_default();
|
||||
this._items = {};
|
||||
this._recentManager.connect('changed', Lang.bind(this, function(recentManager) {
|
||||
this._reload();
|
||||
this.emit('changed');
|
||||
}));
|
||||
this._reload();
|
||||
},
|
||||
|
||||
_reload: function() {
|
||||
let docs = this._recentManager.get_items();
|
||||
let newItems = {};
|
||||
for (let i = 0; i < docs.length; i++) {
|
||||
let recentInfo = docs[i];
|
||||
let docInfo = new DocInfo(recentInfo);
|
||||
|
||||
// we use GtkRecentInfo URI as an item Id
|
||||
newItems[docInfo.uri] = docInfo;
|
||||
}
|
||||
let deleted = {};
|
||||
for (var uri in this._items) {
|
||||
if (!(uri in newItems))
|
||||
deleted[uri] = this._items[uri];
|
||||
}
|
||||
/* If we'd cached any thumbnail references that no longer exist,
|
||||
dump them here */
|
||||
let texCache = Shell.TextureCache.get_default();
|
||||
for (var uri in deleted) {
|
||||
texCache.evict_recent_thumbnail(this._iconSize, this._items[uri]);
|
||||
}
|
||||
this._items = newItems;
|
||||
},
|
||||
|
||||
getItems: function() {
|
||||
return this._items;
|
||||
}
|
||||
}
|
||||
|
||||
Signals.addSignalMethods(DocManager.prototype);
|
||||
|
@ -5,13 +5,16 @@ dist_jsui_DATA = \
|
||||
appDisplay.js \
|
||||
button.js \
|
||||
chrome.js \
|
||||
dash.js \
|
||||
dnd.js \
|
||||
docDisplay.js \
|
||||
genericDisplay.js \
|
||||
link.js \
|
||||
lookingGlass.js \
|
||||
main.js \
|
||||
overlay.js \
|
||||
panel.js \
|
||||
places.js \
|
||||
runDialog.js \
|
||||
sidebar.js \
|
||||
tweener.js \
|
||||
|
@ -36,9 +36,9 @@ function AltTabPopup() {
|
||||
|
||||
AltTabPopup.prototype = {
|
||||
_init : function() {
|
||||
let global = Shell.Global.get();
|
||||
let global = Shell.Global.get();
|
||||
|
||||
this.actor = new Big.Box({ background_color : POPUP_BG_COLOR,
|
||||
this.actor = new Big.Box({ background_color : POPUP_BG_COLOR,
|
||||
corner_radius: POPUP_GRID_SPACING,
|
||||
padding: POPUP_GRID_SPACING,
|
||||
spacing: POPUP_GRID_SPACING,
|
||||
@ -48,26 +48,26 @@ AltTabPopup.prototype = {
|
||||
// but Tidy.Grid is lame in various ways. (Eg, it seems to
|
||||
// have a minimum size of 200x200.) So we create a vertical
|
||||
// Big.Box containing multiple horizontal Big.Boxes.
|
||||
this._grid = new Big.Box({ spacing: POPUP_GRID_SPACING,
|
||||
this._grid = new Big.Box({ spacing: POPUP_GRID_SPACING,
|
||||
orientation: Big.BoxOrientation.VERTICAL });
|
||||
let gcenterbox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
x_align: Big.BoxAlignment.CENTER });
|
||||
gcenterbox.append(this._grid, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(gcenterbox, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(gcenterbox, Big.BoxPackFlags.NONE);
|
||||
|
||||
// Selected-window label
|
||||
this._label = new Clutter.Text({ font_name: "Sans 16px",
|
||||
this._label = new Clutter.Text({ font_name: "Sans 16px",
|
||||
ellipsize: Pango.EllipsizeMode.END });
|
||||
|
||||
let labelbox = new Big.Box({ background_color: POPUP_INDICATOR_COLOR,
|
||||
corner_radius: POPUP_GRID_SPACING / 2,
|
||||
padding: POPUP_GRID_SPACING / 2 });
|
||||
labelbox.append(this._label, Big.BoxPackFlags.EXPAND);
|
||||
labelbox.append(this._label, Big.BoxPackFlags.NONE);
|
||||
let lcenterbox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
x_align: Big.BoxAlignment.CENTER,
|
||||
width: POPUP_LABEL_MAX_WIDTH + POPUP_GRID_SPACING });
|
||||
lcenterbox.append(labelbox, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(lcenterbox, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(lcenterbox, Big.BoxPackFlags.NONE);
|
||||
|
||||
// Indicator around selected icon
|
||||
this._indicator = new Big.Rectangle({ border_width: POPUP_INDICATOR_WIDTH,
|
||||
@ -76,10 +76,10 @@ AltTabPopup.prototype = {
|
||||
color: POPUP_TRANSPARENT });
|
||||
this.actor.append(this._indicator, Big.BoxPackFlags.FIXED);
|
||||
|
||||
this._items = [];
|
||||
this._items = [];
|
||||
this._toplevels = global.window_group.get_children();
|
||||
|
||||
global.stage.add_actor(this.actor);
|
||||
global.stage.add_actor(this.actor);
|
||||
|
||||
// Dark translucent window used to cover all but the
|
||||
// currently-selected window while Alt-Tabbing. Actually
|
||||
@ -142,7 +142,7 @@ AltTabPopup.prototype = {
|
||||
},
|
||||
|
||||
show : function(initialSelection) {
|
||||
let global = Shell.Global.get();
|
||||
let global = Shell.Global.get();
|
||||
|
||||
Main.startModal();
|
||||
|
||||
@ -154,15 +154,15 @@ AltTabPopup.prototype = {
|
||||
time: SHOW_TIME,
|
||||
transition: "easeOutQuad" });
|
||||
|
||||
this.actor.show_all();
|
||||
this.actor.x = (global.screen_width - this.actor.width) / 2;
|
||||
this.actor.y = (global.screen_height - this.actor.height) / 2;
|
||||
this.actor.show_all();
|
||||
this.actor.x = Math.floor((global.screen_width - this.actor.width) / 2);
|
||||
this.actor.y = Math.floor((global.screen_height - this.actor.height) / 2);
|
||||
|
||||
this.select(initialSelection);
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
this.actor.destroy();
|
||||
this.actor.destroy();
|
||||
this._overlay.destroy();
|
||||
|
||||
Main.endModal();
|
||||
@ -240,7 +240,7 @@ AltTabPopup.prototype = {
|
||||
},
|
||||
|
||||
_adjust_overlay : function() {
|
||||
let global = Shell.Global.get();
|
||||
let global = Shell.Global.get();
|
||||
|
||||
if (this._selected && this._selected.icon_rect) {
|
||||
// We want to highlight a specific rectangle within the
|
||||
|
@ -3,18 +3,33 @@
|
||||
const Big = imports.gi.Big;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Pango = imports.gi.Pango;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Tidy = imports.gi.Tidy;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
const AppInfo = imports.misc.appInfo;
|
||||
const DND = imports.ui.dnd;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
const Main = imports.ui.main;
|
||||
const Workspaces = imports.ui.workspaces;
|
||||
|
||||
const ENTERED_MENU_COLOR = new Clutter.Color();
|
||||
ENTERED_MENU_COLOR.from_pixel(0x00ff0022);
|
||||
|
||||
const GLOW_COLOR = new Clutter.Color();
|
||||
GLOW_COLOR.from_pixel(0x4f6ba4ff);
|
||||
const GLOW_PADDING = 5;
|
||||
|
||||
|
||||
const APP_ICON_SIZE = 48;
|
||||
const WELL_DEFAULT_COLUMNS = 4;
|
||||
const WELL_ITEM_HSPACING = 0;
|
||||
const WELL_ITEM_VSPACING = 4;
|
||||
|
||||
const MENU_ICON_SIZE = 24;
|
||||
const MENU_SPACING = 15;
|
||||
|
||||
@ -23,21 +38,23 @@ const MAX_ITEMS = 30;
|
||||
/* This class represents a single display item containing information about an application.
|
||||
*
|
||||
* appInfo - AppInfo object containing information about the application
|
||||
* availableWidth - total width available for the item
|
||||
*/
|
||||
function AppDisplayItem(appInfo, availableWidth) {
|
||||
this._init(appInfo, availableWidth);
|
||||
function AppDisplayItem(appInfo) {
|
||||
this._init(appInfo);
|
||||
}
|
||||
|
||||
AppDisplayItem.prototype = {
|
||||
__proto__: GenericDisplay.GenericDisplayItem.prototype,
|
||||
|
||||
_init : function(appInfo, availableWidth) {
|
||||
GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
|
||||
_init : function(appInfo) {
|
||||
GenericDisplay.GenericDisplayItem.prototype._init.call(this);
|
||||
this._appInfo = appInfo;
|
||||
|
||||
this._setItemInfo(appInfo.name, appInfo.description,
|
||||
appInfo.getIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE));
|
||||
this._setItemInfo(appInfo.get_name(), appInfo.get_description());
|
||||
},
|
||||
|
||||
getId: function() {
|
||||
return this._appInfo.get_id();
|
||||
},
|
||||
|
||||
//// Public method overrides ////
|
||||
@ -49,21 +66,14 @@ AppDisplayItem.prototype = {
|
||||
|
||||
//// Protected method overrides ////
|
||||
|
||||
// Ensures the preview icon is created.
|
||||
_ensurePreviewIconCreated : function() {
|
||||
if (!this._showPreview || this._previewIcon)
|
||||
return;
|
||||
// Returns an icon for the item.
|
||||
_createIcon : function() {
|
||||
return this._appInfo.create_icon_texture(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
|
||||
},
|
||||
|
||||
let previewIconPath = this._appInfo.getIconPath(GenericDisplay.PREVIEW_ICON_SIZE);
|
||||
if (previewIconPath) {
|
||||
try {
|
||||
this._previewIcon = new Clutter.Texture({ width: GenericDisplay.PREVIEW_ICON_SIZE, height: GenericDisplay.PREVIEW_ICON_SIZE});
|
||||
this._previewIcon.set_from_file(previewIconPath);
|
||||
} catch (e) {
|
||||
// we can get an error here if the file path doesn't exist on the system
|
||||
log('Error loading AppDisplayItem preview icon ' + e);
|
||||
}
|
||||
}
|
||||
// Returns a preview icon for the item.
|
||||
_createPreviewIcon : function() {
|
||||
return this._appInfo.create_icon_texture(GenericDisplay.PREVIEW_ICON_SIZE);
|
||||
}
|
||||
};
|
||||
|
||||
@ -104,24 +114,28 @@ MenuItem.prototype = {
|
||||
}
|
||||
if (pixbuf != null)
|
||||
Shell.clutter_texture_set_from_pixbuf(this._icon, pixbuf);
|
||||
this.actor.append(this._icon, 0);
|
||||
this.actor.append(this._icon, Big.BoxPackFlags.NONE);
|
||||
this._text = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
|
||||
font_name: "Sans 14px",
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
text: name });
|
||||
this.actor.append(this._text, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
let box = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
y_align: Big.BoxAlignment.CENTER
|
||||
});
|
||||
// We use individual boxes for the label and the arrow to ensure that they
|
||||
// are aligned vertically. Just setting y_align: Big.BoxAlignment.CENTER
|
||||
// on this.actor does not seem to achieve that.
|
||||
let labelBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
|
||||
|
||||
this._arrow = new Shell.Arrow({ surface_width: MENU_ICON_SIZE/2,
|
||||
surface_height: MENU_ICON_SIZE/2,
|
||||
labelBox.append(this._text, Big.BoxPackFlags.NONE);
|
||||
|
||||
this.actor.append(labelBox, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
let arrowBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
|
||||
|
||||
this._arrow = new Shell.Arrow({ surface_width: MENU_ICON_SIZE / 2,
|
||||
surface_height: MENU_ICON_SIZE / 2,
|
||||
direction: Gtk.ArrowType.RIGHT,
|
||||
opacity: 0
|
||||
});
|
||||
box.append(this._arrow, 0);
|
||||
this.actor.append(box, 0);
|
||||
opacity: 0 });
|
||||
arrowBox.append(this._arrow, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(arrowBox, Big.BoxPackFlags.NONE);
|
||||
},
|
||||
|
||||
getState: function() {
|
||||
@ -147,22 +161,20 @@ MenuItem.prototype = {
|
||||
}
|
||||
Signals.addSignalMethods(MenuItem.prototype);
|
||||
|
||||
|
||||
/* This class represents a display containing a collection of application items.
|
||||
* The applications are sorted based on their popularity by default, and based on
|
||||
* their name if some search filter is applied.
|
||||
*
|
||||
* width - width available for the display
|
||||
* height - height available for the display
|
||||
*/
|
||||
function AppDisplay(width, height, numberOfColumns, columnGap) {
|
||||
this._init(width, height, numberOfColumns, columnGap);
|
||||
function AppDisplay() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
AppDisplay.prototype = {
|
||||
__proto__: GenericDisplay.GenericDisplay.prototype,
|
||||
|
||||
_init : function(width, height, numberOfColumns, columnGap) {
|
||||
GenericDisplay.GenericDisplay.prototype._init.call(this, width, height, numberOfColumns, columnGap);
|
||||
_init : function() {
|
||||
GenericDisplay.GenericDisplay.prototype._init.call(this);
|
||||
|
||||
this._menus = [];
|
||||
this._menuDisplays = [];
|
||||
@ -173,20 +185,19 @@ AppDisplay.prototype = {
|
||||
this._appMonitor = Shell.AppMonitor.get_default();
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this._appsStale = true;
|
||||
this._appSystem.connect('changed', Lang.bind(this, function(appSys) {
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, function(appSys) {
|
||||
this._appsStale = true;
|
||||
// We still need to determine what events other than search can trigger
|
||||
// a change in the set of applications that are being shown while the
|
||||
// user in in the overlay mode, however let's redisplay just in case.
|
||||
this._redisplay(false);
|
||||
this._redisplayMenus();
|
||||
}));
|
||||
this._appSystem.connect('favorites-changed', Lang.bind(this, function(appSys) {
|
||||
this._redisplay(false);
|
||||
}));
|
||||
this._appMonitor.connect('changed', Lang.bind(this, function(monitor) {
|
||||
this._appsStale = true;
|
||||
this._redisplay(false);
|
||||
}));
|
||||
|
||||
// Load the GAppInfos now so it doesn't slow down the first
|
||||
// Load the apps now so it doesn't slow down the first
|
||||
// transition into the overlay
|
||||
this._refreshCache();
|
||||
|
||||
@ -202,6 +213,7 @@ AppDisplay.prototype = {
|
||||
this.connect('expanded', Lang.bind(this, function (self) {
|
||||
this._filterReset();
|
||||
}));
|
||||
this._filterReset();
|
||||
},
|
||||
|
||||
moveRight: function() {
|
||||
@ -221,7 +233,7 @@ AppDisplay.prototype = {
|
||||
},
|
||||
|
||||
// Override genericDisplay.js
|
||||
getSideArea: function() {
|
||||
getNavigationArea: function() {
|
||||
return this._menuDisplay;
|
||||
},
|
||||
|
||||
@ -242,16 +254,13 @@ AppDisplay.prototype = {
|
||||
// Protected overrides
|
||||
|
||||
_filterActive: function() {
|
||||
return !!this._search || this._activeMenuIndex >= 0;
|
||||
// We always have a filter now since a menu must be selected
|
||||
return true;
|
||||
},
|
||||
|
||||
_filterReset: function() {
|
||||
GenericDisplay.GenericDisplay.prototype._filterReset.call(this);
|
||||
if (this._activeMenu != null)
|
||||
this._activeMenu.setState(MENU_UNSELECTED);
|
||||
this._activeMenuIndex = -1;
|
||||
this._activeMenu = null;
|
||||
this._focusInMenu = true;
|
||||
this._selectMenuIndex(0);
|
||||
},
|
||||
|
||||
//// Private ////
|
||||
@ -266,49 +275,52 @@ AppDisplay.prototype = {
|
||||
this._menuDisplays[index].setState(MENU_SELECTED);
|
||||
},
|
||||
|
||||
_redisplayMenus: function() {
|
||||
this._menuDisplay.remove_all();
|
||||
for (let i = 0; i < this._menus.length; i++) {
|
||||
let menu = this._menus[i];
|
||||
let display = new MenuItem(menu.name, menu.id, menu.icon);
|
||||
this._menuDisplays.push(display);
|
||||
let menuIndex = i;
|
||||
display.connect('state-changed', Lang.bind(this, function (display) {
|
||||
let activated = display.getState() != MENU_UNSELECTED;
|
||||
if (!activated && display == this._activeMenu) {
|
||||
this._activeMenuIndex = -1;
|
||||
this._activeMenu = null;
|
||||
} else if (activated) {
|
||||
if (display != this._activeMenu && this._activeMenu != null)
|
||||
this._activeMenu.setState(MENU_UNSELECTED);
|
||||
this._activeMenuIndex = menuIndex;
|
||||
this._activeMenu = display;
|
||||
this._activeMenuApps = this._appSystem.get_applications_for_menu(menu.id);
|
||||
}
|
||||
this._redisplay();
|
||||
}));
|
||||
this._menuDisplay.append(display.actor, 0);
|
||||
}
|
||||
_getMostUsed: function() {
|
||||
let context = "";
|
||||
return this._appMonitor.get_most_used_apps(context, 30).map(Lang.bind(this, function (id) {
|
||||
return this._appSystem.lookup_cached_app(id);
|
||||
})).filter(function (e) { return e != null });
|
||||
},
|
||||
|
||||
_addAppForId: function(appId) {
|
||||
let appInfo = AppInfo.getAppInfo(appId);
|
||||
if (appInfo != null) {
|
||||
this._addApp(appInfo);
|
||||
} else {
|
||||
log("appInfo for " + appId + " was not found.");
|
||||
_addMenuItem: function(name, id, icon, index) {
|
||||
let display = new MenuItem(name, id, icon);
|
||||
this._menuDisplays.push(display);
|
||||
display.connect('state-changed', Lang.bind(this, function (display) {
|
||||
let activated = display.getState() != MENU_UNSELECTED;
|
||||
if (!activated && display == this._activeMenu) {
|
||||
this._activeMenuIndex = -1;
|
||||
this._activeMenu = null;
|
||||
} else if (activated) {
|
||||
if (display != this._activeMenu && this._activeMenu != null)
|
||||
this._activeMenu.setState(MENU_UNSELECTED);
|
||||
this._activeMenuIndex = index;
|
||||
this._activeMenu = display;
|
||||
if (id == null) {
|
||||
this._activeMenuApps = this._getMostUsed();
|
||||
} else {
|
||||
this._activeMenuApps = this._appSystem.get_applications_for_menu(id);
|
||||
}
|
||||
}
|
||||
this._redisplay(true);
|
||||
}));
|
||||
this._menuDisplay.append(display.actor, 0);
|
||||
},
|
||||
|
||||
_redisplayMenus: function() {
|
||||
this._menuDisplay.remove_all();
|
||||
this._addMenuItem('Frequent', null, 'gtk-select-all');
|
||||
for (let i = 0; i < this._menus.length; i++) {
|
||||
let menu = this._menus[i];
|
||||
this._addMenuItem(menu.name, menu.id, menu.icon, i+1);
|
||||
}
|
||||
},
|
||||
|
||||
_addApp: function(appInfo) {
|
||||
let appId = appInfo.id;
|
||||
let appId = appInfo.get_id();
|
||||
this._allItems[appId] = appInfo;
|
||||
// [] is returned if we could not get the categories or the list of categories was empty
|
||||
let categories = Shell.get_categories_for_desktop_file(appId);
|
||||
this._appCategories[appId] = categories;
|
||||
},
|
||||
|
||||
//// Protected method overrides ////
|
||||
//// Protected method overrides ////
|
||||
|
||||
// Gets information about all applications by calling Gio.app_info_get_all().
|
||||
_refreshCache : function() {
|
||||
@ -319,15 +331,14 @@ AppDisplay.prototype = {
|
||||
this._appCategories = {};
|
||||
|
||||
this._menus = this._appSystem.get_menus();
|
||||
|
||||
// Loop over the toplevel menu items, load the set of desktop file ids
|
||||
// associated with each one
|
||||
for (let i = 0; i < this._menus.length; i++) {
|
||||
let menu = this._menus[i];
|
||||
let menuApps = this._appSystem.get_applications_for_menu(menu.id);
|
||||
for (let j = 0; j < menuApps.length; j++) {
|
||||
let appId = menuApps[j];
|
||||
this._addAppForId(appId);
|
||||
let app = menuApps[j];
|
||||
this._addApp(app);
|
||||
}
|
||||
}
|
||||
|
||||
@ -335,26 +346,16 @@ AppDisplay.prototype = {
|
||||
// These show up in search, but not with the rest of apps.
|
||||
let settings = this._appSystem.get_all_settings();
|
||||
for (let i = 0; i < settings.length; i++) {
|
||||
let appId = settings[i];
|
||||
this._addAppForId(appId);
|
||||
let app = settings[i];
|
||||
this._addApp(app);
|
||||
}
|
||||
|
||||
// Some applications, such as Evince, might not be in the menus,
|
||||
// but might be returned by the applications monitor as most used
|
||||
// applications, in which case we include them.
|
||||
let mostUsedAppInfos = AppInfo.getMostUsedApps(MAX_ITEMS);
|
||||
for (let i = 0; i < mostUsedAppInfos.length; i++) {
|
||||
let appInfo = mostUsedAppInfos[i];
|
||||
this._addApp(appInfo);
|
||||
}
|
||||
|
||||
this._appsStale = false;
|
||||
},
|
||||
|
||||
// Sets the list of the displayed items based on the most used apps.
|
||||
// Stub this out; the app display always has a category selected
|
||||
_setDefaultList : function() {
|
||||
let matchedInfos = AppInfo.getMostUsedApps(MAX_ITEMS);
|
||||
this._matchedItems = matchedInfos.map(function(info) { return info.appId; });
|
||||
this._matchedItems = [];
|
||||
},
|
||||
|
||||
// Compares items associated with the item ids based on the alphabetical order
|
||||
@ -363,14 +364,17 @@ AppDisplay.prototype = {
|
||||
_compareItems : function(itemIdA, itemIdB) {
|
||||
let appA = this._allItems[itemIdA];
|
||||
let appB = this._allItems[itemIdB];
|
||||
return appA.name.localeCompare(appB.name);
|
||||
return appA.get_name().localeCompare(appB.get_name());
|
||||
},
|
||||
|
||||
// Checks if the item info can be a match for the search string by checking
|
||||
// the name, description, execution command, and categories for the application.
|
||||
// Item info is expected to be GAppInfo.
|
||||
// the name, description, execution command, and categories for the application.
|
||||
// Item info is expected to be Shell.AppInfo.
|
||||
// Returns a boolean flag indicating if itemInfo is a match.
|
||||
_isInfoMatching : function(itemInfo, search) {
|
||||
// Don't show nodisplay items here
|
||||
if (itemInfo.get_is_nodisplay())
|
||||
return false;
|
||||
// Search takes precedence; not typically useful to search within a
|
||||
// menu
|
||||
if (this._activeMenu == null || search != "")
|
||||
@ -380,10 +384,10 @@ AppDisplay.prototype = {
|
||||
},
|
||||
|
||||
_isInfoMatchingMenu : function(itemInfo, search) {
|
||||
let id = itemInfo.id;
|
||||
let id = itemInfo.get_id();
|
||||
for (let i = 0; i < this._activeMenuApps.length; i++) {
|
||||
let activeId = this._activeMenuApps[i];
|
||||
if (activeId == id)
|
||||
let activeApp = this._activeMenuApps[i];
|
||||
if (activeApp.get_id() == id)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -393,40 +397,472 @@ AppDisplay.prototype = {
|
||||
if (search == null || search == '')
|
||||
return true;
|
||||
|
||||
let name = itemInfo.name.toLowerCase();
|
||||
let fold = function(s) {
|
||||
if (!s)
|
||||
return s;
|
||||
return GLib.utf8_casefold(GLib.utf8_normalize(s, -1,
|
||||
GLib.NormalizeMode.ALL), -1);
|
||||
};
|
||||
let name = fold(itemInfo.get_name());
|
||||
if (name.indexOf(search) >= 0)
|
||||
return true;
|
||||
|
||||
let description = itemInfo.description;
|
||||
let description = fold(itemInfo.get_description());
|
||||
if (description) {
|
||||
description = description.toLowerCase();
|
||||
if (description.indexOf(search) >= 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemInfo.executable == null) {
|
||||
let exec = fold(itemInfo.get_executable());
|
||||
if (exec == null) {
|
||||
log("Missing an executable for " + itemInfo.name);
|
||||
} else {
|
||||
let exec = itemInfo.executable.toLowerCase();
|
||||
if (exec.indexOf(search) >= 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
// we expect this._appCategories.hasOwnProperty(itemInfo.id) to always be true here
|
||||
let categories = this._appCategories[itemInfo.id];
|
||||
let categories = itemInfo.get_categories();
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
let category = categories[i].toLowerCase();
|
||||
let category = fold(categories[i]);
|
||||
if (category.indexOf(search) >= 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
// Creates an AppDisplayItem based on itemInfo, which is expected be an AppInfo object.
|
||||
// Creates an AppDisplayItem based on itemInfo, which is expected be an Shell.AppInfo object.
|
||||
_createDisplayItem: function(itemInfo) {
|
||||
return new AppDisplayItem(itemInfo, this._columnWidth);
|
||||
return new AppDisplayItem(itemInfo);
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(AppDisplay.prototype);
|
||||
|
||||
function WellDisplayItem(appInfo, isFavorite) {
|
||||
this._init(appInfo, isFavorite);
|
||||
}
|
||||
|
||||
WellDisplayItem.prototype = {
|
||||
_init : function(appInfo, isFavorite) {
|
||||
this.appInfo = appInfo;
|
||||
|
||||
this.isFavorite = isFavorite;
|
||||
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
corner_radius: 2,
|
||||
border: 0,
|
||||
padding: 1,
|
||||
border_color: GenericDisplay.ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR,
|
||||
reactive: true });
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('button-release-event', Lang.bind(this, function (b, e) {
|
||||
this._handleActivate();
|
||||
}));
|
||||
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
|
||||
let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
x_align: Big.BoxAlignment.CENTER });
|
||||
this._icon = appInfo.create_icon_texture(APP_ICON_SIZE);
|
||||
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
||||
|
||||
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.get_id());
|
||||
|
||||
let nameBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
x_align: Big.BoxAlignment.CENTER });
|
||||
this._nameBox = nameBox;
|
||||
|
||||
this._name = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
|
||||
font_name: "Sans 12px",
|
||||
line_alignment: Pango.Alignment.CENTER,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
text: appInfo.get_name() });
|
||||
nameBox.append(this._name, Big.BoxPackFlags.NONE);
|
||||
if (this._windows.length > 0) {
|
||||
let glow = new Shell.DrawingArea({});
|
||||
glow.connect('redraw', Lang.bind(this, function (e, tex) {
|
||||
Shell.draw_glow(tex,
|
||||
GLOW_COLOR.red / 255,
|
||||
GLOW_COLOR.green / 255,
|
||||
GLOW_COLOR.blue / 255,
|
||||
GLOW_COLOR.alpha / 255);
|
||||
}));
|
||||
this._name.connect('notify::allocation', Lang.bind(this, function () {
|
||||
let x = this._name.x;
|
||||
let y = this._name.y;
|
||||
let width = this._name.width;
|
||||
let height = this._name.height;
|
||||
// If we're smaller than the allocated box width, pad out the glow a bit
|
||||
// to make it more visible
|
||||
if ((width + GLOW_PADDING * 2) < this._nameBox.width) {
|
||||
width += GLOW_PADDING * 2;
|
||||
x -= GLOW_PADDING;
|
||||
}
|
||||
glow.set_size(width, height);
|
||||
glow.set_position(x, y);
|
||||
}));
|
||||
nameBox.add_actor(glow);
|
||||
glow.lower(this._name);
|
||||
}
|
||||
this.actor.append(nameBox, Big.BoxPackFlags.NONE);
|
||||
},
|
||||
|
||||
_handleActivate: function () {
|
||||
if (this._windows.length == 0)
|
||||
this.launch();
|
||||
else {
|
||||
/* Pick the first window and activate it;
|
||||
* In the future, we want to have a menu dropdown here. */
|
||||
let first = this._windows[0];
|
||||
Main.overlay.activateWindow (first, Clutter.get_current_event_time());
|
||||
}
|
||||
this.emit('activated');
|
||||
},
|
||||
|
||||
// Opens an application represented by this display item.
|
||||
launch : function() {
|
||||
this.appInfo.launch();
|
||||
},
|
||||
|
||||
// Draggable interface - FIXME deduplicate with GenericDisplay
|
||||
getDragActor: function(stageX, stageY) {
|
||||
this.dragActor = this.appInfo.create_icon_texture(APP_ICON_SIZE);
|
||||
|
||||
// If the user dragged from the icon itself, then position
|
||||
// the dragActor over the original icon. Otherwise center it
|
||||
// around the pointer
|
||||
let [iconX, iconY] = this._icon.get_transformed_position();
|
||||
let [iconWidth, iconHeight] = this._icon.get_transformed_size();
|
||||
if (stageX > iconX && stageX <= iconX + iconWidth &&
|
||||
stageY > iconY && stageY <= iconY + iconHeight)
|
||||
this.dragActor.set_position(iconX, iconY);
|
||||
else
|
||||
this.dragActor.set_position(stageX - this.dragActor.width / 2, stageY - this.dragActor.height / 2);
|
||||
return this.dragActor;
|
||||
},
|
||||
|
||||
// Returns the original icon that is being used as a source for the cloned texture
|
||||
// that represents the item as it is being dragged.
|
||||
getDragActorSource: function() {
|
||||
return this._icon;
|
||||
},
|
||||
|
||||
setWidth: function(width) {
|
||||
this._nameBox.width = width + GLOW_PADDING * 2;
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(WellDisplayItem.prototype);
|
||||
|
||||
function WellGrid() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
WellGrid.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new Shell.GenericContainer();
|
||||
|
||||
this._separator = new Big.Box({ border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
|
||||
border_top: 1,
|
||||
height: 1 });
|
||||
this.actor.add_actor(this._separator);
|
||||
this._separatorIndex = 0;
|
||||
this._cachedSeparatorY = 0;
|
||||
|
||||
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));
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (grid, forHeight, alloc) {
|
||||
let [itemMin, itemNatural] = this._getItemPreferredWidth();
|
||||
let children = this._getItemChildren();
|
||||
let nColumns;
|
||||
if (children.length < WELL_DEFAULT_COLUMNS)
|
||||
nColumns = children.length;
|
||||
else
|
||||
nColumns = WELL_DEFAULT_COLUMNS;
|
||||
let spacing = Math.max(nColumns - 1, 0) * WELL_ITEM_HSPACING;
|
||||
alloc.min_size = itemMin * nColumns + spacing;
|
||||
alloc.natural_size = itemNatural * nColumns + spacing;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||
let [rows, columns, itemWidth, itemHeight] = this._computeLayout(forWidth);
|
||||
let totalVerticalSpacing = Math.max(rows - 1, 0) * WELL_ITEM_VSPACING;
|
||||
|
||||
let [separatorMin, separatorNatural] = this._separator.get_preferred_height(forWidth);
|
||||
alloc.min_size = alloc.natural_size = rows * itemHeight + totalVerticalSpacing + separatorNatural;
|
||||
},
|
||||
|
||||
_allocate: function (grid, box, flags) {
|
||||
let children = this._getItemChildren();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
let [rows, columns, itemWidth, itemHeight] = this._computeLayout(availWidth);
|
||||
|
||||
let [separatorMin, separatorNatural] = this._separator.get_preferred_height(-1);
|
||||
|
||||
let x = box.x1;
|
||||
let y = box.y1;
|
||||
let columnIndex = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [childMinWidth, childMinHeight,
|
||||
childNaturalWidth, childNaturalHeight] = children[i].get_preferred_size();
|
||||
|
||||
/* Center the item in its allocation */
|
||||
let width = Math.min(itemWidth, childNaturalWidth);
|
||||
let height = Math.min(itemHeight, childNaturalHeight);
|
||||
let horizSpacing = (itemWidth - width) / 2;
|
||||
let vertSpacing = (itemHeight - height) / 2;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.x1 = Math.floor(x + horizSpacing);
|
||||
childBox.y1 = Math.floor(y + vertSpacing);
|
||||
childBox.x2 = childBox.x1 + width;
|
||||
childBox.y2 = childBox.y1 + height;
|
||||
children[i].allocate(childBox, flags);
|
||||
|
||||
let atSeparator = (i == this._separatorIndex - 1);
|
||||
|
||||
columnIndex++;
|
||||
if (columnIndex == columns || atSeparator) {
|
||||
columnIndex = 0;
|
||||
}
|
||||
|
||||
if (columnIndex == 0) {
|
||||
y += itemHeight + WELL_ITEM_VSPACING;
|
||||
x = box.x1;
|
||||
} else {
|
||||
x += itemWidth + WELL_ITEM_HSPACING;
|
||||
}
|
||||
|
||||
if (atSeparator) {
|
||||
y += separatorNatural + WELL_ITEM_VSPACING;
|
||||
}
|
||||
}
|
||||
|
||||
let separatorRowIndex = Math.ceil(this._separatorIndex / columns);
|
||||
|
||||
/* Allocate the separator */
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.x1 = box.x1;
|
||||
childBox.y1 = (itemHeight + WELL_ITEM_VSPACING) * separatorRowIndex;
|
||||
this._cachedSeparatorY = childBox.y1;
|
||||
childBox.x2 = box.x2;
|
||||
childBox.y2 = childBox.y1+separatorNatural;
|
||||
this._separator.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
setSeparatorIndex: function (index) {
|
||||
this._separatorIndex = index;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
removeAll: function () {
|
||||
let itemChildren = this._getItemChildren();
|
||||
for (let i = 0; i < itemChildren.length; i++) {
|
||||
itemChildren[i].destroy();
|
||||
}
|
||||
this._separatorIndex = 0;
|
||||
},
|
||||
|
||||
isBeforeSeparator: function(x, y) {
|
||||
return y < this._cachedSeparatorY;
|
||||
},
|
||||
|
||||
_getItemChildren: function () {
|
||||
let children = this.actor.get_children();
|
||||
children.shift();
|
||||
return children;
|
||||
},
|
||||
|
||||
_computeLayout: function (forWidth) {
|
||||
let [itemMinWidth, itemNaturalWidth] = this._getItemPreferredWidth();
|
||||
let columnsNatural;
|
||||
let i;
|
||||
let children = this._getItemChildren();
|
||||
if (children.length == 0)
|
||||
return [0, WELL_DEFAULT_COLUMNS, 0, 0];
|
||||
let nColumns;
|
||||
if (children.length < WELL_DEFAULT_COLUMNS)
|
||||
nColumns = children.length;
|
||||
else
|
||||
nColumns = WELL_DEFAULT_COLUMNS;
|
||||
|
||||
if (forWidth >= 0 && forWidth < minWidth) {
|
||||
log("WellGrid: trying to allocate for width " + forWidth + " but min is " + minWidth);
|
||||
/* FIXME - we should fall back to fewer than WELL_DEFAULT_COLUMNS here */
|
||||
}
|
||||
|
||||
let horizSpacingTotal = Math.max(nColumns - 1, 0) * WELL_ITEM_HSPACING;
|
||||
let minWidth = itemMinWidth * nColumns + horizSpacingTotal;
|
||||
|
||||
let lastColumnIndex = nColumns - 1;
|
||||
let separatorColumns = lastColumnIndex - ((lastColumnIndex + this._separatorIndex) % nColumns);
|
||||
let rows = Math.ceil((children.length + separatorColumns) / nColumns);
|
||||
|
||||
let itemWidth;
|
||||
if (forWidth < 0) {
|
||||
itemWidth = itemNaturalWidth;
|
||||
} else {
|
||||
itemWidth = Math.max(forWidth - horizSpacingTotal, 0) / nColumns;
|
||||
}
|
||||
|
||||
let itemNaturalHeight = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [childMin, childNatural] = children[i].get_preferred_height(itemWidth);
|
||||
if (childNatural > itemNaturalHeight)
|
||||
itemNaturalHeight = childNatural;
|
||||
}
|
||||
|
||||
return [rows, WELL_DEFAULT_COLUMNS, itemWidth, itemNaturalHeight];
|
||||
},
|
||||
|
||||
_getItemPreferredWidth: function () {
|
||||
let children = this._getItemChildren();
|
||||
let minWidth = 0;
|
||||
let naturalWidth = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [childMin, childNatural] = children[i].get_preferred_width(-1);
|
||||
if (childMin > minWidth)
|
||||
minWidth = childMin;
|
||||
if (childNatural > naturalWidth)
|
||||
naturalWidth = childNatural;
|
||||
}
|
||||
return [minWidth, naturalWidth];
|
||||
}
|
||||
}
|
||||
|
||||
function AppWell() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
AppWell.prototype = {
|
||||
_init : function() {
|
||||
this._menus = [];
|
||||
this._menuDisplays = [];
|
||||
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
x_align: Big.BoxAlignment.CENTER });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._grid = new WellGrid();
|
||||
this.actor.append(this._grid.actor, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this._appMonitor = Shell.AppMonitor.get_default();
|
||||
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, function(appSys) {
|
||||
this._redisplay();
|
||||
}));
|
||||
this._appSystem.connect('favorites-changed', Lang.bind(this, function(appSys) {
|
||||
this._redisplay();
|
||||
}));
|
||||
this._appMonitor.connect('changed', Lang.bind(this, function(monitor) {
|
||||
this._redisplay();
|
||||
}));
|
||||
|
||||
this._redisplay();
|
||||
},
|
||||
|
||||
_lookupApps: function(appIds) {
|
||||
let result = [];
|
||||
for (let i = 0; i < appIds.length; i++) {
|
||||
let id = appIds[i];
|
||||
let app = this._appSystem.lookup_cached_app(id);
|
||||
if (!app)
|
||||
continue;
|
||||
result.push(app);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
_arrayValues: function(array) {
|
||||
return array.reduce(function (values, id, index) {
|
||||
values[id] = index; return values; }, {});
|
||||
},
|
||||
|
||||
_redisplay: function () {
|
||||
this._grid.removeAll();
|
||||
|
||||
let favoriteIds = this._appSystem.get_favorites();
|
||||
let favoriteIdsHash = this._arrayValues(favoriteIds);
|
||||
|
||||
/* hardcode here pending some design about how exactly desktop contexts behave */
|
||||
let contextId = "";
|
||||
|
||||
let runningIds = this._appMonitor.get_running_app_ids(contextId).filter(function (e) {
|
||||
return !(e in favoriteIdsHash);
|
||||
});
|
||||
let favorites = this._lookupApps(favoriteIds);
|
||||
let running = this._lookupApps(runningIds);
|
||||
|
||||
let displays = []
|
||||
this._addApps(favorites, true);
|
||||
this._grid.setSeparatorIndex(favorites.length);
|
||||
this._addApps(running, false);
|
||||
this._displays = displays;
|
||||
},
|
||||
|
||||
_addApps: function(apps) {
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let app = apps[i];
|
||||
let display = new WellDisplayItem(app, this.isFavorite);
|
||||
display.connect('activated', Lang.bind(this, function (display) {
|
||||
Main.overlay.hide();
|
||||
}));
|
||||
this._grid.actor.add_actor(display.actor);
|
||||
}
|
||||
},
|
||||
|
||||
// Draggable target interface
|
||||
acceptDrop : function(source, actor, x, y, time) {
|
||||
let global = Shell.Global.get();
|
||||
|
||||
let id = null;
|
||||
if (source instanceof WellDisplayItem) {
|
||||
id = source.appInfo.get_id();
|
||||
} else if (source instanceof AppDisplayItem) {
|
||||
id = source.getId();
|
||||
} else if (source instanceof Workspaces.WindowClone) {
|
||||
let appMonitor = Shell.AppMonitor.get_default();
|
||||
let app = appMonitor.get_window_app(source.metaWindow);
|
||||
id = app.get_id();
|
||||
}
|
||||
|
||||
if (id == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
let favoriteIds = this._appSystem.get_favorites();
|
||||
let favoriteIdsObject = this._arrayValues(favoriteIds);
|
||||
|
||||
let dropIsFavorite = this._grid.isBeforeSeparator(x - this._grid.actor.x,
|
||||
y - this._grid.actor.y);
|
||||
let srcIsFavorite = (id in favoriteIdsObject);
|
||||
|
||||
if (srcIsFavorite && (!dropIsFavorite)) {
|
||||
Mainloop.idle_add(function () {
|
||||
appSystem.remove_favorite(id);
|
||||
return false;
|
||||
});
|
||||
} else if ((!srcIsFavorite) && dropIsFavorite) {
|
||||
Mainloop.idle_add(function () {
|
||||
appSystem.add_favorite(id);
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(AppWell.prototype);
|
||||
|
126
js/ui/button.js
126
js/ui/button.js
@ -2,6 +2,11 @@
|
||||
|
||||
const Big = imports.gi.Big;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
const Shell = imports.gi.Shell;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const DEFAULT_BUTTON_COLOR = new Clutter.Color();
|
||||
DEFAULT_BUTTON_COLOR.from_pixel(0xeeddcc66);
|
||||
@ -9,12 +14,20 @@ DEFAULT_BUTTON_COLOR.from_pixel(0xeeddcc66);
|
||||
const DEFAULT_PRESSED_BUTTON_COLOR = new Clutter.Color();
|
||||
DEFAULT_PRESSED_BUTTON_COLOR.from_pixel(0xccbbaa66);
|
||||
|
||||
function Button(widget, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) {
|
||||
this._init(widget, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight);
|
||||
const DEFAULT_TEXT_COLOR = new Clutter.Color();
|
||||
DEFAULT_TEXT_COLOR.from_pixel(0x000000ff);
|
||||
|
||||
const DEFAULT_FONT = 'Sans Bold 16px';
|
||||
|
||||
// Padding on the left and right side of the button.
|
||||
const SIDE_PADDING = 14;
|
||||
|
||||
function Button(widget, buttonColor, pressedButtonColor, textColor, staysPressed, minWidth, minHeight, font) {
|
||||
this._init(widget, buttonColor, pressedButtonColor, textColor, staysPressed, minWidth, minHeight, font);
|
||||
}
|
||||
|
||||
Button.prototype = {
|
||||
_init : function(widgetOrText, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) {
|
||||
_init : function(widgetOrText, buttonColor, pressedButtonColor, textColor, staysPressed, minWidth, minHeight, font) {
|
||||
let me = this;
|
||||
|
||||
this._buttonColor = buttonColor
|
||||
@ -25,10 +38,18 @@ Button.prototype = {
|
||||
if (pressedButtonColor == null)
|
||||
this._pressedButtonColor = DEFAULT_PRESSED_BUTTON_COLOR;
|
||||
|
||||
this._textColor = textColor;
|
||||
if (textColor == null)
|
||||
this._textColor = DEFAULT_TEXT_COLOR;
|
||||
|
||||
this._staysPressed = staysPressed
|
||||
if (staysPressed == null)
|
||||
this._staysPressed = false;
|
||||
|
||||
this._font = font;
|
||||
if (font == null)
|
||||
this._font = DEFAULT_FONT;
|
||||
|
||||
if (minWidth == null)
|
||||
minWidth = 0;
|
||||
if (minHeight == null)
|
||||
@ -42,13 +63,14 @@ Button.prototype = {
|
||||
|
||||
this.button = new Big.Box({ reactive: true,
|
||||
corner_radius: 5,
|
||||
padding_left: 4,
|
||||
padding_right: 4,
|
||||
padding_left: SIDE_PADDING,
|
||||
padding_right: SIDE_PADDING,
|
||||
orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
y_align: Big.BoxAlignment.CENTER
|
||||
});
|
||||
if (typeof widgetOrText == 'string') {
|
||||
this._widget = new Clutter.Text({ font_name: "Sans Bold 16px",
|
||||
this._widget = new Clutter.Text({ font_name: this._font,
|
||||
color: this._textColor,
|
||||
text: widgetOrText });
|
||||
} else {
|
||||
this._widget = widgetOrText;
|
||||
@ -112,3 +134,95 @@ Button.prototype = {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Delay before the icon should appear, in seconds after the pointer has entered the parent */
|
||||
const ANIMATION_TIME = 0.25;
|
||||
|
||||
/* This is an icon button that fades in/out when mouse enters/leaves the parent.
|
||||
* A delay is used before the fading starts. You can force it to be shown if needed.
|
||||
*
|
||||
* parent -- used to show/hide the button depending on mouse entering/leaving it
|
||||
* size -- size in pixels of both the button and the icon it contains
|
||||
* texture -- optional, must be used if the texture for the icon is already created (else, use setIconFromName)
|
||||
*/
|
||||
function iconButton(parent, size, texture) {
|
||||
this._init(parent, size, texture);
|
||||
}
|
||||
|
||||
iconButton.prototype = {
|
||||
_init : function(parent, size, texture) {
|
||||
this._size = size;
|
||||
if (texture)
|
||||
this.actor = texture;
|
||||
else
|
||||
this.actor = new Clutter.Texture({ width: this._size, height: this._size });
|
||||
this.actor.set_reactive(true);
|
||||
this.actor.set_opacity(0);
|
||||
parent.connect("enter-event", Lang.bind(this, function(actor, event) {
|
||||
this._shouldHide = false;
|
||||
|
||||
// Nothing to do if the cursor has come back from a child of the parent actor
|
||||
if (actor.get_children().indexOf(Shell.get_event_related(event)) != -1)
|
||||
return;
|
||||
|
||||
this._fadeIn();
|
||||
}));
|
||||
parent.connect("leave-event", Lang.bind(this, function(actor, event) {
|
||||
// Nothing to do if the cursor has merely entered a child of the parent actor
|
||||
if (actor.get_children().indexOf(Shell.get_event_related(event)) != -1)
|
||||
return;
|
||||
|
||||
// Remember that we should not be visible to hide the button if forceShow is unset
|
||||
if (this._forceShow) {
|
||||
this._shouldHide = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this._fadeOut();
|
||||
}));
|
||||
},
|
||||
|
||||
/// Private methods ///
|
||||
|
||||
setIconFromName : function(iconName) {
|
||||
let iconTheme = Gtk.IconTheme.get_default();
|
||||
let iconInfo = iconTheme.lookup_icon(iconName, this._size, 0);
|
||||
if (!iconInfo)
|
||||
return;
|
||||
|
||||
let iconPath = iconInfo.get_filename();
|
||||
this.actor.set_from_file(iconPath);
|
||||
},
|
||||
|
||||
// Useful if we want to show the button immediately,
|
||||
// e.g. in case the mouse is already in the parent when the button is created
|
||||
show : function() {
|
||||
this.actor.set_opacity(255);
|
||||
},
|
||||
|
||||
// If show is true, prevents the button from fading out
|
||||
forceShow : function(show) {
|
||||
this._forceShow = show;
|
||||
// Hide the button if it should have been hidden under normal conditions
|
||||
if (!this._forceShow && this._shouldHide) {
|
||||
this._fadeOut();
|
||||
}
|
||||
},
|
||||
|
||||
/// Private methods ///
|
||||
|
||||
_fadeIn : function() {
|
||||
Tweener.removeTweens(this.actor);
|
||||
Tweener.addTween(this.actor, { opacity: 255,
|
||||
time: ANIMATION_TIME,
|
||||
transition :"easeInQuad" });
|
||||
},
|
||||
|
||||
_fadeOut : function() {
|
||||
Tweener.removeTweens(this.actor);
|
||||
Tweener.addTween(this.actor, { opacity: 0,
|
||||
time: ANIMATION_TIME,
|
||||
transition :"easeOutQuad" });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -126,9 +126,11 @@ Chrome.prototype = {
|
||||
//
|
||||
// Removes @actor from the chrome layer
|
||||
removeActor: function(actor) {
|
||||
this.actor.remove_actor(actor);
|
||||
// We don't have to do anything else; the parent-set handlers
|
||||
// will do the rest.
|
||||
if (actor.get_parent() == this.nonOverlayActor)
|
||||
this.nonOverlayActor.remove_actor(actor);
|
||||
else
|
||||
this.actor.remove_actor(actor);
|
||||
this._untrackActor(actor, true, true);
|
||||
},
|
||||
|
||||
_findActor: function(actor) {
|
||||
@ -170,7 +172,7 @@ Chrome.prototype = {
|
||||
this._trackedActors.push(actorData);
|
||||
|
||||
actor = actor.get_parent();
|
||||
if (actor != this.actor)
|
||||
if (actor != this.actor && actor != this.nonOverlayActor)
|
||||
this._trackActor(actor, false, false);
|
||||
|
||||
if (inputRegion || strut)
|
||||
@ -198,7 +200,7 @@ Chrome.prototype = {
|
||||
actor.disconnect(actorData.parentSetId);
|
||||
|
||||
actor = actor.get_parent();
|
||||
if (actor && actor != this.actor)
|
||||
if (actor && actor != this.actor && actor != this.nonOverlayActor)
|
||||
this._untrackActor(actor, false, false);
|
||||
}
|
||||
|
||||
@ -209,10 +211,10 @@ Chrome.prototype = {
|
||||
_actorReparented: function(actor, oldParent) {
|
||||
if (this._verifyAncestry(actor, this.actor)) {
|
||||
let newParent = actor.get_parent();
|
||||
if (newParent != this.actor)
|
||||
if (newParent != this.actor && newParent != this.nonOverlayActor)
|
||||
this._trackActor(newParent, false, false);
|
||||
}
|
||||
if (oldParent != this.actor)
|
||||
if (oldParent != this.actor && oldParent != this.nonOverlayActor)
|
||||
this._untrackActor(oldParent, false, false);
|
||||
},
|
||||
|
||||
@ -231,7 +233,8 @@ Chrome.prototype = {
|
||||
|
||||
_queueUpdateRegions: function() {
|
||||
if (!this._updateRegionIdle)
|
||||
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions));
|
||||
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
|
||||
Meta.PRIORITY_BEFORE_REDRAW);
|
||||
},
|
||||
|
||||
_windowsRestacked: function() {
|
||||
|
518
js/ui/dash.js
Normal file
518
js/ui/dash.js
Normal file
@ -0,0 +1,518 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Big = imports.gi.Big;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Lang = imports.lang;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const DocDisplay = imports.ui.docDisplay;
|
||||
const Places = imports.ui.places;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
const Button = imports.ui.button;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const DEFAULT_PADDING = 4;
|
||||
const DASH_SECTION_PADDING = 6;
|
||||
const DASH_SECTION_SPACING = 12;
|
||||
const DASH_CORNER_RADIUS = 5;
|
||||
const DASH_SEARCH_BG_COLOR = new Clutter.Color();
|
||||
DASH_SEARCH_BG_COLOR.from_pixel(0xffffffff);
|
||||
const DASH_SECTION_COLOR = new Clutter.Color();
|
||||
DASH_SECTION_COLOR.from_pixel(0x846c3dff);
|
||||
const DASH_TEXT_COLOR = new Clutter.Color();
|
||||
DASH_TEXT_COLOR.from_pixel(0xffffffff);
|
||||
|
||||
const PANE_BORDER_COLOR = new Clutter.Color();
|
||||
PANE_BORDER_COLOR.from_pixel(0x101d3cfa);
|
||||
const PANE_BORDER_WIDTH = 2;
|
||||
|
||||
const PANE_BACKGROUND_COLOR = new Clutter.Color();
|
||||
PANE_BACKGROUND_COLOR.from_pixel(0x000000f4);
|
||||
|
||||
|
||||
function Pane() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
Pane.prototype = {
|
||||
_init: function () {
|
||||
this._open = false;
|
||||
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
background_color: PANE_BACKGROUND_COLOR,
|
||||
border: PANE_BORDER_WIDTH,
|
||||
border_color: PANE_BORDER_COLOR,
|
||||
padding: DEFAULT_PADDING,
|
||||
reactive: true });
|
||||
this.actor.connect('button-press-event', Lang.bind(this, function (a, e) {
|
||||
// Eat button press events so they don't go through and close the pane
|
||||
return true;
|
||||
}));
|
||||
|
||||
let chromeTop = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
spacing: 6 });
|
||||
|
||||
let global = Shell.Global.get();
|
||||
let closeIconUri = "file://" + global.imagedir + "close.svg";
|
||||
let closeIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
||||
closeIconUri,
|
||||
16,
|
||||
16);
|
||||
closeIcon.reactive = true;
|
||||
closeIcon.connect('button-press-event', Lang.bind(this, function (b, e) {
|
||||
this.close();
|
||||
return true;
|
||||
}));
|
||||
chromeTop.append(closeIcon, Big.BoxPackFlags.END);
|
||||
this.actor.append(chromeTop, Big.BoxPackFlags.NONE);
|
||||
|
||||
this.content = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: DEFAULT_PADDING });
|
||||
this.actor.append(this.content, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
// Hidden by default
|
||||
this.actor.hide();
|
||||
},
|
||||
|
||||
open: function () {
|
||||
if (this._open)
|
||||
return;
|
||||
this._open = true;
|
||||
this.actor.show();
|
||||
this.emit('open-state-changed', this._open);
|
||||
},
|
||||
|
||||
close: function () {
|
||||
if (!this._open)
|
||||
return;
|
||||
this._open = false;
|
||||
this.actor.hide();
|
||||
this.emit('open-state-changed', this._open);
|
||||
},
|
||||
|
||||
destroyContent: function() {
|
||||
let children = this.content.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
children[i].destroy();
|
||||
}
|
||||
},
|
||||
|
||||
toggle: function () {
|
||||
if (this._open)
|
||||
this.close();
|
||||
else
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(Pane.prototype);
|
||||
|
||||
function ResultArea(displayClass, enableNavigation) {
|
||||
this._init(displayClass, enableNavigation);
|
||||
}
|
||||
|
||||
ResultArea.prototype = {
|
||||
_init : function(displayClass, enableNavigation) {
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
|
||||
this.resultsContainer = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
spacing: DEFAULT_PADDING
|
||||
});
|
||||
this.actor.append(this.resultsContainer, Big.BoxPackFlags.EXPAND);
|
||||
this.navContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
|
||||
this.resultsContainer.append(this.navContainer, Big.BoxPackFlags.NONE);
|
||||
|
||||
this.display = new displayClass();
|
||||
|
||||
this.navArea = this.display.getNavigationArea();
|
||||
if (enableNavigation && this.navArea)
|
||||
this.navContainer.append(this.navArea, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
this.resultsContainer.append(this.display.actor, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
this.controlBox = new Big.Box({ x_align: Big.BoxAlignment.CENTER });
|
||||
this.controlBox.append(this.display.displayControl, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(this.controlBox, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
this.display.load();
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function shared between ResultPane and the DocDisplay in the main dash.
|
||||
// Connects to the detail signal of the display, and on-demand creates a new
|
||||
// pane.
|
||||
function createPaneForDetails(dash, display, detailsWidth) {
|
||||
let detailPane = null;
|
||||
display.connect('show-details', Lang.bind(this, function(display, index) {
|
||||
if (detailPane == null) {
|
||||
detailPane = new Pane();
|
||||
detailPane.connect('open-state-changed', Lang.bind(this, function (pane, isOpen) {
|
||||
if (!isOpen) {
|
||||
/* Ensure we don't keep around large preview textures */
|
||||
detailPane.destroyContent();
|
||||
}
|
||||
}));
|
||||
dash._addPane(detailPane);
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
detailPane.destroyContent();
|
||||
let details = display.createDetailsForIndex(index, detailsWidth, -1);
|
||||
detailPane.content.append(details, Big.BoxPackFlags.EXPAND);
|
||||
detailPane.open();
|
||||
} else {
|
||||
detailPane.close();
|
||||
}
|
||||
}));
|
||||
return null;
|
||||
}
|
||||
|
||||
function ResultPane(dash, detailsWidth) {
|
||||
this._init(dash, detailsWidth);
|
||||
}
|
||||
|
||||
ResultPane.prototype = {
|
||||
__proto__: Pane.prototype,
|
||||
|
||||
_init: function(dash, detailsWidth) {
|
||||
Pane.prototype._init.call(this);
|
||||
this._dash = dash;
|
||||
this._detailsWidth = detailsWidth;
|
||||
},
|
||||
|
||||
// Create an instance of displayClass and pack it into this pane's
|
||||
// content area. Return the displayClass instance.
|
||||
packResults: function(displayClass, enableNavigation) {
|
||||
let resultArea = new ResultArea(displayClass, enableNavigation);
|
||||
|
||||
createPaneForDetails(this._dash, resultArea.display, this._detailsWidth);
|
||||
|
||||
this.content.append(resultArea.actor, Big.BoxPackFlags.EXPAND);
|
||||
this.connect('open-state-changed', Lang.bind(this, function(pane, isOpen) {
|
||||
resultArea.display.resetState();
|
||||
}));
|
||||
return resultArea.display;
|
||||
}
|
||||
}
|
||||
|
||||
function SearchEntry() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
SearchEntry.prototype = {
|
||||
_init : function() {
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
y_align: Big.BoxAlignment.CENTER,
|
||||
background_color: DASH_SEARCH_BG_COLOR,
|
||||
corner_radius: 4,
|
||||
spacing: DEFAULT_PADDING,
|
||||
padding: DEFAULT_PADDING
|
||||
});
|
||||
|
||||
let icon = new Gio.ThemedIcon({ name: 'gtk-find' });
|
||||
let searchIconTexture = Shell.TextureCache.get_default().load_gicon(icon, 16);
|
||||
this.actor.append(searchIconTexture, Big.BoxPackFlags.NONE);
|
||||
|
||||
this.pane = null;
|
||||
|
||||
// We need to initialize the text for the entry to have the cursor displayed
|
||||
// in it. See http://bugzilla.openedhand.com/show_bug.cgi?id=1365
|
||||
this.entry = new Clutter.Text({ font_name: "Sans 14px",
|
||||
editable: true,
|
||||
activatable: true,
|
||||
singleLineMode: true,
|
||||
text: ""
|
||||
});
|
||||
this.actor.append(this.entry, Big.BoxPackFlags.EXPAND);
|
||||
},
|
||||
|
||||
setPane: function (pane) {
|
||||
this._pane = pane;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(SearchEntry.prototype);
|
||||
|
||||
function MoreLink() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
MoreLink.prototype = {
|
||||
_init : function () {
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
padding_left: DEFAULT_PADDING,
|
||||
padding_right: DEFAULT_PADDING });
|
||||
let global = Shell.Global.get();
|
||||
let inactiveUri = "file://" + global.imagedir + "view-more.svg";
|
||||
let activeUri = "file://" + global.imagedir + "view-more-activated.svg";
|
||||
this._inactiveIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
||||
inactiveUri, 29, 18);
|
||||
this._activeIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
||||
activeUri, 29, 18);
|
||||
this._iconBox = new Big.Box({ reactive: true });
|
||||
this._iconBox.append(this._inactiveIcon, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(this._iconBox, Big.BoxPackFlags.END);
|
||||
|
||||
this.pane = null;
|
||||
|
||||
this._iconBox.connect('button-press-event', Lang.bind(this, function (b, e) {
|
||||
if (this.pane == null) {
|
||||
// Ensure the pane is created; the activated handler will call setPane
|
||||
this.emit('activated');
|
||||
}
|
||||
this._pane.toggle();
|
||||
return true;
|
||||
}));
|
||||
},
|
||||
|
||||
setPane: function (pane) {
|
||||
this._pane = pane;
|
||||
this._pane.connect('open-state-changed', Lang.bind(this, function(pane, isOpen) {
|
||||
this._iconBox.remove_all();
|
||||
this._iconBox.append(isOpen ? this._activeIcon : this._inactiveIcon,
|
||||
Big.BoxPackFlags.NONE);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Signals.addSignalMethods(MoreLink.prototype);
|
||||
|
||||
function SectionHeader(title) {
|
||||
this._init(title);
|
||||
}
|
||||
|
||||
SectionHeader.prototype = {
|
||||
_init : function (title) {
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
let text = new Clutter.Text({ color: DASH_SECTION_COLOR,
|
||||
font_name: "Sans Bold 10px",
|
||||
text: title });
|
||||
this.moreLink = new MoreLink();
|
||||
this.actor.append(text, Big.BoxPackFlags.EXPAND);
|
||||
this.actor.append(this.moreLink.actor, Big.BoxPackFlags.END);
|
||||
}
|
||||
}
|
||||
|
||||
function Dash(displayGridColumnWidth) {
|
||||
this._init(displayGridColumnWidth);
|
||||
}
|
||||
|
||||
Dash.prototype = {
|
||||
_init : function(displayGridColumnWidth) {
|
||||
this._width = displayGridColumnWidth;
|
||||
|
||||
this._detailsWidth = displayGridColumnWidth * 2;
|
||||
|
||||
let global = Shell.Global.get();
|
||||
|
||||
// dash and the popup panes need to be reactive so that the clicks in unoccupied places on them
|
||||
// are not passed to the transparent background underneath them. This background is used for the workspaces area when
|
||||
// the additional dash panes are being shown and it handles clicks by closing the additional panes, so that the user
|
||||
// can interact with the workspaces. However, this behavior is not desirable when the click is actually over a pane.
|
||||
//
|
||||
// We have to make the individual panes reactive instead of making the whole dash actor reactive because the width
|
||||
// of the Group actor ends up including the width of its hidden children, so we were getting a reactive object as
|
||||
// wide as the details pane that was blocking the clicks to the workspaces underneath it even when the details pane
|
||||
// was actually hidden.
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
width: this._width,
|
||||
padding: DEFAULT_PADDING,
|
||||
reactive: true });
|
||||
|
||||
this.dashContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: DASH_SECTION_SPACING });
|
||||
this.actor.append(this.dashContainer, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
// The currently active popup display
|
||||
this._activePane = null;
|
||||
|
||||
/***** Search *****/
|
||||
|
||||
this._searchPane = null;
|
||||
this._searchActive = false;
|
||||
this._searchEntry = new SearchEntry();
|
||||
this.dashContainer.append(this._searchEntry.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._searchAreaApps = null;
|
||||
this._searchAreaDocs = null;
|
||||
|
||||
this._searchQueued = false;
|
||||
this._searchEntry.entry.connect('text-changed', Lang.bind(this, function (se, prop) {
|
||||
this._searchActive = this._searchEntry.text != '';
|
||||
if (this._searchQueued)
|
||||
return;
|
||||
if (this._searchPane == null) {
|
||||
this._searchPane = new ResultPane(this, this._detailsWidth);
|
||||
this._searchPane.content.append(new Clutter.Text({ color: DASH_SECTION_COLOR,
|
||||
font_name: 'Sans Bold 10px',
|
||||
text: "APPLICATIONS" }),
|
||||
Big.BoxPackFlags.NONE);
|
||||
this._searchAreaApps = this._searchPane.packResults(AppDisplay.AppDisplay, false);
|
||||
this._searchPane.content.append(new Clutter.Text({ color: DASH_SECTION_COLOR,
|
||||
font_name: 'Sans Bold 10px',
|
||||
text: "RECENT DOCUMENTS" }),
|
||||
Big.BoxPackFlags.NONE);
|
||||
this._searchAreaDocs = this._searchPane.packResults(DocDisplay.DocDisplay, false);
|
||||
this._addPane(this._searchPane);
|
||||
this._searchEntry.setPane(this._searchPane);
|
||||
}
|
||||
this._searchQueued = true;
|
||||
Mainloop.timeout_add(250, Lang.bind(this, function() {
|
||||
// Strip leading and trailing whitespace
|
||||
let text = this._searchEntry.entry.text.replace(/^\s+/g, "").replace(/\s+$/g, "");
|
||||
this._searchQueued = false;
|
||||
this._searchAreaApps.setSearch(text);
|
||||
this._searchAreaDocs.setSearch(text);
|
||||
if (text == '')
|
||||
this._searchPane.close();
|
||||
else
|
||||
this._searchPane.open();
|
||||
return false;
|
||||
}));
|
||||
}));
|
||||
this._searchEntry.entry.connect('activate', Lang.bind(this, function (se) {
|
||||
// only one of the displays will have an item selected, so it's ok to
|
||||
// call activateSelected() on all of them
|
||||
this._searchAreaApps.activateSelected();
|
||||
this._searchAreaDocs.activateSelected();
|
||||
return true;
|
||||
}));
|
||||
this._searchEntry.entry.connect('key-press-event', Lang.bind(this, function (se, e) {
|
||||
let symbol = Shell.get_event_key_symbol(e);
|
||||
if (symbol == Clutter.Escape) {
|
||||
// Escape will keep clearing things back to the desktop. First, if
|
||||
// we have active text, we remove it.
|
||||
if (this._searchEntry.entry.text != '')
|
||||
this._searchEntry.entry.text = '';
|
||||
// Next, if we're in one of the "more" modes or showing the details pane, close them
|
||||
else if (this._activePane != null)
|
||||
this._activePane.close();
|
||||
// Finally, just close the overlay entirely
|
||||
else
|
||||
Main.overlay.hide();
|
||||
return true;
|
||||
} else if (symbol == Clutter.Up) {
|
||||
if (!this._searchActive)
|
||||
return true;
|
||||
// selectUp and selectDown wrap around in their respective displays
|
||||
// too, but there doesn't seem to be any flickering if we first select
|
||||
// something in one display, but then unset the selection, and move
|
||||
// it to the other display, so it's ok to do that.
|
||||
if (this._searchAreaDocs.hasSelected())
|
||||
this._searchAreaDocs.selectUp();
|
||||
else if (this._searchAreaApps.hasItems())
|
||||
this._searchAreaApps.selectUp();
|
||||
else
|
||||
this._searchAreaDocs.selectUp();
|
||||
return true;
|
||||
} else if (symbol == Clutter.Down) {
|
||||
if (!this._searchActive)
|
||||
return true;
|
||||
if (this._searchAreaDocs.hasSelected())
|
||||
this._searchAreaDocs.selectDown();
|
||||
else if (this._searchAreaApps.hasItems())
|
||||
this._searchAreaApps.selectDown();
|
||||
else
|
||||
this._searchAreaDocs.selectDown();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
|
||||
/***** Applications *****/
|
||||
|
||||
let appsHeader = new SectionHeader("APPLICATIONS");
|
||||
this._appsSection = new Big.Box({ spacing: DEFAULT_PADDING });
|
||||
this._appsSection.append(appsHeader.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._appsContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
this._appsSection.append(this._appsContent, Big.BoxPackFlags.EXPAND);
|
||||
this._appWell = new AppDisplay.AppWell();
|
||||
this._appsContent.append(this._appWell.actor, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
this._moreAppsPane = null;
|
||||
appsHeader.moreLink.connect('activated', Lang.bind(this, function (link) {
|
||||
if (this._moreAppsPane == null) {
|
||||
this._moreAppsPane = new ResultPane(this, this._detailsWidth);
|
||||
this._moreAppsPane.packResults(AppDisplay.AppDisplay, true);
|
||||
this._addPane(this._moreAppsPane);
|
||||
link.setPane(this._moreAppsPane);
|
||||
}
|
||||
}));
|
||||
|
||||
this.dashContainer.append(this._appsSection, Big.BoxPackFlags.NONE);
|
||||
|
||||
/***** Places *****/
|
||||
|
||||
let placesSection = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: DEFAULT_PADDING });
|
||||
let placesHeader = new SectionHeader("PLACES");
|
||||
placesSection.append(placesHeader.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
let placesDisplay = new Places.Places();
|
||||
placesSection.append(placesDisplay.actor, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
this.dashContainer.append(placesSection, Big.BoxPackFlags.NONE);
|
||||
|
||||
/***** Documents *****/
|
||||
|
||||
this._docsSection = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: DEFAULT_PADDING });
|
||||
this._moreDocsPane = null;
|
||||
|
||||
let docsHeader = new SectionHeader("RECENT DOCUMENTS");
|
||||
this._docsSection.append(docsHeader.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._docDisplay = new DocDisplay.DocDisplay();
|
||||
this._docDisplay.load();
|
||||
this._docsSection.append(this._docDisplay.actor, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
createPaneForDetails(this, this._docDisplay, this._detailsWidth);
|
||||
|
||||
docsHeader.moreLink.connect('activated', Lang.bind(this, function (link) {
|
||||
if (this._moreDocsPane == null) {
|
||||
this._moreDocsPane = new ResultPane(this, this._detailsWidth);
|
||||
this._moreDocsPane.packResults(DocDisplay.DocDisplay, true);
|
||||
this._addPane(this._moreDocsPane);
|
||||
link.setPane(this._moreDocsPane);
|
||||
}
|
||||
}));
|
||||
|
||||
this.dashContainer.append(this._docsSection, Big.BoxPackFlags.EXPAND);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
let global = Shell.Global.get();
|
||||
global.stage.set_key_focus(this._searchEntry.entry);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this._firstSelectAfterOverlayShow = true;
|
||||
if (this._searchEntry.entry.text != '')
|
||||
this._searchEntry.entry.text = '';
|
||||
if (this._activePane != null)
|
||||
this._activePane.close();
|
||||
},
|
||||
|
||||
closePanes: function () {
|
||||
if (this._activePane != null)
|
||||
this._activePane.close();
|
||||
},
|
||||
|
||||
_addPane: function(pane) {
|
||||
pane.connect('open-state-changed', Lang.bind(this, function (pane, isOpen) {
|
||||
if (isOpen) {
|
||||
if (pane != this._activePane && this._activePane != null) {
|
||||
this._activePane.close();
|
||||
}
|
||||
this._activePane = pane;
|
||||
} else if (pane == this._activePane) {
|
||||
this._activePane = null;
|
||||
}
|
||||
}));
|
||||
Main.overlay.addPane(pane);
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(Dash.prototype);
|
19
js/ui/dnd.js
19
js/ui/dnd.js
@ -133,8 +133,8 @@ _Draggable.prototype = {
|
||||
// We can check the return value of the function and break the loop if it's true if we don't want
|
||||
// to continue checking the parents.
|
||||
target._delegate.handleDragOver(this.actor._delegate, actor,
|
||||
(stageX + this._dragOffsetX + this._xOffset - targX) / target.scale_x,
|
||||
(stageY + this._dragOffsetY + this._yOffset - targY) / target.scale_y,
|
||||
(stageX + this._dragOffsetX - targX) / target.scale_x,
|
||||
(stageY + this._dragOffsetY - targY) / target.scale_y,
|
||||
event.get_time());
|
||||
}
|
||||
target = target.get_parent();
|
||||
@ -153,8 +153,6 @@ _Draggable.prototype = {
|
||||
if (!dragging)
|
||||
return false;
|
||||
|
||||
this.emit('drag-end', event.get_time());
|
||||
|
||||
// Find a drop target
|
||||
actor.hide();
|
||||
let [dropX, dropY] = event.get_coords();
|
||||
@ -165,14 +163,15 @@ _Draggable.prototype = {
|
||||
if (target._delegate && target._delegate.acceptDrop) {
|
||||
let [targX, targY] = target.get_transformed_position();
|
||||
if (target._delegate.acceptDrop(this.actor._delegate, actor,
|
||||
(dropX + this._xOffset - targX) / target.scale_x,
|
||||
(dropY + this._yOffset - targY) / target.scale_y,
|
||||
(dropX - targX) / target.scale_x,
|
||||
(dropY - targY) / target.scale_y,
|
||||
event.get_time())) {
|
||||
// If it accepted the drop without taking the actor,
|
||||
// destroy it.
|
||||
if (actor.get_parent() == actor.get_stage())
|
||||
actor.destroy();
|
||||
|
||||
this.emit('drag-end', event.get_time(), true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -196,18 +195,20 @@ _Draggable.prototype = {
|
||||
transition: "easeOutQuad",
|
||||
onComplete: this._onSnapBackComplete,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [actor]
|
||||
onCompleteParams: [actor, event.get_time()]
|
||||
});
|
||||
return true;
|
||||
},
|
||||
|
||||
_onSnapBackComplete : function (dragActor) {
|
||||
_onSnapBackComplete : function (dragActor, eventTime) {
|
||||
if (this._dragOrigParent) {
|
||||
dragActor.reparent(this._dragOrigParent);
|
||||
dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
|
||||
dragActor.set_position(this._dragOrigX, this._dragOrigY);
|
||||
} else
|
||||
} else {
|
||||
dragActor.destroy();
|
||||
}
|
||||
this.emit('drag-end', eventTime, false);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -6,33 +6,47 @@ const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
const DocInfo = imports.misc.docInfo;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
/* 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
|
||||
* availableWidth - total width available for the item
|
||||
* currentSeconds - current number of seconds since the epoch
|
||||
*/
|
||||
function DocDisplayItem(docInfo, availableWidth) {
|
||||
this._init(docInfo, availableWidth);
|
||||
function DocDisplayItem(docInfo, currentSecs) {
|
||||
this._init(docInfo, currentSecs);
|
||||
}
|
||||
|
||||
DocDisplayItem.prototype = {
|
||||
__proto__: GenericDisplay.GenericDisplayItem.prototype,
|
||||
|
||||
_init : function(docInfo, availableWidth) {
|
||||
GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
|
||||
_init : function(docInfo, currentSecs) {
|
||||
GenericDisplay.GenericDisplayItem.prototype._init.call(this);
|
||||
this._docInfo = docInfo;
|
||||
|
||||
this._setItemInfo(docInfo.name, "",
|
||||
docInfo.getIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE));
|
||||
|
||||
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.
|
||||
@ -42,10 +56,14 @@ DocDisplayItem.prototype = {
|
||||
|
||||
//// Protected method overrides ////
|
||||
|
||||
// Ensures the preview icon is created.
|
||||
_ensurePreviewIconCreated : function() {
|
||||
if (!this._previewIcon)
|
||||
this._previewIcon = this._docInfo.getIcon(GenericDisplay.PREVIEW_ICON_SIZE);
|
||||
// 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
|
||||
@ -54,54 +72,68 @@ DocDisplayItem.prototype = {
|
||||
if (this._docInfo.mimeType == null || this._docInfo.mimeType.indexOf("image/") != 0)
|
||||
return null;
|
||||
|
||||
return Shell.TextureCache.get_default().load_uri_sync(this._docInfo.uri, availableWidth, availableHeight);
|
||||
return Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.NONE,
|
||||
this._docInfo.uri, availableWidth, availableHeight);
|
||||
},
|
||||
|
||||
//// 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] = Shell.Global.get().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.
|
||||
*
|
||||
* width - width available for the display
|
||||
* height - height available for the display
|
||||
*/
|
||||
function DocDisplay(width, height, numberOfColumns, columnGap) {
|
||||
this._init(width, height, numberOfColumns, columnGap);
|
||||
}
|
||||
function DocDisplay() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DocDisplay.prototype = {
|
||||
__proto__: GenericDisplay.GenericDisplay.prototype,
|
||||
|
||||
_init : function(width, height, numberOfColumns, columnGap) {
|
||||
GenericDisplay.GenericDisplay.prototype._init.call(this, width, height, numberOfColumns, columnGap);
|
||||
_init : function() {
|
||||
GenericDisplay.GenericDisplay.prototype._init.call(this);
|
||||
let me = this;
|
||||
this._recentManager = Gtk.RecentManager.get_default();
|
||||
|
||||
// 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(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
|
||||
this._docsStale = true;
|
||||
this._recentManager.connect('changed', function(recentManager, userData) {
|
||||
this._docManager.connect('changed', function(mgr, userData) {
|
||||
me._docsStale = true;
|
||||
// Changes in local recent files should not happen when we are in the overlay 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.
|
||||
me._redisplay(false);
|
||||
me._redisplay(false);
|
||||
});
|
||||
|
||||
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() {
|
||||
let me = this;
|
||||
if (!this._docsStale)
|
||||
return;
|
||||
this._allItems = {};
|
||||
let docs = this._recentManager.get_items();
|
||||
for (let i = 0; i < docs.length; i++) {
|
||||
let recentInfo = docs[i];
|
||||
let docInfo = new DocInfo.DocInfo(recentInfo);
|
||||
|
||||
// we use GtkRecentInfo URI as an item Id
|
||||
this._allItems[docInfo.uri] = docInfo;
|
||||
}
|
||||
this._allItems = this._docManager.getItems();
|
||||
this._docsStale = false;
|
||||
},
|
||||
|
||||
@ -145,7 +177,7 @@ DocDisplay.prototype = {
|
||||
let docA = this._allItems[itemIdA];
|
||||
let docB = this._allItems[itemIdB];
|
||||
|
||||
return docB.lastVisited() - docA.lastVisited();
|
||||
return docB.timestamp - docA.timestamp;
|
||||
},
|
||||
|
||||
// Checks if the item info can be a match for the search string by checking
|
||||
@ -168,8 +200,39 @@ DocDisplay.prototype = {
|
||||
|
||||
// Creates a DocDisplayItem based on itemInfo, which is expected to be a DocInfo object.
|
||||
_createDisplayItem: function(itemInfo) {
|
||||
return new DocDisplayItem(itemInfo, this._columnWidth);
|
||||
}
|
||||
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);
|
||||
|
@ -12,8 +12,10 @@ const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Tidy = imports.gi.Tidy;
|
||||
|
||||
const Button = imports.ui.button;
|
||||
const DND = imports.ui.dnd;
|
||||
const Link = imports.ui.link;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const ITEM_DISPLAY_NAME_COLOR = new Clutter.Color();
|
||||
ITEM_DISPLAY_NAME_COLOR.from_pixel(0xffffffff);
|
||||
@ -22,76 +24,110 @@ ITEM_DISPLAY_DESCRIPTION_COLOR.from_pixel(0xffffffbb);
|
||||
const ITEM_DISPLAY_BACKGROUND_COLOR = new Clutter.Color();
|
||||
ITEM_DISPLAY_BACKGROUND_COLOR.from_pixel(0x00000000);
|
||||
const ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR = new Clutter.Color();
|
||||
ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR.from_pixel(0x00ff0055);
|
||||
ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR.from_pixel(0x4f6fadaa);
|
||||
const DISPLAY_CONTROL_SELECTED_COLOR = new Clutter.Color();
|
||||
DISPLAY_CONTROL_SELECTED_COLOR.from_pixel(0x112288ff);
|
||||
const PREVIEW_BOX_BACKGROUND_COLOR = new Clutter.Color();
|
||||
PREVIEW_BOX_BACKGROUND_COLOR.from_pixel(0xADADADf0);
|
||||
|
||||
const DEFAULT_PADDING = 4;
|
||||
|
||||
const ITEM_DISPLAY_HEIGHT = 50;
|
||||
const ITEM_DISPLAY_ICON_SIZE = 48;
|
||||
const ITEM_DISPLAY_PADDING = 1;
|
||||
const ITEM_DISPLAY_PADDING_RIGHT = 2;
|
||||
const DEFAULT_COLUMN_GAP = 6;
|
||||
const LABEL_HEIGHT = 16;
|
||||
|
||||
const PREVIEW_ICON_SIZE = 96;
|
||||
const PREVIEW_BOX_PADDING = 6;
|
||||
const PREVIEW_BOX_SPACING = 4;
|
||||
const PREVIEW_BOX_CORNER_RADIUS = 10;
|
||||
const PREVIEW_BOX_SPACING = DEFAULT_PADDING;
|
||||
const PREVIEW_BOX_CORNER_RADIUS = 10;
|
||||
// how far relative to the full item width the preview box should be placed
|
||||
const PREVIEW_PLACING = 3/4;
|
||||
const PREVIEW_DETAILS_MIN_WIDTH = PREVIEW_ICON_SIZE * 2;
|
||||
|
||||
const INFORMATION_BUTTON_SIZE = 16;
|
||||
|
||||
/* 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
|
||||
* 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.
|
||||
*
|
||||
* availableWidth - total width available for the item
|
||||
*/
|
||||
function GenericDisplayItem(availableWidth) {
|
||||
this._init(availableWidth);
|
||||
function GenericDisplayItem() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
GenericDisplayItem.prototype = {
|
||||
_init: function(availableWidth) {
|
||||
this._availableWidth = availableWidth;
|
||||
this._showPreview = false;
|
||||
this._havePointer = false;
|
||||
this._previewEventSourceId = null;
|
||||
_init: function() {
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
spacing: ITEM_DISPLAY_PADDING,
|
||||
reactive: true,
|
||||
background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
|
||||
corner_radius: 4,
|
||||
height: ITEM_DISPLAY_HEIGHT });
|
||||
|
||||
this.actor = new Clutter.Group({ reactive: true,
|
||||
width: availableWidth,
|
||||
height: ITEM_DISPLAY_HEIGHT });
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('button-press-event',
|
||||
this.actor.connect('button-release-event',
|
||||
Lang.bind(this,
|
||||
function(actor, e) {
|
||||
let clickCount = Shell.get_button_event_click_count(e);
|
||||
if (clickCount == 1)
|
||||
this.select();
|
||||
else if (clickCount == 2)
|
||||
this.activate();
|
||||
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, this._onDragBegin));
|
||||
|
||||
this._bg = new Big.Box({ background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
|
||||
corner_radius: 4,
|
||||
x: 0, y: 0,
|
||||
width: availableWidth, height: ITEM_DISPLAY_HEIGHT });
|
||||
this.actor.add_actor(this._bg);
|
||||
|
||||
this._infoContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
spacing: DEFAULT_PADDING });
|
||||
this.actor.append(this._infoContent, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
this._iconBox = new Big.Box();
|
||||
this._infoContent.append(this._iconBox, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._infoText = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: DEFAULT_PADDING });
|
||||
this._infoContent.append(this._infoText, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
let global = Shell.Global.get();
|
||||
let infoIconUri = "file://" + global.imagedir + "info.svg";
|
||||
let infoIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
||||
infoIconUri,
|
||||
INFORMATION_BUTTON_SIZE,
|
||||
INFORMATION_BUTTON_SIZE);
|
||||
this._informationButton = new Button.iconButton(this.actor, INFORMATION_BUTTON_SIZE, infoIcon);
|
||||
let buttonBox = new Big.Box({ width: INFORMATION_BUTTON_SIZE + 2 * DEFAULT_PADDING,
|
||||
height: INFORMATION_BUTTON_SIZE,
|
||||
padding_left: DEFAULT_PADDING, padding_right: DEFAULT_PADDING,
|
||||
y_align: Big.BoxAlignment.CENTER });
|
||||
buttonBox.append(this._informationButton.actor, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(buttonBox, Big.BoxPackFlags.END);
|
||||
|
||||
// Connecting to the button-press-event for the information button ensures that the actor,
|
||||
// which is a draggable actor, does not get the button-press-event and doesn't initiate
|
||||
// the dragging, which then prevents us from getting the button-release-event for the button.
|
||||
this._informationButton.actor.connect('button-press-event',
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
return true;
|
||||
}));
|
||||
this._informationButton.actor.connect('button-release-event',
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
// Selects the item by highlighting it and displaying its details
|
||||
this.emit('show-details');
|
||||
return true;
|
||||
}));
|
||||
|
||||
this._name = null;
|
||||
this._description = null;
|
||||
this._icon = null;
|
||||
this._preview = null;
|
||||
this._previewIcon = null;
|
||||
|
||||
// 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 = [];
|
||||
|
||||
this.dragActor = null;
|
||||
|
||||
this.actor.connect('enter-event', Lang.bind(this, this._onEnter));
|
||||
this.actor.connect('leave-event', Lang.bind(this, this._onLeave));
|
||||
},
|
||||
|
||||
//// Draggable object interface ////
|
||||
@ -99,8 +135,7 @@ GenericDisplayItem.prototype = {
|
||||
// Returns a cloned texture of the item's icon to represent the item as it
|
||||
// is being dragged.
|
||||
getDragActor: function(stageX, stageY) {
|
||||
this.dragActor = new Clutter.Clone({ source: this._icon });
|
||||
[this.dragActor.width, this.dragActor.height] = this._icon.get_transformed_size();
|
||||
this.dragActor = this._createIcon();
|
||||
|
||||
// If the user dragged from the icon itself, then position
|
||||
// the dragActor over the original icon. Otherwise center it
|
||||
@ -115,90 +150,43 @@ GenericDisplayItem.prototype = {
|
||||
return this.dragActor;
|
||||
},
|
||||
|
||||
// Returns the original icon that is being used as a source for the cloned texture
|
||||
// that represents the item as it is being dragged.
|
||||
// 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 ////
|
||||
|
||||
// Sets a boolean value that indicates whether the item should display a pop-up preview on mouse over.
|
||||
setShowPreview: function(showPreview) {
|
||||
this._showPreview = showPreview;
|
||||
},
|
||||
|
||||
// Returns a boolean value that indicates whether the item displays a pop-up preview on mouse over.
|
||||
getShowPreview: function() {
|
||||
return this._showPreview;
|
||||
},
|
||||
|
||||
// Displays the preview for the item.
|
||||
showPreview: function() {
|
||||
if(!this._showPreview)
|
||||
return;
|
||||
|
||||
this._ensurePreviewCreated();
|
||||
|
||||
let [x, y] = this.actor.get_transformed_position();
|
||||
let global = Shell.Global.get();
|
||||
let previewX = Math.min(x + this._availableWidth * PREVIEW_PLACING, global.screen_width - this._preview.width);
|
||||
let previewY = Math.min(y, global.screen_height - this._preview.height);
|
||||
this._preview.set_position(previewX, previewY);
|
||||
|
||||
this._preview.show();
|
||||
},
|
||||
|
||||
// Hides the preview for the item and removes the preview event source so that
|
||||
// there is no preview scheduled to show up.
|
||||
hidePreview: function() {
|
||||
if (this._previewEventSourceId) {
|
||||
Mainloop.source_remove(this._previewEventSourceId);
|
||||
this._previewEventSourceId = null;
|
||||
}
|
||||
|
||||
if (this._preview)
|
||||
this._preview.hide();
|
||||
},
|
||||
|
||||
// Shows a preview when the item was drawn under the mouse pointer.
|
||||
// Shows the information button when the item was drawn under the mouse pointer.
|
||||
onDrawnUnderPointer: function() {
|
||||
this._havePointer = true;
|
||||
// This code is usually triggered when we just had a different preview showing on the same spot
|
||||
// and having a delay before showing a new preview looks bad. So we just show it right away.
|
||||
this.showPreview();
|
||||
this._informationButton.show();
|
||||
},
|
||||
|
||||
// Highlights the item by setting a different background color than the default
|
||||
// if isSelected is true, removes the highlighting otherwise.
|
||||
markSelected: function(isSelected) {
|
||||
let color;
|
||||
if (isSelected)
|
||||
if (isSelected) {
|
||||
color = ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR;
|
||||
else
|
||||
this._informationButton.forceShow(true);
|
||||
}
|
||||
else {
|
||||
color = ITEM_DISPLAY_BACKGROUND_COLOR;
|
||||
this._bg.background_color = color;
|
||||
},
|
||||
|
||||
// Activates the item, as though it was launched
|
||||
activate: function() {
|
||||
this.hidePreview();
|
||||
this.emit('activate');
|
||||
},
|
||||
|
||||
// Selects the item, as though it was clicked
|
||||
select: function() {
|
||||
this.emit('select');
|
||||
this._informationButton.forceShow(false);
|
||||
}
|
||||
this.actor.background_color = color;
|
||||
},
|
||||
|
||||
/*
|
||||
* Returns an actor containing item details. In the future details can have more information than what
|
||||
* 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.
|
||||
*
|
||||
* availableWidth - width available for displaying details
|
||||
* availableHeight - height available for displaying details
|
||||
*/
|
||||
createDetailsActor: function(availableWidth, availableHeight) {
|
||||
*/
|
||||
createDetailsActor: function(availableWidth) {
|
||||
|
||||
let details = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PREVIEW_BOX_SPACING,
|
||||
@ -214,7 +202,7 @@ GenericDisplayItem.prototype = {
|
||||
let detailsName = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
|
||||
font_name: "Sans bold 14px",
|
||||
line_wrap: true,
|
||||
text: this._name.text});
|
||||
text: this._name.text });
|
||||
textDetails.append(detailsName, Big.BoxPackFlags.NONE);
|
||||
|
||||
let detailsDescription = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
|
||||
@ -223,14 +211,15 @@ GenericDisplayItem.prototype = {
|
||||
text: this._description.text });
|
||||
textDetails.append(detailsDescription, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._detailsDescriptions.push(detailsDescription);
|
||||
|
||||
mainDetails.append(textDetails, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
this._ensurePreviewIconCreated();
|
||||
let largePreviewIcon = this._createLargePreviewIcon(availableWidth, Math.max(0, availableHeight - mainDetails.height - PREVIEW_BOX_SPACING));
|
||||
let previewIcon = this._createPreviewIcon();
|
||||
let largePreviewIcon = this._createLargePreviewIcon(availableWidth, -1);
|
||||
|
||||
if (this._previewIcon != null && largePreviewIcon == null) {
|
||||
let previewIconClone = new Clutter.Clone({ source: this._previewIcon });
|
||||
mainDetails.prepend(previewIconClone, Big.BoxPackFlags.NONE);
|
||||
if (previewIcon != null && largePreviewIcon == null) {
|
||||
mainDetails.prepend(previewIcon, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
|
||||
details.append(mainDetails, Big.BoxPackFlags.NONE);
|
||||
@ -241,32 +230,16 @@ GenericDisplayItem.prototype = {
|
||||
details.append(largePreview, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
|
||||
// We hide the preview pop-up if the details are shown elsewhere.
|
||||
details.connect("show",
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
// Right now "show" signal is emitted when an actor is added to a parent that
|
||||
// has not been added to anything and "visible" property is also set to true
|
||||
// at this point, so checking if the parent that the actor has been added to
|
||||
// has a parent of its own is a temporary workaround. That other actor is
|
||||
// presumed to be displayed, which is a limitation of this workaround, but is
|
||||
// the case with our usage of the details actor now.
|
||||
// http://bugzilla.openedhand.com/show_bug.cgi?id=1138
|
||||
if (details.get_parent() != null && details.get_parent().get_parent() != null)
|
||||
this.hidePreview();
|
||||
}));
|
||||
return details;
|
||||
},
|
||||
|
||||
// Destoys the item, as well as a preview for the item if it exists.
|
||||
// Destroys the item.
|
||||
destroy: function() {
|
||||
this.actor.destroy();
|
||||
if (this._preview != null)
|
||||
this._preview.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");
|
||||
@ -279,9 +252,8 @@ GenericDisplayItem.prototype = {
|
||||
*
|
||||
* nameText - name of the item
|
||||
* descriptionText - short description of the item
|
||||
* iconActor - ClutterTexture containing the icon image which should be ITEM_DISPLAY_ICON_SIZE size
|
||||
*/
|
||||
_setItemInfo: function(nameText, descriptionText, iconActor) {
|
||||
_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
|
||||
@ -297,37 +269,35 @@ GenericDisplayItem.prototype = {
|
||||
// and therefore should be responsible for distroying it
|
||||
this._icon.destroy();
|
||||
this._icon = null;
|
||||
}
|
||||
// This ensures we'll create a new preview and previewIcon next time we need a preview
|
||||
if (this._preview != null) {
|
||||
this._preview.destroy();
|
||||
this._preview = null;
|
||||
}
|
||||
if (this._previewIcon != null) {
|
||||
this._previewIcon.destroy();
|
||||
this._previewIcon = null;
|
||||
}
|
||||
|
||||
this._icon = iconActor;
|
||||
this.actor.add_actor(this._icon);
|
||||
this._icon = this._createIcon();
|
||||
this._iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
||||
|
||||
let textWidth = this._availableWidth - (ITEM_DISPLAY_ICON_SIZE + 4);
|
||||
this._name = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
|
||||
font_name: "Sans 14px",
|
||||
width: textWidth,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
text: nameText,
|
||||
x: ITEM_DISPLAY_ICON_SIZE + 4,
|
||||
y: ITEM_DISPLAY_PADDING });
|
||||
this.actor.add_actor(this._name);
|
||||
text: nameText });
|
||||
this._infoText.append(this._name, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
this._description = new Clutter.Text({ color: ITEM_DISPLAY_DESCRIPTION_COLOR,
|
||||
font_name: "Sans 12px",
|
||||
width: textWidth,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
text: descriptionText ? descriptionText : "",
|
||||
x: this._name.x,
|
||||
y: this._name.height + 4 });
|
||||
this.actor.add_actor(this._description);
|
||||
text: descriptionText ? descriptionText : ""
|
||||
});
|
||||
this._infoText.append(this._description, Big.BoxPackFlags.EXPAND);
|
||||
},
|
||||
|
||||
// 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 ////
|
||||
@ -339,86 +309,23 @@ GenericDisplayItem.prototype = {
|
||||
|
||||
//// Pure virtual protected methods ////
|
||||
|
||||
// Ensures the preview icon is created.
|
||||
_ensurePreviewIconCreated: function() {
|
||||
// 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 ////
|
||||
|
||||
// Ensures the preview actor is created.
|
||||
_ensurePreviewCreated: function() {
|
||||
if (!this._showPreview || this._preview)
|
||||
return;
|
||||
|
||||
this._preview = new Big.Box({ background_color: PREVIEW_BOX_BACKGROUND_COLOR,
|
||||
orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
corner_radius: PREVIEW_BOX_CORNER_RADIUS,
|
||||
padding: PREVIEW_BOX_PADDING,
|
||||
spacing: PREVIEW_BOX_SPACING });
|
||||
|
||||
let textDetailsWidth = this._availableWidth - PREVIEW_BOX_PADDING * 2;
|
||||
|
||||
this._ensurePreviewIconCreated();
|
||||
|
||||
if (this._previewIcon != null) {
|
||||
this._preview.append(this._previewIcon, Big.BoxPackFlags.EXPAND);
|
||||
textDetailsWidth = this._availableWidth - this._previewIcon.width - PREVIEW_BOX_PADDING * 2 - PREVIEW_BOX_SPACING;
|
||||
}
|
||||
|
||||
// Inner box with name and description
|
||||
let textDetails = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PREVIEW_BOX_SPACING });
|
||||
let detailsName = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
|
||||
font_name: "Sans bold 14px",
|
||||
text: this._name.text});
|
||||
|
||||
textDetails.width = Math.max(PREVIEW_DETAILS_MIN_WIDTH, textDetailsWidth, detailsName.width);
|
||||
|
||||
textDetails.append(detailsName, Big.BoxPackFlags.NONE);
|
||||
|
||||
let detailsDescription = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
|
||||
font_name: "Sans 14px",
|
||||
line_wrap: true,
|
||||
text: this._description.text });
|
||||
textDetails.append(detailsDescription, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._preview.append(textDetails, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
// Add the preview to global stage to allow for top-level layering
|
||||
let global = Shell.Global.get();
|
||||
global.stage.add_actor(this._preview);
|
||||
this._preview.hide();
|
||||
},
|
||||
|
||||
// Performs actions on mouse enter event for the item. Currently, shows the preview for the item.
|
||||
_onEnter: function(actor, event) {
|
||||
this._havePointer = true;
|
||||
let tooltipTimeout = Gtk.Settings.get_default().gtk_tooltip_timeout;
|
||||
this._previewEventSourceId = Mainloop.timeout_add(tooltipTimeout,
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
if (this._havePointer) {
|
||||
this.showPreview();
|
||||
}
|
||||
this._previewEventSourceId = null;
|
||||
return false;
|
||||
}));
|
||||
},
|
||||
|
||||
// Performs actions on mouse leave event for the item. Currently, hides the preview for the item.
|
||||
_onLeave: function(actor, event) {
|
||||
this._havePointer = false;
|
||||
this.hidePreview();
|
||||
},
|
||||
|
||||
// Hides the preview once the item starts being dragged.
|
||||
// Hides the information button once the item starts being dragged.
|
||||
_onDragBegin : function (draggable, time) {
|
||||
// For some reason, we are not getting leave-event signal when we are dragging an item,
|
||||
// so the preview box stays behind if we didn't have the call here. It makes sense to hide
|
||||
// the preview as soon as the item starts being dragged anyway.
|
||||
this._havePointer = false;
|
||||
this.hidePreview();
|
||||
// so we should remove the link manually.
|
||||
this._informationButton.actor.hide();
|
||||
}
|
||||
};
|
||||
|
||||
@ -426,84 +333,60 @@ 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.
|
||||
*
|
||||
* width - width available for the display
|
||||
* height - height available for the display
|
||||
*/
|
||||
function GenericDisplay(width, height, numberOfColumns, columnGap) {
|
||||
this._init(width, height, numberOfColumns, columnGap);
|
||||
function GenericDisplay() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
GenericDisplay.prototype = {
|
||||
_init : function(width, height, numberOfColumns, columnGap) {
|
||||
_init : function() {
|
||||
this._search = '';
|
||||
this._expanded = false;
|
||||
this._width = null;
|
||||
this._height = null;
|
||||
this._columnWidth = null;
|
||||
|
||||
this._numberOfColumns = numberOfColumns;
|
||||
this._columnGap = columnGap;
|
||||
if (this._columnGap == null)
|
||||
this._columnGap = DEFAULT_COLUMN_GAP;
|
||||
|
||||
this._maxItemsPerPage = null;
|
||||
this._grid = new Tidy.Grid({width: this._width, height: this._height});
|
||||
this._list = new Shell.OverflowList({ spacing: 6.0,
|
||||
item_height: ITEM_DISPLAY_HEIGHT });
|
||||
|
||||
this._setDimensionsAndMaxItems(width, 0, height);
|
||||
this._list.connect('notify::n-pages', Lang.bind(this, function () {
|
||||
this._updateDisplayControl(true);
|
||||
}));
|
||||
this._list.connect('notify::page', Lang.bind(this, function () {
|
||||
this._updateDisplayControl(false);
|
||||
}));
|
||||
|
||||
this._grid.column_major = true;
|
||||
this._grid.column_gap = this._columnGap;
|
||||
// map<itemId, Object> where Object represents the item info
|
||||
this._allItems = {};
|
||||
// an array of itemIds of items that match the current request
|
||||
this._allItems = {};
|
||||
// an array of itemIds of items that match the current request
|
||||
// in the order in which the items should be displayed
|
||||
this._matchedItems = [];
|
||||
// map<itemId, GenericDisplayItem>
|
||||
this._displayedItems = {};
|
||||
this._displayedItemsCount = 0;
|
||||
this._pageDisplayed = 0;
|
||||
this._displayedItems = {};
|
||||
this._openDetailIndex = -1;
|
||||
this._selectedIndex = -1;
|
||||
// These two are public - .actor is the normal "actor subclass" property,
|
||||
// but we also expose a .displayControl actor which is separate.
|
||||
// See also getSideArea.
|
||||
this.actor = this._grid;
|
||||
// See also getNavigationArea.
|
||||
this.actor = this._list;
|
||||
this.displayControl = new Big.Box({ background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
|
||||
corner_radius: 4,
|
||||
height: 24,
|
||||
spacing: 12,
|
||||
orientation: Big.BoxOrientation.HORIZONTAL});
|
||||
|
||||
this._availableWidthForItemDetails = this._columnWidth;
|
||||
this._availableHeightForItemDetails = this._height;
|
||||
this.selectedItemDetails = new Big.Box({});
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
|
||||
// Sets dimensions available for the item details display.
|
||||
setAvailableDimensionsForItemDetails: function(availableWidth, availableHeight) {
|
||||
this._availableWidthForItemDetails = availableWidth;
|
||||
this._availableHeightForItemDetails = availableHeight;
|
||||
},
|
||||
|
||||
// Returns dimensions available for the item details display.
|
||||
getAvailableDimensionsForItemDetails: function() {
|
||||
return [this._availableWidthForItemDetails, this._availableHeightForItemDetails];
|
||||
},
|
||||
|
||||
// Sets the search string and displays the matching items.
|
||||
setSearch: function(text) {
|
||||
this._search = text.toLowerCase();
|
||||
this._redisplay(true);
|
||||
},
|
||||
|
||||
// Launches the item that is currently selected and emits 'activated' signal.
|
||||
// Launches the item that is currently selected, closing the overlay
|
||||
activateSelected: function() {
|
||||
if (this._selectedIndex != -1) {
|
||||
let selected = this._findDisplayedByIndex(this._selectedIndex);
|
||||
selected.launch()
|
||||
this.emit('activated');
|
||||
selected.launch();
|
||||
this.unsetSelected();
|
||||
Main.overlay.hide();
|
||||
}
|
||||
},
|
||||
|
||||
@ -511,10 +394,11 @@ GenericDisplay.prototype = {
|
||||
// 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._list.displayedCount;
|
||||
let selectedUp = true;
|
||||
let prev = this._selectedIndex - 1;
|
||||
if (this._selectedIndex <= 0) {
|
||||
prev = this._displayedItemsCount - 1;
|
||||
prev = count - 1;
|
||||
selectedUp = false;
|
||||
}
|
||||
this._selectIndex(prev);
|
||||
@ -525,9 +409,10 @@ GenericDisplay.prototype = {
|
||||
// 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._list.displayedCount;
|
||||
let selectedDown = true;
|
||||
let next = this._selectedIndex + 1;
|
||||
if (this._selectedIndex == this._displayedItemsCount - 1) {
|
||||
if (this._selectedIndex == count - 1) {
|
||||
next = 0;
|
||||
selectedDown = false;
|
||||
}
|
||||
@ -543,8 +428,9 @@ GenericDisplay.prototype = {
|
||||
|
||||
// Selects the last item among the displayed items.
|
||||
selectLastItem: function() {
|
||||
let count = this._list.displayedCount;
|
||||
if (this.hasItems())
|
||||
this._selectIndex(this._displayedItemsCount - 1);
|
||||
this._selectIndex(count - 1);
|
||||
},
|
||||
|
||||
// Returns true if the display has some item selected.
|
||||
@ -557,58 +443,33 @@ GenericDisplay.prototype = {
|
||||
this._selectIndex(-1);
|
||||
},
|
||||
|
||||
// Hides the preview if any item has one being displayed.
|
||||
hidePreview: function() {
|
||||
for (itemId in this._displayedItems) {
|
||||
let item = this._displayedItems[itemId];
|
||||
item.hidePreview();
|
||||
}
|
||||
},
|
||||
|
||||
// Returns true if the display has any displayed items.
|
||||
hasItems: function() {
|
||||
return this._displayedItemsCount > 0;
|
||||
return this._list.displayedCount > 0;
|
||||
},
|
||||
|
||||
// Readjusts display layout and the items displayed based on the new dimensions.
|
||||
setExpanded: function(expanded, baseWidth, expandWidth, height, numberOfColumns) {
|
||||
this._expanded = expanded;
|
||||
this._numberOfColumns = numberOfColumns;
|
||||
this._setDimensionsAndMaxItems(baseWidth, expandWidth, height);
|
||||
this._grid.width = this._width;
|
||||
this._grid.height = this._height;
|
||||
this._pageDisplayed = 0;
|
||||
this._displayMatchedItems(true);
|
||||
let gridWidth = this._width;
|
||||
let sideArea = this.getSideArea();
|
||||
if (sideArea) {
|
||||
if (expanded)
|
||||
sideArea.show();
|
||||
else
|
||||
sideArea.hide();
|
||||
}
|
||||
this.emit('expanded');
|
||||
},
|
||||
|
||||
// Updates the displayed items and makes the display actor visible.
|
||||
show: function() {
|
||||
this._grid.show();
|
||||
// Load the initial state
|
||||
load: function() {
|
||||
this._redisplay(true);
|
||||
},
|
||||
|
||||
// Hides the display actor.
|
||||
hide: function() {
|
||||
this._grid.hide();
|
||||
// Should be called when the display is closed
|
||||
resetState: function() {
|
||||
this._filterReset();
|
||||
this._removeAllDisplayItems();
|
||||
this._openDetailIndex = -1;
|
||||
},
|
||||
|
||||
// Returns an actor which acts as a sidebar; this is used for
|
||||
// the applications category view
|
||||
getSideArea: function () {
|
||||
getNavigationArea: function () {
|
||||
return null;
|
||||
},
|
||||
|
||||
createDetailsForIndex: function(index, width, height) {
|
||||
let item = this._findDisplayedByIndex(index);
|
||||
return item.createDetailsActor(width, height);
|
||||
},
|
||||
|
||||
//// Protected methods ////
|
||||
|
||||
/*
|
||||
@ -627,9 +488,7 @@ GenericDisplay.prototype = {
|
||||
let hadSelected = this.hasSelected();
|
||||
|
||||
this._removeAllDisplayItems();
|
||||
|
||||
for (let i = this._maxItemsPerPage * this._pageDisplayed; i < this._matchedItems.length && i < this._maxItemsPerPage * (this._pageDisplayed + 1); i++) {
|
||||
|
||||
for (let i = 0; i < this._matchedItems.length; i++) {
|
||||
this._addDisplayItem(this._matchedItems[i]);
|
||||
}
|
||||
|
||||
@ -638,20 +497,18 @@ GenericDisplay.prototype = {
|
||||
this.selectFirstItem();
|
||||
}
|
||||
|
||||
this._updateDisplayControl(resetDisplayControl);
|
||||
|
||||
// We currently redisplay matching items and raise the sideshow as part of two different callbacks.
|
||||
// Checking what is under the pointer after a timeout allows us to not merge these callbacks into one, at least for now.
|
||||
Mainloop.timeout_add(5,
|
||||
// Checking what is under the pointer after a timeout allows us to not merge these callbacks into one, at least for now.
|
||||
Mainloop.timeout_add(5,
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
// Check if the pointer is over one of the items and display the preview pop-up if it is.
|
||||
// Check if the pointer is over one of the items and display the information button if it is.
|
||||
let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
|
||||
let global = Shell.Global.get();
|
||||
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
|
||||
x, y);
|
||||
if (actor != null) {
|
||||
let item = this._findDisplayedByActor(actor.get_parent());
|
||||
let item = this._findDisplayedByActor(actor);
|
||||
if (item != null) {
|
||||
item.onDrawnUnderPointer();
|
||||
}
|
||||
@ -670,66 +527,47 @@ GenericDisplay.prototype = {
|
||||
|
||||
let itemInfo = this._allItems[itemId];
|
||||
let displayItem = this._createDisplayItem(itemInfo);
|
||||
displayItem.setShowPreview(true);
|
||||
|
||||
displayItem.connect('activate',
|
||||
displayItem.connect('activate',
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
// update the selection
|
||||
this._selectIndex(this._getIndexOfDisplayedActor(displayItem.actor));
|
||||
this._selectIndex(this._list.get_actor_index(displayItem.actor));
|
||||
this.activateSelected();
|
||||
}));
|
||||
|
||||
displayItem.connect('select',
|
||||
displayItem.connect('show-details',
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
// update the selection
|
||||
this._selectIndex(this._getIndexOfDisplayedActor(displayItem.actor));
|
||||
let index = this._list.get_actor_index(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._grid.add_actor(displayItem.actor);
|
||||
this._list.add_actor(displayItem.actor);
|
||||
this._displayedItems[itemId] = displayItem;
|
||||
this._displayedItemsCount++;
|
||||
},
|
||||
|
||||
// Removes an item identifed by the itemId from the displayed items.
|
||||
_removeDisplayItem: function(itemId) {
|
||||
let count = this._list.displayedCount;
|
||||
let displayItem = this._displayedItems[itemId];
|
||||
let displayItemIndex = this._getIndexOfDisplayedActor(displayItem.actor);
|
||||
let displayItemIndex = this._list.get_actor_index(displayItem.actor);
|
||||
|
||||
if (this.hasSelected() && (this._displayedItemsCount == 1 || !this._grid.visible)) {
|
||||
if (this.hasSelected() && count == 1) {
|
||||
this.unsetSelected();
|
||||
} else if (this.hasSelected() && displayItemIndex < this._selectedIndex) {
|
||||
this.selectUp();
|
||||
}
|
||||
|
||||
if (displayItem.dragActor) {
|
||||
// The user might be handling a dragActor when the list of items
|
||||
// changes (for example, if the dragging caused us to transition
|
||||
// from an expanded overlay view to the regular view). So we need
|
||||
// to keep the item around so that the drag and drop action initiated
|
||||
// by the user can be completed. However, we remove the item from the list.
|
||||
//
|
||||
// For some reason, just removing the displayItem.actor
|
||||
// is not enough to get displayItem._icon.visible
|
||||
// to return false, so we hide the display item and
|
||||
// all its children first. (We check displayItem._icon.visible
|
||||
// when deciding if a dragActor has a place to snap back to
|
||||
// in case the drop was not accepted by any actor.)
|
||||
displayItem.actor.hide_all();
|
||||
this._grid.remove_actor(displayItem.actor);
|
||||
// We should not destroy the item up-front, because that would also
|
||||
// destroy the icon that was used to clone the image for the drag actor.
|
||||
// We destroy it once the dragActor is destroyed instead.
|
||||
displayItem.dragActor.connect('destroy',
|
||||
function(item) {
|
||||
displayItem.destroy();
|
||||
});
|
||||
|
||||
} else {
|
||||
displayItem.destroy();
|
||||
}
|
||||
|
||||
displayItem.destroy();
|
||||
|
||||
delete this._displayedItems[itemId];
|
||||
this._displayedItemsCount--;
|
||||
},
|
||||
|
||||
// Removes all displayed items.
|
||||
@ -760,9 +598,6 @@ GenericDisplay.prototype = {
|
||||
* their own while the user was browsing through the result pages.
|
||||
*/
|
||||
_redisplay: function(resetPage) {
|
||||
if (!this._grid.visible)
|
||||
return;
|
||||
|
||||
this._refreshCache();
|
||||
if (!this._filterActive())
|
||||
this._setDefaultList();
|
||||
@ -770,7 +605,7 @@ GenericDisplay.prototype = {
|
||||
this._doSearchFilter();
|
||||
|
||||
if (resetPage)
|
||||
this._pageDisplayed = 0;
|
||||
this._list.page = 0;
|
||||
|
||||
this._displayMatchedItems(true);
|
||||
|
||||
@ -811,27 +646,6 @@ GenericDisplay.prototype = {
|
||||
|
||||
//// Private methods ////
|
||||
|
||||
// Sets this._width, this._height, this._columnWidth, and this._maxItemsPerPage based on the
|
||||
// space available for the display, number of columns, and the number of items it can fit.
|
||||
_setDimensionsAndMaxItems: function(baseWidth, expandWidth, height) {
|
||||
this._width = baseWidth + expandWidth;
|
||||
let gridWidth;
|
||||
let sideArea = this.getSideArea();
|
||||
if (this._expanded && sideArea) {
|
||||
gridWidth = expandWidth;
|
||||
sideArea.width = baseWidth;
|
||||
sideArea.height = this._height;
|
||||
} else {
|
||||
gridWidth = this._width;
|
||||
}
|
||||
this._columnWidth = (gridWidth - this._columnGap * (this._numberOfColumns - 1)) / this._numberOfColumns;
|
||||
let maxItemsInColumn = Math.floor(height / ITEM_DISPLAY_HEIGHT);
|
||||
this._maxItemsPerPage = maxItemsInColumn * this._numberOfColumns;
|
||||
this._height = maxItemsInColumn * ITEM_DISPLAY_HEIGHT;
|
||||
this._grid.width = gridWidth;
|
||||
this._grid.height = this._height;
|
||||
},
|
||||
|
||||
_getSearchMatchedItems: function() {
|
||||
let matchedItemsForSearch = {};
|
||||
// Break the search up into terms, and search for each
|
||||
@ -881,13 +695,12 @@ GenericDisplay.prototype = {
|
||||
return 1;
|
||||
else
|
||||
return this._compareItems(a, b);
|
||||
}));
|
||||
}));
|
||||
},
|
||||
|
||||
// Displays the page specified by the pageNumber argument. The pageNumber is 0-based.
|
||||
// Displays the page specified by the pageNumber argument.
|
||||
_displayPage: function(pageNumber) {
|
||||
this._pageDisplayed = pageNumber;
|
||||
this._displayMatchedItems(false);
|
||||
this._list.page = pageNumber;
|
||||
},
|
||||
|
||||
/*
|
||||
@ -900,30 +713,30 @@ GenericDisplay.prototype = {
|
||||
*/
|
||||
_updateDisplayControl: function(resetDisplayControl) {
|
||||
if (resetDisplayControl) {
|
||||
this._selectedIndex = -1;
|
||||
this.displayControl.remove_all();
|
||||
let pageNumber = 0;
|
||||
for (let i = 0; i < this._matchedItems.length; i = i + this._maxItemsPerPage) {
|
||||
let pageControl = new Link.Link({ color: (pageNumber == this._pageDisplayed) ? DISPLAY_CONTROL_SELECTED_COLOR : ITEM_DISPLAY_DESCRIPTION_COLOR,
|
||||
let nPages = this._list.n_pages;
|
||||
let pageNumber = this._list.page;
|
||||
for (let i = 0; i < nPages; i++) {
|
||||
let pageControl = new Link.Link({ color: (i == pageNumber) ? DISPLAY_CONTROL_SELECTED_COLOR : ITEM_DISPLAY_DESCRIPTION_COLOR,
|
||||
font_name: "Sans Bold 16px",
|
||||
text: (pageNumber + 1) + "",
|
||||
height: LABEL_HEIGHT,
|
||||
reactive: (pageNumber == this._pageDisplayed) ? false : true});
|
||||
text: (i+1) + "",
|
||||
reactive: (i == pageNumber) ? false : true});
|
||||
this.displayControl.append(pageControl.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
// we use pageNumberLocalScope to get the page number right in the callback function
|
||||
let pageNumberLocalScope = pageNumber;
|
||||
let pageNumberLocalScope = i;
|
||||
pageControl.connect('clicked',
|
||||
Lang.bind(this,
|
||||
function(o, event) {
|
||||
this._displayPage(pageNumberLocalScope);
|
||||
}));
|
||||
pageNumber ++;
|
||||
}
|
||||
} else {
|
||||
let pageControlActors = this.displayControl.get_children();
|
||||
for (let i = 0; i < pageControlActors.length; i++) {
|
||||
for (let i = 0; i < pageControlActors.length; i++) {
|
||||
let pageControlActor = pageControlActors[i];
|
||||
if (i == this._pageDisplayed) {
|
||||
if (i == this._list.page) {
|
||||
pageControlActor.color = DISPLAY_CONTROL_SELECTED_COLOR;
|
||||
pageControlActor.reactive = false;
|
||||
} else {
|
||||
@ -934,11 +747,10 @@ GenericDisplay.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
// Returns a display item based on its index in the ordering of the
|
||||
// Returns a display item based on its index in the ordering of the
|
||||
// display children.
|
||||
_findDisplayedByIndex: function(index) {
|
||||
let displayedActors = this._grid.get_children();
|
||||
let actor = displayedActors[index];
|
||||
let actor = this._list.get_displayed_actor(index);
|
||||
return this._findDisplayedByActor(actor);
|
||||
},
|
||||
|
||||
@ -954,39 +766,31 @@ GenericDisplay.prototype = {
|
||||
return null;
|
||||
},
|
||||
|
||||
// Returns and index that the actor has in the ordering of the display's
|
||||
// children.
|
||||
_getIndexOfDisplayedActor: function(actor) {
|
||||
let children = this._grid.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (children[i] == actor)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
// Selects (e.g. highlights) a display item at the provided index,
|
||||
// updates this.selectedItemDetails actor, and emits 'selected' signal.
|
||||
_selectIndex: function(index) {
|
||||
if (this._selectedIndex != -1) {
|
||||
let prev = this._findDisplayedByIndex(this._selectedIndex);
|
||||
prev.markSelected(false);
|
||||
// Calling destroy() gets large image previews released as quickly as
|
||||
// possible, if we just removed them, they might hang around for a while
|
||||
// until the actor was garbage collected.
|
||||
let children = this.selectedItemDetails.get_children();
|
||||
for (let i = 0; i < children.length; i++)
|
||||
children[i].destroy();
|
||||
|
||||
this.selectedItemDetails.remove_all();
|
||||
if (index >= this._list.displayedCount)
|
||||
return
|
||||
|
||||
// If the item is already selected, all we do is toggling the details pane.
|
||||
if (this._selectedIndex == index && index >= 0) {
|
||||
this.emit('details', index);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cleanup from the previous item
|
||||
if (this._selectedIndex >= 0) {
|
||||
this._findDisplayedByIndex(this._selectedIndex).markSelected(false);
|
||||
}
|
||||
|
||||
this._selectedIndex = index;
|
||||
if (index != -1 && index < this._displayedItemsCount) {
|
||||
let item = this._findDisplayedByIndex(index);
|
||||
item.markSelected(true);
|
||||
this.selectedItemDetails.append(item.createDetailsActor(this._availableWidthForItemDetails, this._availableHeightForItemDetails), Big.BoxPackFlags.NONE);
|
||||
this.emit('selected');
|
||||
}
|
||||
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');
|
||||
}
|
||||
};
|
||||
|
||||
|
589
js/ui/lookingGlass.js
Normal file
589
js/ui/lookingGlass.js
Normal file
@ -0,0 +1,589 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Big = imports.gi.Big;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const LG_BORDER_COLOR = new Clutter.Color();
|
||||
LG_BORDER_COLOR.from_pixel(0x0000aca0);
|
||||
const LG_BACKGROUND_COLOR = new Clutter.Color();
|
||||
LG_BACKGROUND_COLOR.from_pixel(0x000000d5);
|
||||
const GREY = new Clutter.Color();
|
||||
GREY.from_pixel(0xAFAFAFFF);
|
||||
const MATRIX_GREEN = new Clutter.Color();
|
||||
MATRIX_GREEN.from_pixel(0x88ff66ff);
|
||||
// FIXME pull from GConf
|
||||
const MATRIX_FONT = 'Monospace 10';
|
||||
|
||||
/* Imports...feel free to add here as needed */
|
||||
var commandHeader = "const Clutter = imports.gi.Clutter; " +
|
||||
"const GLib = imports.gi.GLib; " +
|
||||
"const Gtk = imports.gi.Gtk; " +
|
||||
"const Mainloop = imports.mainloop; " +
|
||||
"const Meta = imports.gi.Meta; " +
|
||||
"const Shell = imports.gi.Shell; " +
|
||||
"const Main = imports.ui.main; " +
|
||||
"const Lang = imports.lang; " +
|
||||
"const Tweener = imports.ui.tweener; " +
|
||||
/* Utility functions...we should probably be able to use these
|
||||
* in the shell core code too. */
|
||||
"const global = Shell.Global.get(); " +
|
||||
"const stage = global.stage; " +
|
||||
"const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; " +
|
||||
/* Special lookingGlass functions */
|
||||
"const it = Main.lookingGlass.getIt(); " +
|
||||
"const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ";
|
||||
|
||||
function Notebook() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
Notebook.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new Big.Box();
|
||||
|
||||
this.tabControls = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
spacing: 4, padding: 2 });
|
||||
|
||||
this._selectedIndex = -1;
|
||||
this._tabs = [];
|
||||
},
|
||||
|
||||
appendPage: function(name, child) {
|
||||
let labelOuterBox = new Big.Box({ padding: 2 });
|
||||
let labelBox = new Big.Box({ padding: 2, border_color: MATRIX_GREEN,
|
||||
reactive: true });
|
||||
labelOuterBox.append(labelBox, Big.BoxPackFlags.NONE);
|
||||
let label = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
text: name });
|
||||
labelBox.connect('button-press-event', Lang.bind(this, function () {
|
||||
this.selectChild(child);
|
||||
return true;
|
||||
}));
|
||||
labelBox.append(label, Big.BoxPackFlags.EXPAND);
|
||||
this._tabs.push([child, labelBox]);
|
||||
child.hide();
|
||||
this.actor.append(child, Big.BoxPackFlags.EXPAND);
|
||||
this.tabControls.append(labelOuterBox, Big.BoxPackFlags.NONE);
|
||||
if (this._selectedIndex == -1)
|
||||
this.selectIndex(0);
|
||||
},
|
||||
|
||||
_unselect: function() {
|
||||
if (this._selectedIndex < 0)
|
||||
return;
|
||||
let [child, labelBox] = this._tabs[this._selectedIndex];
|
||||
labelBox.padding = 2;
|
||||
labelBox.border = 0;
|
||||
child.hide();
|
||||
this._selectedIndex = -1;
|
||||
},
|
||||
|
||||
selectIndex: function(index) {
|
||||
if (index == this._selectedIndex)
|
||||
return;
|
||||
this._unselect();
|
||||
if (index < 0) {
|
||||
this.emit('selection', null);
|
||||
return;
|
||||
}
|
||||
let [child, labelBox] = this._tabs[index];
|
||||
labelBox.padding = 1;
|
||||
labelBox.border = 1;
|
||||
child.show();
|
||||
this._selectedIndex = index;
|
||||
this.emit('selection', child);
|
||||
},
|
||||
|
||||
selectChild: function(child) {
|
||||
if (child == null)
|
||||
this.selectIndex(-1);
|
||||
else {
|
||||
for (let i = 0; i < this._tabs.length; i++) {
|
||||
let [tabChild, labelBox] = this._tabs[i];
|
||||
if (tabChild == child) {
|
||||
this.selectIndex(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(Notebook.prototype);
|
||||
|
||||
function Result(command, o, index) {
|
||||
this._init(command, o, index);
|
||||
}
|
||||
|
||||
Result.prototype = {
|
||||
_init : function(command, o, index) {
|
||||
this.index = index;
|
||||
this.o = o;
|
||||
|
||||
this.actor = new Big.Box();
|
||||
|
||||
let cmdTxt = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
text: command });
|
||||
this.actor.append(cmdTxt, Big.BoxPackFlags.NONE);
|
||||
let resultTxt = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
text: "r(" + index + ") = " + o });
|
||||
this.actor.append(resultTxt, Big.BoxPackFlags.NONE);
|
||||
let line = new Big.Box({ border_color: GREY,
|
||||
border_bottom: 1,
|
||||
height: 8 });
|
||||
this.actor.append(line, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
function ActorHierarchy() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
ActorHierarchy.prototype = {
|
||||
_init : function () {
|
||||
this._previousTarget = null;
|
||||
this._target = null;
|
||||
|
||||
this._parentList = [];
|
||||
|
||||
this.actor = new Big.Box({ spacing: 4,
|
||||
border: 1,
|
||||
padding: 4,
|
||||
border_color: GREY });
|
||||
},
|
||||
|
||||
setTarget: function(actor) {
|
||||
this._previousTarget = this._target;
|
||||
this.target = actor;
|
||||
|
||||
this.actor.remove_all();
|
||||
|
||||
if (!(actor instanceof Clutter.Actor))
|
||||
return;
|
||||
|
||||
if (this.target == null)
|
||||
return;
|
||||
|
||||
this._parentList = [];
|
||||
let parent = actor;
|
||||
while ((parent = parent.get_parent()) != null) {
|
||||
this._parentList.push(parent);
|
||||
|
||||
let link = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
reactive: true,
|
||||
text: "" + parent });
|
||||
this.actor.append(link, Big.BoxPackFlags.IF_FITS);
|
||||
let parentTarget = parent;
|
||||
link.connect('button-press-event', Lang.bind(this, function () {
|
||||
this._selectByActor(parentTarget);
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
this.emit('selection', actor);
|
||||
},
|
||||
|
||||
_selectByActor: function(actor) {
|
||||
let idx = this._parentList.indexOf(actor);
|
||||
let children = this.actor.get_children();
|
||||
let link = children[idx];
|
||||
this.emit('selection', actor);
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(ActorHierarchy.prototype);
|
||||
|
||||
function PropertyInspector() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
PropertyInspector.prototype = {
|
||||
_init : function () {
|
||||
this._target = null;
|
||||
|
||||
this._parentList = [];
|
||||
|
||||
this.actor = new Big.Box({ spacing: 4,
|
||||
border: 1,
|
||||
padding: 4,
|
||||
border_color: GREY });
|
||||
},
|
||||
|
||||
setTarget: function(actor) {
|
||||
this.target = actor;
|
||||
|
||||
this.actor.remove_all();
|
||||
|
||||
for (let propName in actor) {
|
||||
let valueStr;
|
||||
try {
|
||||
valueStr = "" + actor[propName];
|
||||
} catch (e) {
|
||||
valueStr = '<error>';
|
||||
}
|
||||
let propText = propName + ": " + valueStr;
|
||||
let propDisplay = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
reactive: true,
|
||||
text: propText });
|
||||
this.actor.append(propDisplay, Big.BoxPackFlags.IF_FITS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Inspector() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
Inspector.prototype = {
|
||||
_init: function() {
|
||||
let global = Shell.Global.get();
|
||||
let width = 150;
|
||||
let eventHandler = new Big.Box({ background_color: LG_BACKGROUND_COLOR,
|
||||
border: 1,
|
||||
border_color: LG_BORDER_COLOR,
|
||||
corner_radius: 4,
|
||||
y: global.stage.height/2,
|
||||
reactive: true
|
||||
});
|
||||
eventHandler.connect('notify::allocation', Lang.bind(this, function () {
|
||||
eventHandler.x = Math.floor((global.stage.width)/2 - (eventHandler.width)/2);
|
||||
}));
|
||||
global.stage.add_actor(eventHandler);
|
||||
let displayText = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT, text: '' });
|
||||
eventHandler.append(displayText, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
let borderPaintTarget = null;
|
||||
let borderPaintId = null;
|
||||
eventHandler.connect('destroy', Lang.bind(this, function() {
|
||||
if (borderPaintTarget != null)
|
||||
borderPaintTarget.disconnect(borderPaintId);
|
||||
}));
|
||||
|
||||
eventHandler.connect('button-press-event', Lang.bind(this, function (actor, event) {
|
||||
let global = Shell.Global.get();
|
||||
Clutter.ungrab_pointer(eventHandler);
|
||||
|
||||
let [stageX, stageY] = event.get_coords();
|
||||
let target = global.stage.get_actor_at_pos(Clutter.PickMode.ALL,
|
||||
stageX,
|
||||
stageY);
|
||||
this.emit('target', target, stageX, stageY);
|
||||
eventHandler.destroy();
|
||||
this.emit('closed');
|
||||
return true;
|
||||
}));
|
||||
|
||||
eventHandler.connect('motion-event', Lang.bind(this, function (actor, event) {
|
||||
let global = Shell.Global.get();
|
||||
let [stageX, stageY] = event.get_coords();
|
||||
let target = global.stage.get_actor_at_pos(Clutter.PickMode.ALL,
|
||||
stageX,
|
||||
stageY);
|
||||
displayText.text = '<inspect x: ' + stageX + ' y: ' + stageY + '> ' + target;
|
||||
if (borderPaintTarget != null)
|
||||
borderPaintTarget.disconnect(borderPaintId);
|
||||
borderPaintTarget = target;
|
||||
borderPaintId = Shell.add_hook_paint_red_border(target);
|
||||
return true;
|
||||
}));
|
||||
Clutter.grab_pointer(eventHandler);
|
||||
}
|
||||
}
|
||||
|
||||
Signals.addSignalMethods(Inspector.prototype);
|
||||
|
||||
function LookingGlass() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
LookingGlass.prototype = {
|
||||
_init : function() {
|
||||
let global = Shell.Global.get();
|
||||
|
||||
this._idleHistorySaveId = 0;
|
||||
let historyPath = global.configdir + "/lookingglass-history.txt";
|
||||
this._historyFile = Gio.file_new_for_path(historyPath);
|
||||
this._savedText = null;
|
||||
this._historyNavIndex = -1;
|
||||
this._history = [];
|
||||
this._readHistory();
|
||||
|
||||
this._open = false;
|
||||
|
||||
this._offset = 0;
|
||||
this._results = [];
|
||||
|
||||
// TODO replace with scrolling or something better
|
||||
this._maxItems = 10;
|
||||
|
||||
this.actor = new Big.Box({ background_color: LG_BACKGROUND_COLOR,
|
||||
border: 1,
|
||||
border_color: LG_BORDER_COLOR,
|
||||
corner_radius: 4,
|
||||
padding_top: 8,
|
||||
padding_left: 4,
|
||||
padding_right: 4,
|
||||
padding_bottom: 4,
|
||||
spacing: 4,
|
||||
visible: false
|
||||
});
|
||||
global.stage.add_actor(this.actor);
|
||||
|
||||
let toolbar = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
border: 1, border_color: GREY,
|
||||
corner_radius: 4 });
|
||||
this.actor.append(toolbar, Big.BoxPackFlags.NONE);
|
||||
let inspectIcon = Shell.TextureCache.get_default().load_gicon(new Gio.ThemedIcon({ name: 'gtk-color-picker' }),
|
||||
24);
|
||||
toolbar.append(inspectIcon, Big.BoxPackFlags.NONE);
|
||||
inspectIcon.reactive = true;
|
||||
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||
let inspector = new Inspector();
|
||||
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
||||
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
|
||||
target);
|
||||
this._hierarchy.setTarget(target);
|
||||
}));
|
||||
inspector.connect('closed', Lang.bind(this, function() {
|
||||
this.actor.show();
|
||||
global.stage.set_key_focus(this._entry);
|
||||
}));
|
||||
this.actor.hide();
|
||||
return true;
|
||||
}));
|
||||
|
||||
let notebook = new Notebook();
|
||||
this.actor.append(notebook.actor, Big.BoxPackFlags.EXPAND);
|
||||
toolbar.append(notebook.tabControls, Big.BoxPackFlags.END);
|
||||
|
||||
this._evalBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: 4 });
|
||||
notebook.appendPage('Evaluator', this._evalBox);
|
||||
|
||||
this._resultsArea = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: 4 });
|
||||
this._evalBox.append(this._resultsArea, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
let entryArea = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
this._evalBox.append(entryArea, Big.BoxPackFlags.NONE);
|
||||
|
||||
let label = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
text: 'js>>> ' });
|
||||
entryArea.append(label, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._entry = new Clutter.Text({ color: MATRIX_GREEN,
|
||||
font_name: MATRIX_FONT,
|
||||
editable: true,
|
||||
activatable: true,
|
||||
singleLineMode: true,
|
||||
text: ''});
|
||||
/* kind of a hack */
|
||||
notebook.connect('selection', Lang.bind(this, function (nb, child) {
|
||||
if (child == this._evalBox)
|
||||
global.stage.set_key_focus(this._entry);
|
||||
}));
|
||||
entryArea.append(this._entry, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
this._hierarchy = new ActorHierarchy();
|
||||
notebook.appendPage('Hierarchy', this._hierarchy.actor);
|
||||
|
||||
this._propInspector = new PropertyInspector();
|
||||
notebook.appendPage('Properties', this._propInspector.actor);
|
||||
this._hierarchy.connect('selection', Lang.bind(this, function (h, actor) {
|
||||
this._pushResult('<parent selection>', actor);
|
||||
notebook.selectIndex(0);
|
||||
}));
|
||||
|
||||
this._entry.connect('activate', Lang.bind(this, function (o, e) {
|
||||
let text = o.get_text();
|
||||
// Ensure we don't get newlines in the command; the history file is
|
||||
// newline-separated.
|
||||
text.replace('\n', ' ');
|
||||
// Strip leading and trailing whitespace
|
||||
text = text.replace(/^\s+/g, "").replace(/\s+$/g, "");
|
||||
if (text == '')
|
||||
return true;
|
||||
this._evaluate(text);
|
||||
this._historyNavIndex = -1;
|
||||
return true;
|
||||
}));
|
||||
this._entry.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
let symbol = Shell.get_event_key_symbol(e);
|
||||
if (symbol == Clutter.Escape) {
|
||||
this.close();
|
||||
return true;
|
||||
} else 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;
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_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);
|
||||
this._results.push(result);
|
||||
this._resultsArea.append(result.actor, Big.BoxPackFlags.NONE);
|
||||
this._propInspector.setTarget(obj);
|
||||
let children = this._resultsArea.get_children();
|
||||
if (children.length > this._maxItems) {
|
||||
this._results.shift();
|
||||
children[0].destroy();
|
||||
this._offset++;
|
||||
}
|
||||
this._it = obj;
|
||||
},
|
||||
|
||||
_evaluate : function(command) {
|
||||
this._history.push(command);
|
||||
this._queueHistorySave();
|
||||
|
||||
let fullCmd = commandHeader + command;
|
||||
|
||||
let resultObj;
|
||||
try {
|
||||
resultObj = eval(fullCmd);
|
||||
} catch (e) {
|
||||
resultObj = "<exception " + e + ">";
|
||||
}
|
||||
|
||||
this._pushResult(command, resultObj);
|
||||
this._hierarchy.setTarget(null);
|
||||
this._entry.text = '';
|
||||
},
|
||||
|
||||
getIt: function () {
|
||||
return this._it;
|
||||
},
|
||||
|
||||
getResult: function(idx) {
|
||||
return this._results[idx - this._offset].o;
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this._open)
|
||||
this.close();
|
||||
else
|
||||
this.open();
|
||||
},
|
||||
|
||||
_resizeTo: function(actor) {
|
||||
let stage = Shell.Global.get().stage;
|
||||
let stageWidth = stage.width;
|
||||
let myWidth = stage.width * 0.7;
|
||||
let myHeight = stage.height * 0.7;
|
||||
let [srcX, srcY] = actor.get_transformed_position();
|
||||
this.actor.x = srcX + (stage.width-myWidth)/2;
|
||||
this._hiddenY = srcY + actor.height - myHeight - 4; // -4 to hide the top corners
|
||||
this._targetY = this._hiddenY + myHeight;
|
||||
this.actor.y = this._hiddenY;
|
||||
this.actor.width = myWidth;
|
||||
this.actor.height = myHeight;
|
||||
},
|
||||
|
||||
slaveTo: function(actor) {
|
||||
this._slaveTo = actor;
|
||||
actor.connect('notify::allocation', Lang.bind(this, function () {
|
||||
this._resizeTo(actor);
|
||||
}));
|
||||
this._resizeTo(actor);
|
||||
},
|
||||
|
||||
open : function() {
|
||||
if (this._open)
|
||||
return;
|
||||
|
||||
this.actor.show();
|
||||
this.actor.lower(Main.chrome.actor);
|
||||
this._open = true;
|
||||
|
||||
Tweener.removeTweens(this.actor);
|
||||
|
||||
if (!Main.startModal())
|
||||
return;
|
||||
|
||||
let global = Shell.Global.get();
|
||||
global.stage.set_key_focus(this._entry);
|
||||
|
||||
Tweener.addTween(this.actor, { time: 0.5,
|
||||
transition: "easeOutQuad",
|
||||
y: this._targetY
|
||||
});
|
||||
},
|
||||
|
||||
close : function() {
|
||||
if (!this._open)
|
||||
return;
|
||||
|
||||
this._historyNavIndex = -1;
|
||||
this._open = false;
|
||||
Tweener.removeTweens(this.actor);
|
||||
|
||||
Main.endModal();
|
||||
|
||||
Tweener.addTween(this.actor, { time: 0.5,
|
||||
transition: "easeOutQuad",
|
||||
y: this._hiddenY,
|
||||
onComplete: Lang.bind(this, function () {
|
||||
this.actor.hide();
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(LookingGlass.prototype);
|
@ -13,6 +13,7 @@ const Chrome = imports.ui.chrome;
|
||||
const Overlay = imports.ui.overlay;
|
||||
const Panel = imports.ui.panel;
|
||||
const RunDialog = imports.ui.runDialog;
|
||||
const LookingGlass = imports.ui.lookingGlass;
|
||||
const Sidebar = imports.ui.sidebar;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const WindowManager = imports.ui.windowManager;
|
||||
@ -25,6 +26,7 @@ let panel = null;
|
||||
let sidebar = null;
|
||||
let overlay = null;
|
||||
let runDialog = null;
|
||||
let lookingGlass = null;
|
||||
let wm = null;
|
||||
let recorder = null;
|
||||
let inModal = false;
|
||||
@ -35,7 +37,6 @@ function start() {
|
||||
Gio.DesktopAppInfo.set_desktop_env("GNOME");
|
||||
|
||||
global.grab_dbus_service();
|
||||
global.start_task_panel();
|
||||
|
||||
Tweener.init();
|
||||
|
||||
@ -52,17 +53,10 @@ function start() {
|
||||
|
||||
global.connect('panel-run-dialog', function(panel) {
|
||||
// Make sure not more than one run dialog is shown.
|
||||
if (!runDialog) {
|
||||
if (runDialog == null) {
|
||||
runDialog = new RunDialog.RunDialog();
|
||||
let endHandler = function() {
|
||||
runDialog.destroy();
|
||||
runDialog = null;
|
||||
};
|
||||
runDialog.connect('run', endHandler);
|
||||
runDialog.connect('cancel', endHandler);
|
||||
if (!runDialog.show())
|
||||
endHandler();
|
||||
}
|
||||
runDialog.open();
|
||||
});
|
||||
|
||||
overlay = new Overlay.Overlay();
|
||||
@ -73,11 +67,6 @@ function start() {
|
||||
|
||||
global.screen.connect('toggle-recording', function() {
|
||||
if (recorder == null) {
|
||||
// We have to initialize GStreamer first. This isn't done
|
||||
// inside ShellRecorder to make it usable inside projects
|
||||
// with other usage of GStreamer.
|
||||
let Gst = imports.gi.Gst;
|
||||
Gst.init(null, null);
|
||||
recorder = new Shell.Recorder({ stage: global.stage });
|
||||
}
|
||||
|
||||
@ -88,6 +77,8 @@ function start() {
|
||||
}
|
||||
});
|
||||
|
||||
panel.startupAnimation();
|
||||
|
||||
let display = global.screen.get_display();
|
||||
display.connect('overlay-key', Lang.bind(overlay, overlay.toggle));
|
||||
global.connect('panel-main-menu', Lang.bind(overlay, overlay.toggle));
|
||||
@ -148,6 +139,14 @@ function endModal() {
|
||||
inModal = false;
|
||||
}
|
||||
|
||||
function createLookingGlass() {
|
||||
if (lookingGlass == null) {
|
||||
lookingGlass = new LookingGlass.LookingGlass();
|
||||
lookingGlass.slaveTo(panel.actor);
|
||||
}
|
||||
return lookingGlass;
|
||||
}
|
||||
|
||||
function createAppLaunchContext() {
|
||||
let global = Shell.Global.get();
|
||||
let screen = global.screen;
|
||||
|
874
js/ui/overlay.js
874
js/ui/overlay.js
File diff suppressed because it is too large
Load Diff
132
js/ui/panel.js
132
js/ui/panel.js
@ -7,35 +7,36 @@ const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const Button = imports.ui.button;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const PANEL_HEIGHT = 32;
|
||||
const PANEL_HEIGHT = 26;
|
||||
const TRAY_HEIGHT = PANEL_HEIGHT - 1;
|
||||
const SHADOW_HEIGHT = 6;
|
||||
|
||||
// The panel has a transparent white background with a gradient.
|
||||
const PANEL_TOP_COLOR = new Clutter.Color();
|
||||
PANEL_TOP_COLOR.from_pixel(0xffffff99);
|
||||
const PANEL_MIDDLE_COLOR = new Clutter.Color();
|
||||
PANEL_MIDDLE_COLOR.from_pixel(0xffffff88);
|
||||
const PANEL_BOTTOM_COLOR = new Clutter.Color();
|
||||
PANEL_BOTTOM_COLOR.from_pixel(0xffffffaa);
|
||||
const PANEL_BACKGROUND_COLOR = new Clutter.Color();
|
||||
PANEL_BACKGROUND_COLOR.from_pixel(0x000000ff);
|
||||
const PANEL_FOREGROUND_COLOR = new Clutter.Color();
|
||||
PANEL_FOREGROUND_COLOR.from_pixel(0xffffffff);
|
||||
|
||||
const SHADOW_COLOR = new Clutter.Color();
|
||||
SHADOW_COLOR.from_pixel(0x00000033);
|
||||
const TRANSPARENT_COLOR = new Clutter.Color();
|
||||
TRANSPARENT_COLOR.from_pixel(0x00000000);
|
||||
|
||||
// Darken (pressed) buttons; lightening has no effect on white backgrounds.
|
||||
// Don't make the mouse hover effect visible to the user for a menu feel.
|
||||
const PANEL_BUTTON_COLOR = new Clutter.Color();
|
||||
PANEL_BUTTON_COLOR.from_pixel(0x00000015);
|
||||
PANEL_BUTTON_COLOR.from_pixel(0x00000000);
|
||||
|
||||
// Lighten pressed buttons; darkening has no effect on a black background.
|
||||
const PRESSED_BUTTON_BACKGROUND_COLOR = new Clutter.Color();
|
||||
PRESSED_BUTTON_BACKGROUND_COLOR.from_pixel(0x00000030);
|
||||
PRESSED_BUTTON_BACKGROUND_COLOR.from_pixel(0x324c6ffa);
|
||||
|
||||
const DEFAULT_FONT = 'Sans 16px';
|
||||
|
||||
const TRAY_PADDING = 0;
|
||||
const TRAY_SPACING = 2;
|
||||
// See comments around _recomputeTraySize
|
||||
const TRAY_SPACING = 14;
|
||||
const TRAY_SPACING_MIN = 8;
|
||||
|
||||
// Used for the tray icon container with gtk pre-2.16, which doesn't
|
||||
// fully support tray icon transparency
|
||||
@ -57,26 +58,11 @@ Panel.prototype = {
|
||||
// Put the background under the panel within a group.
|
||||
this.actor = new Clutter.Group();
|
||||
|
||||
// Create backBox, which contains two boxes, backUpper and backLower,
|
||||
// for the background gradients and one for the shadow. The shadow at
|
||||
// the bottom has a fixed height (packing flag NONE), and the rest of
|
||||
// the height above is divided evenly between backUpper and backLower
|
||||
// (with packing flag EXPAND).
|
||||
let backBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: global.screen_width,
|
||||
height: PANEL_HEIGHT + SHADOW_HEIGHT });
|
||||
let backUpper = global.create_vertical_gradient(PANEL_TOP_COLOR,
|
||||
PANEL_MIDDLE_COLOR);
|
||||
let backLower = global.create_vertical_gradient(PANEL_MIDDLE_COLOR,
|
||||
PANEL_BOTTOM_COLOR);
|
||||
let shadow = global.create_vertical_gradient(SHADOW_COLOR,
|
||||
TRANSPARENT_COLOR);
|
||||
shadow.set_height(SHADOW_HEIGHT + 1);
|
||||
backBox.append(backUpper, Big.BoxPackFlags.EXPAND);
|
||||
backBox.append(backLower, Big.BoxPackFlags.EXPAND);
|
||||
backBox.append(shadow, Big.BoxPackFlags.NONE);
|
||||
// backBox contains the panel background and the clock.
|
||||
let backBox = new Big.Box({ width: global.screen_width,
|
||||
height: PANEL_HEIGHT,
|
||||
backgroundColor: PANEL_BACKGROUND_COLOR,
|
||||
x_align: Big.BoxAlignment.CENTER });
|
||||
this.actor.add_actor(backBox);
|
||||
|
||||
let box = new Big.Box({ x: 0,
|
||||
@ -86,14 +72,35 @@ Panel.prototype = {
|
||||
orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
spacing: 4 });
|
||||
|
||||
this.button = new Button.Button("Activities", PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR, true, null, PANEL_HEIGHT);
|
||||
this.button = new Button.Button("Activities", PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR, PANEL_FOREGROUND_COLOR, true, null, PANEL_HEIGHT, DEFAULT_FONT);
|
||||
|
||||
box.append(this.button.button, Big.BoxPackFlags.NONE);
|
||||
|
||||
let hotCorner = new Clutter.Rectangle({ width: 1,
|
||||
height: 1,
|
||||
opacity: 0,
|
||||
reactive: true });
|
||||
|
||||
// In addition to being triggered by the mouse enter event, the hot corner
|
||||
// can be triggered by clicking on it. This is useful if the user wants to
|
||||
// undo the effect of triggering the hot corner once in the hot corner.
|
||||
hotCorner.connect('enter-event',
|
||||
Lang.bind(this, this._onHotCornerTriggered));
|
||||
hotCorner.connect('button-release-event',
|
||||
Lang.bind(this, this._onHotCornerTriggered));
|
||||
|
||||
box.add_actor(hotCorner);
|
||||
|
||||
let statusbox = new Big.Box();
|
||||
let statusmenu = this._statusmenu = new Shell.StatusMenu();
|
||||
statusmenu.get_icon().hide();
|
||||
statusmenu.get_name().fontName = DEFAULT_FONT;
|
||||
statusmenu.get_name().color = PANEL_FOREGROUND_COLOR;
|
||||
statusbox.append(this._statusmenu, Big.BoxPackFlags.NONE);
|
||||
let statusbutton = new Button.Button(statusbox, PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR,
|
||||
let statusbutton = new Button.Button(statusbox,
|
||||
PANEL_BUTTON_COLOR,
|
||||
PRESSED_BUTTON_BACKGROUND_COLOR,
|
||||
PANEL_FOREGROUND_COLOR,
|
||||
true, null, PANEL_HEIGHT);
|
||||
statusbutton.button.connect('button-press-event', function (b, e) {
|
||||
statusmenu.toggle(e);
|
||||
@ -105,15 +112,14 @@ Panel.prototype = {
|
||||
statusbutton.release();
|
||||
});
|
||||
|
||||
this._clock = new Clutter.Text({ font_name: "Sans Bold 16px",
|
||||
this._clock = new Clutter.Text({ font_name: DEFAULT_FONT,
|
||||
color: PANEL_FOREGROUND_COLOR,
|
||||
text: "" });
|
||||
let pad = (PANEL_HEIGHT - this._clock.height) / 2;
|
||||
let clockbox = new Big.Box({ padding_top: pad,
|
||||
padding_bottom: pad,
|
||||
let clockbox = new Big.Box({ y_align: Big.BoxAlignment.CENTER,
|
||||
padding_left: 4,
|
||||
padding_right: 4 });
|
||||
clockbox.append(this._clock, Big.BoxPackFlags.NONE);
|
||||
box.append(clockbox, Big.BoxPackFlags.END);
|
||||
backBox.append(clockbox, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
// The tray icons live in trayBox within trayContainer.
|
||||
// The trayBox is hidden when there are no tray icons.
|
||||
@ -124,6 +130,7 @@ Panel.prototype = {
|
||||
height: TRAY_HEIGHT,
|
||||
padding: TRAY_PADDING,
|
||||
spacing: TRAY_SPACING });
|
||||
this._trayBox = trayBox;
|
||||
|
||||
// gtk+ < 2.16 doesn't have fully-working icon transparency,
|
||||
// so we want trayBox to be opaque in that case (the icons
|
||||
@ -140,19 +147,21 @@ Panel.prototype = {
|
||||
|
||||
this._traymanager = new Shell.TrayManager({ bg_color: TRAY_BACKGROUND_COLOR });
|
||||
this._traymanager.connect('tray-icon-added',
|
||||
function(o, icon) {
|
||||
Lang.bind(this, function(o, icon) {
|
||||
trayBox.append(icon, Big.BoxPackFlags.NONE);
|
||||
|
||||
// Make sure the trayBox is shown.
|
||||
trayBox.show();
|
||||
});
|
||||
this._recomputeTraySize();
|
||||
}));
|
||||
this._traymanager.connect('tray-icon-removed',
|
||||
function(o, icon) {
|
||||
Lang.bind(this, function(o, icon) {
|
||||
trayBox.remove_actor(icon);
|
||||
|
||||
if (trayBox.get_children().length == 0)
|
||||
trayBox.hide();
|
||||
});
|
||||
this._recomputeTraySize();
|
||||
}));
|
||||
this._traymanager.manage_stage(global.stage);
|
||||
|
||||
// TODO: decide what to do with the rest of the panel in the overlay mode (make it fade-out, become non-reactive, etc.)
|
||||
@ -176,6 +185,26 @@ Panel.prototype = {
|
||||
this._updateClock();
|
||||
},
|
||||
|
||||
startupAnimation: function() {
|
||||
this.actor.y = -this.actor.height;
|
||||
Tweener.addTween(this.actor,
|
||||
{ y: 0,
|
||||
time: 0.2,
|
||||
transition: "easeOutQuad"
|
||||
});
|
||||
},
|
||||
|
||||
// By default, tray icons have a spacing of TRAY_SPACING. However this
|
||||
// starts to fail if we have too many as can sadly happen; just jump down
|
||||
// to a spacing of 8 if we're over 6.
|
||||
// http://bugzilla.gnome.org/show_bug.cgi?id=590495
|
||||
_recomputeTraySize: function () {
|
||||
if (this._trayBox.get_children().length > 6)
|
||||
this._trayBox.spacing = TRAY_SPACING_MIN;
|
||||
else
|
||||
this._trayBox.spacing = TRAY_SPACING;
|
||||
},
|
||||
|
||||
_updateClock: function() {
|
||||
let displayDate = new Date();
|
||||
let msecRemaining = 60000 - (1000 * displayDate.getSeconds() +
|
||||
@ -184,8 +213,15 @@ Panel.prototype = {
|
||||
displayDate.setMinutes(displayDate.getMinutes() + 1);
|
||||
msecRemaining += 60000;
|
||||
}
|
||||
this._clock.set_text(displayDate.toLocaleFormat("%a %b %e, %l:%M %p"));
|
||||
this._clock.set_text(displayDate.toLocaleFormat("%a %l:%M %p"));
|
||||
Mainloop.timeout_add(msecRemaining, Lang.bind(this, this._updateClock));
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
_onHotCornerTriggered : function() {
|
||||
if (!Main.overlay.animationInProgress) {
|
||||
Main.overlay.toggle();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
159
js/ui/places.js
Normal file
159
js/ui/places.js
Normal file
@ -0,0 +1,159 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Big = imports.gi.Big;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Pango = imports.gi.Pango;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
|
||||
const PLACES_VSPACING = 8;
|
||||
const PLACES_ICON_SIZE = 16;
|
||||
|
||||
function PlaceDisplay(name, icon, onActivate) {
|
||||
this._init(name, icon, onActivate);
|
||||
}
|
||||
|
||||
PlaceDisplay.prototype = {
|
||||
_init : function(name, iconTexture, onActivate) {
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
reactive: true,
|
||||
spacing: 4 });
|
||||
this.actor.connect('button-press-event', Lang.bind(this, function (b, e) {
|
||||
onActivate(this);
|
||||
}));
|
||||
let text = new Clutter.Text({ font_name: "Sans 14px",
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
|
||||
text: name });
|
||||
this.actor.append(iconTexture, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(text, Big.BoxPackFlags.EXPAND);
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(PlaceDisplay.prototype);
|
||||
|
||||
function Places() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
Places.prototype = {
|
||||
_init : function() {
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
spacing: 4 });
|
||||
this._menuBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING });
|
||||
this.actor.append(this._menuBox, Big.BoxPackFlags.EXPAND);
|
||||
this._dirsBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING });
|
||||
this.actor.append(this._dirsBox, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
||||
let homeUri = homeFile.get_uri();
|
||||
let homeLabel = Shell.util_get_label_for_uri (homeUri);
|
||||
let homeIcon = Shell.util_get_icon_for_uri (homeUri);
|
||||
let homeTexture = Shell.TextureCache.get_default().load_gicon(homeIcon, PLACES_ICON_SIZE);
|
||||
let home = new PlaceDisplay(homeLabel, homeTexture, Lang.bind(this, function() {
|
||||
Main.overlay.hide();
|
||||
Gio.app_info_launch_default_for_uri(homeUri, Main.createAppLaunchContext());
|
||||
}));
|
||||
|
||||
this._menuBox.append(home.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
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) {
|
||||
let networkIcon = networkApp.create_icon_texture(PLACES_ICON_SIZE);
|
||||
let network = new PlaceDisplay(networkApp.get_name(), networkIcon, Lang.bind(this, function () {
|
||||
Main.overlay.hide();
|
||||
networkApp.launch();
|
||||
}));
|
||||
this._menuBox.append(network.actor, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
|
||||
let connectIcon = Shell.TextureCache.get_default().load_icon_name("applications-internet", PLACES_ICON_SIZE);
|
||||
let connect = new PlaceDisplay('Connect to...', connectIcon, Lang.bind(this, function () {
|
||||
Main.overlay.hide();
|
||||
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
|
||||
}));
|
||||
this._menuBox.append(connect.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), ".gtk-bookmarks"]);
|
||||
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
|
||||
let monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
||||
let timeoutId = 0;
|
||||
monitor.connect('changed', Lang.bind(this, function () {
|
||||
if (timeoutId > 0)
|
||||
return;
|
||||
/* Defensive event compression */
|
||||
timeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
|
||||
this._timeoutId = 0;
|
||||
this._reloadBookmarks();
|
||||
return false;
|
||||
}));
|
||||
}));
|
||||
|
||||
this._reloadBookmarks();
|
||||
},
|
||||
|
||||
_reloadBookmarks: function() {
|
||||
|
||||
this._dirsBox.remove_all();
|
||||
|
||||
let [success, bookmarksContent, len] = GLib.file_get_contents(this._bookmarksPath);
|
||||
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
let bookmarks = bookmarksContent.split('\n');
|
||||
|
||||
let bookmarksToLabel = {};
|
||||
let bookmarksOrder = [];
|
||||
for (let i = 0; i < bookmarks.length; i++) {
|
||||
let bookmarkLine = bookmarks[i];
|
||||
let components = bookmarkLine.split(' ');
|
||||
let bookmark = components[0];
|
||||
if (bookmark in bookmarksToLabel)
|
||||
continue;
|
||||
let label = null;
|
||||
if (components.length > 1)
|
||||
label = components.slice(1).join(' ');
|
||||
bookmarksToLabel[bookmark] = label;
|
||||
bookmarksOrder.push(bookmark);
|
||||
}
|
||||
|
||||
for (let i = 0; i < bookmarksOrder.length; i++) {
|
||||
let bookmark = bookmarksOrder[i];
|
||||
let label = bookmarksToLabel[bookmark];
|
||||
let file = Gio.file_new_for_uri(bookmark);
|
||||
if (!file.query_exists(null))
|
||||
continue;
|
||||
if (label == null)
|
||||
label = Shell.util_get_label_for_uri(bookmark);
|
||||
if (label == null)
|
||||
continue;
|
||||
let icon = Shell.util_get_icon_for_uri(bookmark);
|
||||
let iconTexture = Shell.TextureCache.get_default().load_gicon(icon, PLACES_ICON_SIZE);
|
||||
let item = new PlaceDisplay(label, iconTexture, Lang.bind(this, function() {
|
||||
Main.overlay.hide();
|
||||
Gio.app_info_launch_default_for_uri(bookmark, Main.createAppLaunchContext());
|
||||
}));
|
||||
this._dirsBox.append(item.actor, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(Places.prototype);
|
@ -1,6 +1,9 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Big = imports.gi.Big;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
@ -26,6 +29,19 @@ RunDialog.prototype = {
|
||||
_init : function() {
|
||||
let global = Shell.Global.get();
|
||||
|
||||
this._isOpen = false;
|
||||
|
||||
this._internalCommands = { 'lg':
|
||||
Lang.bind(this, function() {
|
||||
Mainloop.idle_add(function() { Main.createLookingGlass().open(); });
|
||||
}),
|
||||
|
||||
'restart': Lang.bind(this, function() {
|
||||
let global = Shell.Global.get();
|
||||
global.reexec_self();
|
||||
})
|
||||
};
|
||||
|
||||
// All actors are inside _group. We create it initially
|
||||
// hidden then show it in show()
|
||||
this._group = new Clutter.Group({ visible: false });
|
||||
@ -43,11 +59,12 @@ RunDialog.prototype = {
|
||||
(global.screen_height - BOX_HEIGHT) / 2);
|
||||
this._group.add_actor(boxGroup);
|
||||
|
||||
let box = new Clutter.Rectangle({ color: BOX_BACKGROUND_COLOR,
|
||||
reactive: false,
|
||||
width: BOX_WIDTH,
|
||||
height: BOX_HEIGHT,
|
||||
border_width: 0 });
|
||||
let box = new Big.Box({ background_color: BOX_BACKGROUND_COLOR,
|
||||
corner_radius: 4,
|
||||
reactive: false,
|
||||
width: BOX_WIDTH,
|
||||
height: BOX_HEIGHT
|
||||
});
|
||||
boxGroup.add_actor(box);
|
||||
|
||||
let label = new Clutter.Text({ color: BOX_TEXT_COLOR,
|
||||
@ -68,20 +85,26 @@ RunDialog.prototype = {
|
||||
this._entry.set_position(6, 30);
|
||||
boxGroup.add_actor(this._entry);
|
||||
|
||||
let me = this;
|
||||
|
||||
this._entry.connect('activate', function (o, e) {
|
||||
me.hide();
|
||||
me._run(o.get_text());
|
||||
this._entry.connect('activate', Lang.bind(this, function (o, e) {
|
||||
this._run(o.get_text());
|
||||
this.close();
|
||||
return false;
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
this._entry.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
let symbol = Shell.get_event_key_symbol(e);
|
||||
if (symbol == Clutter.Escape) {
|
||||
this.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
},
|
||||
|
||||
_run : function(command) {
|
||||
if (command == 'restart') {
|
||||
let global = Shell.Global.get();
|
||||
global.reexec_self();
|
||||
let f = this._internalCommands[command];
|
||||
if (f) {
|
||||
f();
|
||||
} else if (command) {
|
||||
var p = new Shell.Process({'args' : [command]});
|
||||
try {
|
||||
@ -91,47 +114,32 @@ RunDialog.prototype = {
|
||||
log('Could not run command ' + command + '.');
|
||||
}
|
||||
}
|
||||
|
||||
this.emit('run');
|
||||
},
|
||||
|
||||
show : function() {
|
||||
let me = this;
|
||||
|
||||
if (this._group.visible) // Already shown
|
||||
return false;
|
||||
open : function() {
|
||||
if (this._isOpen) // Already shown
|
||||
return;
|
||||
|
||||
if (!Main.startModal())
|
||||
return false;
|
||||
|
||||
this._group.show_all();
|
||||
|
||||
this._entry.connect('key-press-event', function(o, e) {
|
||||
if (Shell.get_event_key_symbol(e) == Clutter.Escape) {
|
||||
me.hide();
|
||||
me.emit('cancel');
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
});
|
||||
return;
|
||||
|
||||
this._isOpen = true;
|
||||
this._group.show();
|
||||
|
||||
let global = Shell.Global.get();
|
||||
global.stage.set_key_focus(this._entry);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
hide : function() {
|
||||
if (!this._group.visible)
|
||||
close : function() {
|
||||
if (!this._isOpen)
|
||||
return;
|
||||
|
||||
this._isOpen = false;
|
||||
|
||||
this._group.hide();
|
||||
Main.endModal();
|
||||
},
|
||||
this._entry.text = '';
|
||||
|
||||
destroy : function(){
|
||||
this.hide();
|
||||
this._group.destroy();
|
||||
Main.endModal();
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(RunDialog.prototype);
|
||||
|
@ -14,10 +14,12 @@ const WidgetBox = imports.ui.widgetBox;
|
||||
const SIDEBAR_SPACING = 4;
|
||||
const SIDEBAR_PADDING = 4;
|
||||
|
||||
// The total sidebar width is the widget width plus the widget
|
||||
// padding, plus the sidebar padding
|
||||
const SIDEBAR_COLLAPSED_WIDTH = Widget.COLLAPSED_WIDTH + 2 * WidgetBox.WIDGETBOX_PADDING + 2 * SIDEBAR_PADDING;
|
||||
const SIDEBAR_EXPANDED_WIDTH = Widget.EXPANDED_WIDTH + 2 * WidgetBox.WIDGETBOX_PADDING + 2 * SIDEBAR_PADDING;
|
||||
// The total sidebar width is the widget width plus the widget padding
|
||||
// (counted twice for the widget box, and once again for the
|
||||
// out-of-screen padding), plus the empty space between the border of
|
||||
// the bar and of the windows
|
||||
const SIDEBAR_COLLAPSED_WIDTH = Widget.COLLAPSED_WIDTH + 3 * WidgetBox.WIDGETBOX_PADDING + SIDEBAR_PADDING;
|
||||
const SIDEBAR_EXPANDED_WIDTH = Widget.EXPANDED_WIDTH + 3 * WidgetBox.WIDGETBOX_PADDING + SIDEBAR_PADDING;
|
||||
|
||||
// The maximum height of the sidebar would be extending from just
|
||||
// below the panel to just above the taskbar. Since the taskbar is
|
||||
@ -26,13 +28,6 @@ const SIDEBAR_EXPANDED_WIDTH = Widget.EXPANDED_WIDTH + 2 * WidgetBox.WIDGETBOX_P
|
||||
const HARDCODED_TASKBAR_HEIGHT = 24;
|
||||
const MAXIMUM_SIDEBAR_HEIGHT = Shell.Global.get().screen_height - Panel.PANEL_HEIGHT - HARDCODED_TASKBAR_HEIGHT;
|
||||
|
||||
// FIXME, needs to be configurable, obviously
|
||||
const default_widgets = [
|
||||
"imports.ui.widget.ClockWidget",
|
||||
"imports.ui.widget.AppsWidget",
|
||||
"imports.ui.widget.DocsWidget"
|
||||
];
|
||||
|
||||
function Sidebar() {
|
||||
this._init();
|
||||
}
|
||||
@ -48,7 +43,6 @@ Sidebar.prototype = {
|
||||
this.actor = new Clutter.Group({ x: -WidgetBox.WIDGETBOX_PADDING,
|
||||
y: Panel.PANEL_HEIGHT,
|
||||
width: SIDEBAR_EXPANDED_WIDTH });
|
||||
Main.chrome.addActor(this.actor);
|
||||
|
||||
// The actual widgets go into a Big.Box inside this.actor. The
|
||||
// box's width will vary during the expand/collapse animations,
|
||||
@ -58,23 +52,38 @@ Sidebar.prototype = {
|
||||
// during the animation.
|
||||
this.box = new Big.Box ({ padding_top: SIDEBAR_PADDING,
|
||||
padding_bottom: SIDEBAR_PADDING,
|
||||
padding_right: SIDEBAR_PADDING,
|
||||
padding_right: 0,
|
||||
padding_left: 0,
|
||||
spacing: SIDEBAR_SPACING });
|
||||
this.actor.add_actor(this.box);
|
||||
|
||||
this._visible = this.expanded = true;
|
||||
this._gconf = Shell.GConf.get_default();
|
||||
|
||||
this._expanded = this._gconf.get_boolean ("sidebar/expanded");
|
||||
if (!this._expanded)
|
||||
this.actor.width = SIDEBAR_COLLAPSED_WIDTH;
|
||||
|
||||
this._visible = this._gconf.get_boolean ("sidebar/visible");
|
||||
if (this._visible)
|
||||
Main.chrome.addActor(this.actor);
|
||||
|
||||
this._widgets = [];
|
||||
this.addWidget(new ToggleWidget(this));
|
||||
this.addWidget(new ToggleWidget());
|
||||
|
||||
let default_widgets = this._gconf.get_string_list("sidebar/widgets");
|
||||
for (let i = 0; i < default_widgets.length; i++)
|
||||
this.addWidget(default_widgets[i]);
|
||||
|
||||
this._gconf.connect('changed::sidebar/expanded',
|
||||
Lang.bind(this, this._expandedChanged));
|
||||
this._gconf.connect('changed::sidebar/visible',
|
||||
Lang.bind(this, this._visibleChanged));
|
||||
},
|
||||
|
||||
addWidget: function(widget) {
|
||||
let widgetBox;
|
||||
try {
|
||||
widgetBox = new WidgetBox.WidgetBox(widget);
|
||||
widgetBox = new WidgetBox.WidgetBox(widget, this._expanded);
|
||||
} catch(e) {
|
||||
logError(e, "Failed to add widget '" + widget + "'");
|
||||
return;
|
||||
@ -84,18 +93,32 @@ Sidebar.prototype = {
|
||||
this._widgets.push(widgetBox);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this._visible = true;
|
||||
this.actor.show();
|
||||
_visibleChanged: function() {
|
||||
let visible = this._gconf.get_boolean("sidebar/visible");
|
||||
if (visible == this._visible)
|
||||
return;
|
||||
|
||||
this._visible = visible;
|
||||
if (visible)
|
||||
Main.chrome.addActor(this.actor);
|
||||
else
|
||||
Main.chrome.removeActor(this.actor);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this._visible = false;
|
||||
this.actor.hide();
|
||||
_expandedChanged: function() {
|
||||
let expanded = this._gconf.get_boolean("sidebar/expanded");
|
||||
if (expanded == this._expanded)
|
||||
return;
|
||||
|
||||
this._expanded = expanded;
|
||||
if (expanded)
|
||||
this._expand();
|
||||
else
|
||||
this._collapse();
|
||||
},
|
||||
|
||||
expand: function() {
|
||||
this.expanded = true;
|
||||
_expand: function() {
|
||||
this._expanded = true;
|
||||
for (let i = 0; i < this._widgets.length; i++)
|
||||
this._widgets[i].expand();
|
||||
|
||||
@ -106,8 +129,8 @@ Sidebar.prototype = {
|
||||
} });
|
||||
},
|
||||
|
||||
collapse: function() {
|
||||
this.expanded = false;
|
||||
_collapse: function() {
|
||||
this._expanded = false;
|
||||
for (let i = 0; i < this._widgets.length; i++)
|
||||
this._widgets[i].collapse();
|
||||
|
||||
@ -130,24 +153,33 @@ Sidebar.prototype = {
|
||||
const LEFT_DOUBLE_ARROW = "\u00AB";
|
||||
const RIGHT_DOUBLE_ARROW = "\u00BB";
|
||||
|
||||
function ToggleWidget(sidebar) {
|
||||
this._init(sidebar);
|
||||
function ToggleWidget() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
ToggleWidget.prototype = {
|
||||
__proto__ : Widget.Widget.prototype,
|
||||
|
||||
_init : function(sidebar) {
|
||||
this._sidebar = sidebar;
|
||||
_init : function() {
|
||||
this._gconf = Shell.GConf.get_default();
|
||||
|
||||
this.actor = new Clutter.Text({ font_name: "Sans Bold 16px",
|
||||
text: LEFT_DOUBLE_ARROW,
|
||||
reactive: true });
|
||||
this.actor.connect('button-release-event',
|
||||
Lang.bind(this._sidebar, this._sidebar.collapse));
|
||||
Lang.bind(this, this._collapse));
|
||||
this.collapsedActor = new Clutter.Text({ font_name: "Sans Bold 16px",
|
||||
text: RIGHT_DOUBLE_ARROW,
|
||||
reactive: true });
|
||||
this.collapsedActor.connect('button-release-event',
|
||||
Lang.bind(this._sidebar, this._sidebar.expand));
|
||||
Lang.bind(this, this._expand));
|
||||
},
|
||||
|
||||
_collapse : function () {
|
||||
this._gconf.set_boolean ("sidebar/expanded", false);
|
||||
},
|
||||
|
||||
_expand : function () {
|
||||
this._gconf.set_boolean ("sidebar/expanded", true);
|
||||
}
|
||||
};
|
||||
|
@ -10,8 +10,6 @@ const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const AppInfo = imports.misc.appInfo;
|
||||
const DocDisplay = imports.ui.docDisplay;
|
||||
const DocInfo = imports.misc.docInfo;
|
||||
|
||||
const COLLAPSED_WIDTH = 24;
|
||||
@ -31,12 +29,22 @@ function Widget() {
|
||||
Widget.prototype = {
|
||||
// _init():
|
||||
//
|
||||
// Your widget constructor. Receives no arguments. Must define a
|
||||
// field named "actor" containing the Clutter.Actor to show in
|
||||
// expanded mode. This actor will be clipped to
|
||||
// Widget.EXPANDED_WIDTH. Most widgets will also define a field
|
||||
// named "title" containing the title string to show above the
|
||||
// widget in the sidebar.
|
||||
// Your widget constructor. Your constructor function should look
|
||||
// like:
|
||||
//
|
||||
// function MyWidgetType() {
|
||||
// this._init.apply(this, arguments);
|
||||
// }
|
||||
//
|
||||
// and your _init method should start by doing:
|
||||
//
|
||||
// Widget.Widget.prototype._init.apply(this, arguments);
|
||||
//
|
||||
// The _init method must define a field named "actor" containing
|
||||
// the Clutter.Actor to show in expanded mode. This actor will be
|
||||
// clipped to Widget.EXPANDED_WIDTH. Most widgets will also define
|
||||
// a field named "title" containing the title string to show above
|
||||
// the widget in the sidebar.
|
||||
//
|
||||
// If you want to have a separate collapsed view, you can define a
|
||||
// field "collapsedActor" containing the Clutter.Actor to show in
|
||||
@ -51,6 +59,9 @@ Widget.prototype = {
|
||||
// the sidebar is collapsed, the widget's expanded view will pop
|
||||
// out of the sidebar until either the cursor moves out of it,
|
||||
// or else the widget calls this.activated() on itself.
|
||||
_init: function (initialState) {
|
||||
this.state = initialState;
|
||||
},
|
||||
|
||||
// destroy():
|
||||
//
|
||||
@ -81,23 +92,22 @@ Widget.prototype = {
|
||||
// state:
|
||||
//
|
||||
// A field set on your widget by the sidebar. Will contain one of
|
||||
// the Widget.STATE_* values. (Eg, Widget.STATE_EXPANDED). Note
|
||||
// that this will not be set until *after* _init() is called, so
|
||||
// you cannot rely on it being set at that point. The widget will
|
||||
// always initially be in STATE_EXPANDED.
|
||||
// the Widget.STATE_* values. (Eg, Widget.STATE_EXPANDED).
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(Widget.prototype);
|
||||
|
||||
|
||||
function ClockWidget() {
|
||||
this._init();
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
ClockWidget.prototype = {
|
||||
__proto__ : Widget.prototype,
|
||||
|
||||
_init: function() {
|
||||
Widget.prototype._init.apply(this, arguments);
|
||||
|
||||
this.actor = new Clutter.Text({ font_name: "Sans Bold 16px",
|
||||
text: "",
|
||||
// Give an explicit height to ensure
|
||||
@ -151,9 +161,9 @@ ClockWidget.prototype = {
|
||||
|
||||
_updateCairo: function(time) {
|
||||
let global = Shell.Global.get();
|
||||
global.clutter_cairo_texture_draw_clock(this.collapsedActor,
|
||||
time.getHours() % 12,
|
||||
time.getMinutes());
|
||||
Shell.draw_clock(this.collapsedActor,
|
||||
time.getHours() % 12,
|
||||
time.getMinutes());
|
||||
}
|
||||
};
|
||||
|
||||
@ -168,7 +178,7 @@ const ITEM_NAME_COLOR = new Clutter.Color();
|
||||
ITEM_NAME_COLOR.from_pixel(0x000000ff);
|
||||
|
||||
function LauncherWidget() {
|
||||
this._init();
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
LauncherWidget.prototype = {
|
||||
@ -182,7 +192,7 @@ LauncherWidget.prototype = {
|
||||
spacing: ITEM_SPACING,
|
||||
reactive: true });
|
||||
item._info = info;
|
||||
item.append(info.getIcon(ITEM_ICON_SIZE), Big.BoxPackFlags.NONE);
|
||||
item.append(info.createIcon(ITEM_ICON_SIZE), Big.BoxPackFlags.NONE);
|
||||
item.append(new Clutter.Text({ color: ITEM_NAME_COLOR,
|
||||
font_name: "Sans 14px",
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
@ -203,7 +213,7 @@ LauncherWidget.prototype = {
|
||||
padding: ITEM_PADDING,
|
||||
reactive: true });
|
||||
item._info = info;
|
||||
item.append(info.getIcon(COLLAPSED_WIDTH - 2 * ITEM_PADDING),
|
||||
item.append(info.createIcon(COLLAPSED_WIDTH - 2 * ITEM_PADDING),
|
||||
Big.BoxPackFlags.NONE);
|
||||
|
||||
this.collapsedActor.append(item, Big.BoxPackFlags.NONE);
|
||||
@ -272,33 +282,61 @@ LauncherWidget.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function AppsWidgetInfo(appInfo) {
|
||||
this._init(appInfo);
|
||||
}
|
||||
|
||||
AppsWidgetInfo.prototype = {
|
||||
_init : function(appInfo) {
|
||||
this._info = appInfo;
|
||||
this.name = appInfo.get_name();
|
||||
},
|
||||
|
||||
createIcon : function(size) {
|
||||
return this._info.create_icon_texture(size);
|
||||
},
|
||||
|
||||
launch : function() {
|
||||
this._info.launch();
|
||||
}
|
||||
}
|
||||
|
||||
function AppsWidget() {
|
||||
this._init();
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
AppsWidget.prototype = {
|
||||
__proto__ : LauncherWidget.prototype,
|
||||
|
||||
_init : function() {
|
||||
Widget.prototype._init.apply(this, arguments);
|
||||
|
||||
this.title = "Applications";
|
||||
this.actor = new Big.Box({ spacing: 2 });
|
||||
this.collapsedActor = new Big.Box({ spacing: 2});
|
||||
|
||||
let apps = AppInfo.getMostUsedApps(5);
|
||||
for (let i = 0; i < apps.length; i++)
|
||||
this.addItem(apps[i]);
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
let apps = appSystem.get_favorites();
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let app = appSystem.lookup_cached_app(apps[i]);
|
||||
if (!app)
|
||||
continue;
|
||||
this.addItem(new AppsWidgetInfo(app));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function DocsWidget() {
|
||||
this._init();
|
||||
function RecentDocsWidget() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
DocsWidget.prototype = {
|
||||
RecentDocsWidget.prototype = {
|
||||
__proto__ : LauncherWidget.prototype,
|
||||
|
||||
_init : function() {
|
||||
this.title = "Recent Docs";
|
||||
Widget.prototype._init.apply(this, arguments);
|
||||
|
||||
this.title = "Recent Documents";
|
||||
this.actor = new Big.Box({ spacing: 2 });
|
||||
|
||||
this._recentManager = Gtk.RecentManager.get_default();
|
||||
@ -320,7 +358,7 @@ DocsWidget.prototype = {
|
||||
items.push(docInfo);
|
||||
}
|
||||
|
||||
items.sort(function (a,b) { return b.lastVisited() - a.lastVisited(); });
|
||||
items.sort(function (a,b) { return b.timestamp - a.timestamp; });
|
||||
for (i = 0; i < Math.min(items.length, 5); i++)
|
||||
this.addItem(items[i]);
|
||||
}
|
||||
|
@ -15,21 +15,24 @@ WIDGETBOX_BG_COLOR.from_pixel(0xf0f0f0ff);
|
||||
const BLACK = new Clutter.Color();
|
||||
BLACK.from_pixel(0x000000ff);
|
||||
|
||||
const WIDGETBOX_PADDING = 4;
|
||||
const WIDGETBOX_PADDING = 2;
|
||||
const ANIMATION_TIME = 0.5;
|
||||
const POP_IN_LAG = 250; /* milliseconds */
|
||||
|
||||
function WidgetBox(widget) {
|
||||
this._init(widget);
|
||||
function WidgetBox(widget, expanded) {
|
||||
this._init(widget, expanded);
|
||||
}
|
||||
|
||||
WidgetBox.prototype = {
|
||||
_init: function(widget) {
|
||||
if (widget instanceof Widget.Widget)
|
||||
_init: function(widget, expanded) {
|
||||
this.state = expanded ? Widget.STATE_EXPANDED : Widget.STATE_COLLAPSED;
|
||||
|
||||
if (widget instanceof Widget.Widget) {
|
||||
this._widget = widget;
|
||||
else {
|
||||
this._widget.state = this.state;
|
||||
} else {
|
||||
let ctor = this._ctorFromName(widget);
|
||||
this._widget = new ctor();
|
||||
this._widget = new ctor(this.state);
|
||||
}
|
||||
|
||||
if (!this._widget.actor)
|
||||
@ -37,7 +40,7 @@ WidgetBox.prototype = {
|
||||
else if (!this._widget.title && !this._widget.collapsedActor)
|
||||
throw new Error("widget has neither title nor collapsedActor");
|
||||
|
||||
this.state = this._widget.state = Widget.STATE_EXPANDED;
|
||||
this.state = expanded ? Widget.STATE_EXPANDED : Widget.STATE_COLLAPSED;
|
||||
|
||||
// The structure of a WidgetBox:
|
||||
//
|
||||
@ -77,9 +80,15 @@ WidgetBox.prototype = {
|
||||
|
||||
this.actor = new Clutter.Group();
|
||||
this._hbox = new Big.Box({ background_color: WIDGETBOX_BG_COLOR,
|
||||
padding: WIDGETBOX_PADDING,
|
||||
padding_top: WIDGETBOX_PADDING,
|
||||
padding_bottom: WIDGETBOX_PADDING,
|
||||
padding_right: WIDGETBOX_PADDING,
|
||||
// Left padding is here to make up for
|
||||
// the X offset used for the sidebar
|
||||
// to hide its rounded corners
|
||||
padding_left: 2 * WIDGETBOX_PADDING,
|
||||
spacing: WIDGETBOX_PADDING,
|
||||
corner_radius: WIDGETBOX_PADDING / 2,
|
||||
corner_radius: WIDGETBOX_PADDING,
|
||||
orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
reactive: true });
|
||||
this.actor.add_actor(this._hbox);
|
||||
@ -122,7 +131,6 @@ WidgetBox.prototype = {
|
||||
this._activationHandler = this._widget.connect('activated',
|
||||
Lang.bind(this, this._activationHandler));
|
||||
}
|
||||
this._cgroup.hide();
|
||||
|
||||
this._egroup = new Clutter.Group({ clip_to_allocation: true });
|
||||
this._hbox.append(this._egroup, Big.BoxPackFlags.NONE);
|
||||
@ -142,6 +150,11 @@ WidgetBox.prototype = {
|
||||
}
|
||||
|
||||
this._ebox.append(this._widget.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
if (expanded)
|
||||
this._setWidgetExpanded();
|
||||
else
|
||||
this._setWidgetCollapsed();
|
||||
},
|
||||
|
||||
// Given a name like "imports.ui.widget.ClockWidget", turn that
|
||||
@ -178,16 +191,25 @@ WidgetBox.prototype = {
|
||||
this.state = this._widget.state = Widget.STATE_EXPANDING;
|
||||
},
|
||||
|
||||
_expandPart1Complete: function() {
|
||||
_setWidgetExpanded: function() {
|
||||
this._cgroup.hide();
|
||||
this._cbox.x = 0;
|
||||
this._egroup.show();
|
||||
|
||||
if (this._singleActor) {
|
||||
log(this._widget.actor);
|
||||
this._widget.actor.unparent();
|
||||
this._ebox.append(this._widget.actor, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
|
||||
if (this._htitle) {
|
||||
this._htitle.show();
|
||||
this._hline.show();
|
||||
}
|
||||
},
|
||||
|
||||
_expandPart1Complete: function() {
|
||||
this._cbox.x = 0;
|
||||
this._setWidgetExpanded();
|
||||
|
||||
if (this._widget.expand) {
|
||||
try {
|
||||
this._widget.expand();
|
||||
@ -196,11 +218,6 @@ WidgetBox.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
this._egroup.show();
|
||||
if (this._htitle) {
|
||||
this._htitle.show();
|
||||
this._hline.show();
|
||||
}
|
||||
this._ebox.x = -Widget.EXPANDED_WIDTH;
|
||||
Tweener.addTween(this._ebox, { x: 0,
|
||||
time: ANIMATION_TIME / 2,
|
||||
@ -222,19 +239,27 @@ WidgetBox.prototype = {
|
||||
this.state = this._widget.state = Widget.STATE_COLLAPSING;
|
||||
},
|
||||
|
||||
_collapsePart1Complete: function() {
|
||||
_setWidgetCollapsed: function() {
|
||||
this._egroup.hide();
|
||||
this._ebox.x = 0;
|
||||
this._cgroup.show();
|
||||
|
||||
if (this._singleActor) {
|
||||
this._widget.actor.unparent();
|
||||
this._cbox.append(this._widget.actor, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
|
||||
if (this._htitle) {
|
||||
this._htitle.hide();
|
||||
this._hline.hide();
|
||||
}
|
||||
|
||||
if (this._singleActor) {
|
||||
log(this._widget.actor);
|
||||
this._widget.actor.unparent();
|
||||
this._cbox.append(this._widget.actor, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
if (this._vtitle)
|
||||
this._cbox.height = this._ebox.height;
|
||||
},
|
||||
|
||||
_collapsePart1Complete: function() {
|
||||
this._ebox.x = 0;
|
||||
this._setWidgetCollapsed();
|
||||
|
||||
if (this._widget.collapse) {
|
||||
try {
|
||||
@ -244,10 +269,7 @@ WidgetBox.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
this._cgroup.show();
|
||||
this._cbox.x = -Widget.COLLAPSED_WIDTH;
|
||||
if (this._vtitle)
|
||||
this._cbox.height = this._ebox.height;
|
||||
Tweener.addTween(this._cbox, { x: 0,
|
||||
time: ANIMATION_TIME / 2,
|
||||
transition: "easeOutQuad",
|
||||
|
@ -11,6 +11,7 @@ const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const DND = imports.ui.dnd;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
const Main = imports.ui.main;
|
||||
@ -34,11 +35,11 @@ FRAME_COLOR.from_pixel(0xffffffff);
|
||||
// Each triplet is [xCenter, yCenter, scale] where the scale
|
||||
// is relative to the width of the workspace.
|
||||
const POSITIONS = {
|
||||
1: [[0.5, 0.5, 0.8]],
|
||||
2: [[0.25, 0.5, 0.4], [0.75, 0.5, 0.4]],
|
||||
3: [[0.25, 0.25, 0.33], [0.75, 0.25, 0.33], [0.5, 0.75, 0.33]],
|
||||
4: [[0.25, 0.25, 0.33], [0.75, 0.25, 0.33], [0.75, 0.75, 0.33], [0.25, 0.75, 0.33]],
|
||||
5: [[0.165, 0.25, 0.28], [0.495, 0.25, 0.28], [0.825, 0.25, 0.28], [0.25, 0.75, 0.4], [0.75, 0.75, 0.4]]
|
||||
1: [[0.5, 0.5, 0.95]],
|
||||
2: [[0.25, 0.5, 0.48], [0.75, 0.5, 0.48]],
|
||||
3: [[0.25, 0.25, 0.48], [0.75, 0.25, 0.48], [0.5, 0.75, 0.48]],
|
||||
4: [[0.25, 0.25, 0.47], [0.75, 0.25, 0.47], [0.75, 0.75, 0.47], [0.25, 0.75, 0.47]],
|
||||
5: [[0.165, 0.25, 0.32], [0.495, 0.25, 0.32], [0.825, 0.25, 0.32], [0.25, 0.75, 0.32], [0.75, 0.75, 0.32]]
|
||||
};
|
||||
|
||||
// Spacing between workspaces. At the moment, the same spacing is used
|
||||
@ -120,9 +121,10 @@ WindowClone.prototype = {
|
||||
_onDragBegin : function (draggable, time) {
|
||||
this._inDrag = true;
|
||||
this._updateTitle();
|
||||
this.emit('drag-begin');
|
||||
},
|
||||
|
||||
_onDragEnd : function (draggable, time) {
|
||||
_onDragEnd : function (draggable, time, snapback) {
|
||||
this._inDrag = false;
|
||||
|
||||
// Most likely, the clone is going to move away from the
|
||||
@ -132,6 +134,8 @@ WindowClone.prototype = {
|
||||
// better to have the label mysteriously missing than
|
||||
// mysteriously present
|
||||
this._havePointer = false;
|
||||
|
||||
this.emit('drag-end');
|
||||
},
|
||||
|
||||
// Called by Tweener
|
||||
@ -154,16 +158,7 @@ WindowClone.prototype = {
|
||||
padding: 4,
|
||||
spacing: 4,
|
||||
orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
|
||||
let icon = this.metaWindow.mini_icon;
|
||||
let iconTexture = new Clutter.Texture({ x: this.actor.x,
|
||||
y: this.actor.y + this.actor.height - 16,
|
||||
width: 16,
|
||||
height: 16,
|
||||
keep_aspect_ratio: true });
|
||||
Shell.clutter_texture_set_from_pixbuf(iconTexture, icon);
|
||||
box.append(iconTexture, Big.BoxPackFlags.NONE);
|
||||
|
||||
|
||||
let title = new Clutter.Text({ color: WINDOWCLONE_TITLE_COLOR,
|
||||
font_name: "Sans 12",
|
||||
text: this.metaWindow.title,
|
||||
@ -257,18 +252,26 @@ DesktopClone.prototype = {
|
||||
Signals.addSignalMethods(DesktopClone.prototype);
|
||||
|
||||
|
||||
function Workspace(workspaceNum) {
|
||||
this._init(workspaceNum);
|
||||
/**
|
||||
* @workspaceNum: Workspace index
|
||||
* @parentActor: The actor which will be the parent of this workspace;
|
||||
* we need this in order to add chrome such as the icons
|
||||
* on top of the windows without having them be scaled.
|
||||
*/
|
||||
function Workspace(workspaceNum, parentActor) {
|
||||
this._init(workspaceNum, parentActor);
|
||||
}
|
||||
|
||||
Workspace.prototype = {
|
||||
_init : function(workspaceNum) {
|
||||
_init : function(workspaceNum, parentActor) {
|
||||
let me = this;
|
||||
let global = Shell.Global.get();
|
||||
|
||||
this.workspaceNum = workspaceNum;
|
||||
this._metaWorkspace = global.screen.get_workspace_by_index(workspaceNum);
|
||||
|
||||
this.parentActor = parentActor;
|
||||
|
||||
this.actor = new Clutter.Group();
|
||||
this.actor._delegate = this;
|
||||
this.scale = 1.0;
|
||||
@ -297,6 +300,7 @@ Workspace.prototype = {
|
||||
// Create clones for remaining windows that should be
|
||||
// visible in the overlay
|
||||
this._windows = [this._desktop];
|
||||
this._windowIcons = [ null ];
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
if (this._isOverlayWindow(windows[i])) {
|
||||
this._addWindowClone(windows[i]);
|
||||
@ -320,6 +324,7 @@ Workspace.prototype = {
|
||||
updateRemovable : function() {
|
||||
let global = Shell.Global.get();
|
||||
let removable = (this._windows.length == 1 /* just desktop */ &&
|
||||
this.workspaceNum != 0 &&
|
||||
this.workspaceNum == global.screen.n_workspaces - 1);
|
||||
|
||||
if (removable) {
|
||||
@ -362,6 +367,21 @@ Workspace.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_lookupIndex: function (metaWindow) {
|
||||
let index, clone;
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
if (this._windows[i].metaWindow == metaWindow) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
lookupCloneForMetaWindow: function (metaWindow) {
|
||||
let index = this._lookupIndex (metaWindow);
|
||||
return index < 0 ? null : this._windows[index];
|
||||
},
|
||||
|
||||
_adjustRemoveButton : function() {
|
||||
this._removeButton.set_scale(1.0 / this.actor.scale_x,
|
||||
1.0 / this.actor.scale_y);
|
||||
@ -391,13 +411,11 @@ Workspace.prototype = {
|
||||
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
|
||||
this._frame.lower_bottom();
|
||||
|
||||
this._framePosHandler = this.actor.connect('notify::x', Lang.bind(this, this._updateFramePosition));
|
||||
this._frameSizeHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFrameSize));
|
||||
this._framePosHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFramePosition));
|
||||
} else {
|
||||
if (!this._frame)
|
||||
return;
|
||||
this.actor.disconnect(this._framePosHandler);
|
||||
this.actor.disconnect(this._frameSizeHandler);
|
||||
this._frame.destroy();
|
||||
this._frame = null;
|
||||
}
|
||||
@ -406,9 +424,6 @@ Workspace.prototype = {
|
||||
_updateFramePosition : function() {
|
||||
this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x,
|
||||
this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y);
|
||||
},
|
||||
|
||||
_updateFrameSize : function() {
|
||||
this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x,
|
||||
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
|
||||
},
|
||||
@ -416,51 +431,103 @@ Workspace.prototype = {
|
||||
// Reposition all windows in their zoomed-to-overlay position. if workspaceZooming
|
||||
// is true, then the workspace is moving at the same time and we need to take
|
||||
// that into account
|
||||
_positionWindows : function(workspaceZooming) {
|
||||
positionWindows : function(workspaceZooming) {
|
||||
let global = Shell.Global.get();
|
||||
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
let icon = this._windowIcons[i];
|
||||
clone.stackAbove = this._windows[i - 1].actor;
|
||||
|
||||
let [xCenter, yCenter, fraction] = this._computeWindowPosition(i);
|
||||
xCenter = xCenter * global.screen_width;
|
||||
yCenter = yCenter * global.screen_height;
|
||||
|
||||
let size = Math.max(clone.actor.width, clone.actor.height);
|
||||
let desiredSize = global.screen_width * fraction;
|
||||
let scale = Math.min(desiredSize / size, 1.0 / this.scale);
|
||||
// clone.actor.width/height aren't reliably set at this point for
|
||||
// a new window - they're only set when the window contents are
|
||||
// initially updated prior to painting.
|
||||
let cloneRect = new Meta.Rectangle();
|
||||
clone.realWindow.meta_window.get_outer_rect(cloneRect);
|
||||
|
||||
let desiredWidth = global.screen_width * fraction;
|
||||
let desiredHeight = global.screen_height * fraction;
|
||||
let scale = Math.min(desiredWidth / cloneRect.width, desiredHeight / cloneRect.height, 1.0 / this.scale);
|
||||
|
||||
icon.hide();
|
||||
Tweener.addTween(clone.actor,
|
||||
{ x: xCenter - 0.5 * scale * clone.actor.width,
|
||||
y: yCenter - 0.5 * scale * clone.actor.height,
|
||||
{ x: xCenter - 0.5 * scale * cloneRect.width,
|
||||
y: yCenter - 0.5 * scale * cloneRect.height,
|
||||
scale_x: scale,
|
||||
scale_y: scale,
|
||||
workspace_relative: workspaceZooming ? this : null,
|
||||
time: Overlay.ANIMATION_TIME,
|
||||
transition: "easeOutQuad"
|
||||
transition: "easeOutQuad",
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._fadeInWindowIcon(clone, icon);
|
||||
})
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_fadeInWindowIcon: function (clone, icon) {
|
||||
icon.opacity = 0;
|
||||
icon.show();
|
||||
// This is a little messy and complicated because when we
|
||||
// start the fade-in we may not have done the final positioning
|
||||
// of the workspaces. (Tweener doesn't necessarily finish
|
||||
// all animations before calling onComplete callbacks.)
|
||||
// So we need to manually compute where the window will
|
||||
// be after the workspace animation finishes.
|
||||
let [parentX, parentY] = icon.get_parent().get_position();
|
||||
let [cloneX, cloneY] = clone.actor.get_position();
|
||||
let [cloneWidth, cloneHeight] = clone.actor.get_size();
|
||||
cloneX = this.gridX + this.scale * cloneX;
|
||||
cloneY = this.gridY + this.scale * cloneY;
|
||||
cloneWidth = this.scale * clone.actor.scale_x * cloneWidth;
|
||||
cloneHeight = this.scale * clone.actor.scale_y * cloneHeight;
|
||||
// Note we only round the first part, because we're still going to be
|
||||
// positioned relative to the parent. By subtracting a possibly
|
||||
// non-integral parent X/Y we cancel it out.
|
||||
let x = Math.round(cloneX + cloneWidth - icon.width) - parentX;
|
||||
let y = Math.round(cloneY + cloneHeight - icon.height) - parentY;
|
||||
icon.set_position(x, y);
|
||||
icon.raise(this.actor);
|
||||
Tweener.addTween(icon,
|
||||
{ opacity: 255,
|
||||
time: Overlay.ANIMATION_TIME,
|
||||
transition: "easeOutQuad" });
|
||||
},
|
||||
|
||||
_fadeInAllIcons: function () {
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
let icon = this._windowIcons[i];
|
||||
this._fadeInWindowIcon(clone, icon);
|
||||
}
|
||||
},
|
||||
|
||||
_hideAllIcons: function () {
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
let icon = this._windowIcons[i];
|
||||
icon.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_windowRemoved : function(metaWorkspace, metaWin) {
|
||||
let global = Shell.Global.get();
|
||||
let win = metaWin.get_compositor_private();
|
||||
|
||||
// find the position of the window in our list
|
||||
let index = - 1, clone;
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
if (this._windows[i].metaWindow == metaWin) {
|
||||
index = i;
|
||||
clone = this._windows[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
let index = this._lookupIndex (metaWin);
|
||||
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
let clone = this._windows[index];
|
||||
let icon = this._windowIcons[index];
|
||||
|
||||
this._windows.splice(index, 1);
|
||||
this._windowIcons.splice(index, 1);
|
||||
|
||||
// If metaWin.get_compositor_private() returned non-NULL, that
|
||||
// means the window still exists (and is just being moved to
|
||||
@ -478,8 +545,9 @@ Workspace.prototype = {
|
||||
};
|
||||
}
|
||||
clone.destroy();
|
||||
icon.destroy();
|
||||
|
||||
this._positionWindows(false);
|
||||
this.positionWindows(false);
|
||||
this.updateRemovable();
|
||||
},
|
||||
|
||||
@ -516,7 +584,7 @@ Workspace.prototype = {
|
||||
clone.actor.set_scale (scale, scale);
|
||||
}
|
||||
|
||||
this._positionWindows(false);
|
||||
this.positionWindows(false);
|
||||
this.updateRemovable();
|
||||
},
|
||||
|
||||
@ -542,13 +610,15 @@ Workspace.prototype = {
|
||||
});
|
||||
|
||||
// Likewise for each of the windows in the workspace.
|
||||
this._positionWindows(true);
|
||||
this.positionWindows(true);
|
||||
},
|
||||
|
||||
// Animates the return from overlay mode
|
||||
zoomFromOverlay : function() {
|
||||
this.leavingOverlay = true;
|
||||
|
||||
this.leavingOverlay = true;
|
||||
|
||||
this._hideAllIcons();
|
||||
|
||||
Tweener.addTween(this.actor,
|
||||
{ x: this.fullSizeX,
|
||||
y: this.fullSizeY,
|
||||
@ -580,16 +650,18 @@ Workspace.prototype = {
|
||||
// Animates grid shrinking/expanding when a row or column
|
||||
// of workspaces is added or removed
|
||||
resizeToGrid : function (oldScale) {
|
||||
this._hideAllIcons();
|
||||
Tweener.addTween(this.actor,
|
||||
{ x: this.gridX,
|
||||
y: this.gridY,
|
||||
scale_x: this.scale,
|
||||
scale_y: this.scale,
|
||||
time: Overlay.ANIMATION_TIME,
|
||||
transition: "easeOutQuad"
|
||||
transition: "easeOutQuad",
|
||||
onComplete: Lang.bind(this, this._fadeInAllIcons)
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
// Animates the addition of a new (empty) workspace
|
||||
slideIn : function(oldScale) {
|
||||
let global = Shell.Global.get();
|
||||
@ -618,6 +690,8 @@ Workspace.prototype = {
|
||||
let global = Shell.Global.get();
|
||||
let destX = this.actor.x, destY = this.actor.y;
|
||||
|
||||
this._hideAllIcons();
|
||||
|
||||
if (this.gridCol > this.gridRow)
|
||||
destX = global.screen_width;
|
||||
else
|
||||
@ -670,16 +744,47 @@ Workspace.prototype = {
|
||||
return !win.is_override_redirect();
|
||||
},
|
||||
|
||||
_createWindowIcon: function(window) {
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let appMon = Shell.AppMonitor.get_default()
|
||||
let appInfo = appMon.get_window_app(window.metaWindow);
|
||||
let iconTexture = null;
|
||||
// The design is application based, so prefer the application
|
||||
// icon here if we have it. FIXME - should move this fallback code
|
||||
// into ShellAppMonitor.
|
||||
if (appInfo) {
|
||||
iconTexture = appInfo.create_icon_texture(48);
|
||||
} else {
|
||||
let icon = window.metaWindow.icon;
|
||||
iconTexture = new Clutter.Texture({ width: 48,
|
||||
height: 48,
|
||||
keep_aspect_ratio: true });
|
||||
Shell.clutter_texture_set_from_pixbuf(iconTexture, icon);
|
||||
}
|
||||
return iconTexture;
|
||||
},
|
||||
|
||||
// Create a clone of a (non-desktop) window and add it to the window list
|
||||
_addWindowClone : function(win) {
|
||||
let icon = this._createWindowIcon(win);
|
||||
this.parentActor.add_actor(icon);
|
||||
|
||||
let clone = new WindowClone(win);
|
||||
clone.connect('selected',
|
||||
Lang.bind(this, this._onCloneSelected));
|
||||
clone.connect('dragged',
|
||||
Lang.bind(this, this._onCloneDragged));
|
||||
clone.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
icon.hide();
|
||||
}));
|
||||
clone.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
icon.show();
|
||||
}));
|
||||
|
||||
this.actor.add_actor(clone.actor);
|
||||
|
||||
this._windows.push(clone);
|
||||
this._windowIcons.push(icon);
|
||||
|
||||
return clone;
|
||||
},
|
||||
@ -697,7 +802,7 @@ Workspace.prototype = {
|
||||
let gridWidth = Math.ceil(Math.sqrt(numberOfWindows));
|
||||
let gridHeight = Math.ceil(numberOfWindows / gridWidth);
|
||||
|
||||
let fraction = Math.sqrt(.5/(gridWidth * gridHeight));
|
||||
let fraction = 0.95 * (1. / gridWidth);
|
||||
|
||||
let xCenter = (.5 / gridWidth) + ((windowIndex) % gridWidth) / gridWidth;
|
||||
let yCenter = (.5 / gridHeight) + Math.floor((windowIndex / gridWidth)) / gridHeight;
|
||||
@ -705,20 +810,8 @@ Workspace.prototype = {
|
||||
return [xCenter, yCenter, fraction];
|
||||
},
|
||||
|
||||
_onCloneDragged : function (clone, stageX, stageY, time) {
|
||||
this.emit('window-dragged', clone, stageX, stageY, time);
|
||||
},
|
||||
|
||||
_onCloneSelected : function (clone, time) {
|
||||
let global = Shell.Global.get();
|
||||
let activeWorkspace = global.screen.get_active_workspace_index();
|
||||
|
||||
if (this.workspaceNum != activeWorkspace) {
|
||||
let workspace = global.screen.get_workspace_by_index(this.workspaceNum);
|
||||
workspace.activate_with_focus(clone.metaWindow, time);
|
||||
} else
|
||||
clone.metaWindow.activate(time);
|
||||
Main.overlay.hide();
|
||||
Main.overlay.activateWindow(clone.metaWindow, time);
|
||||
},
|
||||
|
||||
_removeSelf : function(actor, event) {
|
||||
@ -752,7 +845,7 @@ Workspace.prototype = {
|
||||
false, // don't create workspace
|
||||
time);
|
||||
return true;
|
||||
} else if (source instanceof GenericDisplay.GenericDisplayItem) {
|
||||
} else if (source instanceof GenericDisplay.GenericDisplayItem || source instanceof AppDisplay.WellDisplayItem) {
|
||||
this._metaWorkspace.activate(time);
|
||||
source.launch();
|
||||
return true;
|
||||
@ -826,6 +919,32 @@ Workspaces.prototype = {
|
||||
Lang.bind(this, this._activeWorkspaceChanged));
|
||||
},
|
||||
|
||||
_lookupCloneForMetaWindow: function (metaWindow) {
|
||||
for (let i = 0; i < this._workspaces.length; i++) {
|
||||
let clone = this._workspaces[i].lookupCloneForMetaWindow(metaWindow);
|
||||
if (clone)
|
||||
return clone;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// Should only be called from active overlay context
|
||||
activateWindowFromOverlay: function (metaWindow, time) {
|
||||
let global = Shell.Global.get();
|
||||
let activeWorkspaceNum = global.screen.get_active_workspace_index();
|
||||
let windowWorkspaceNum = metaWindow.get_workspace().index();
|
||||
|
||||
let clone = this._lookupCloneForMetaWindow (metaWindow);
|
||||
clone.actor.raise_top();
|
||||
|
||||
if (windowWorkspaceNum != activeWorkspaceNum) {
|
||||
let workspace = global.screen.get_workspace_by_index(windowWorkspaceNum);
|
||||
workspace.activate_with_focus(metaWindow, time);
|
||||
} else {
|
||||
metaWindow.activate(time);
|
||||
}
|
||||
},
|
||||
|
||||
// Updates position of the workspaces display based on the new coordinates.
|
||||
// Preserves the old value for the coordinate, if the passed value is null.
|
||||
updatePosition : function(x, y) {
|
||||
@ -1001,6 +1120,14 @@ Workspaces.prototype = {
|
||||
this._workspaces[w].resizeToGrid(oldScale);
|
||||
}
|
||||
|
||||
if (newScale != oldScale) {
|
||||
// The workspace scale affects window size/positioning because we clamp
|
||||
// window size to a 1:1 ratio and never scale them up
|
||||
let existingWorkspaces = Math.min(oldNumWorkspaces, newNumWorkspaces);
|
||||
for (let w = 0; w < existingWorkspaces; w++)
|
||||
this._workspaces[w].positionWindows(false);
|
||||
}
|
||||
|
||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
||||
// Slide new workspaces in from offscreen
|
||||
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
||||
@ -1022,7 +1149,7 @@ Workspaces.prototype = {
|
||||
},
|
||||
|
||||
_addWorkspaceActor : function(workspaceNum) {
|
||||
let workspace = new Workspace(workspaceNum);
|
||||
let workspace = new Workspace(workspaceNum, this.actor);
|
||||
this._workspaces[workspaceNum] = workspace;
|
||||
this.actor.add_actor(workspace.actor);
|
||||
},
|
||||
|
@ -33,16 +33,16 @@ big_source_c = \
|
||||
big-enum-types.h: stamp-big-enum-types.h Makefile
|
||||
@true
|
||||
stamp-big-enum-types.h: $(big_source_h) big/big-enum-types.h.in
|
||||
( cd $(srcdir) && \
|
||||
$(AM_V_GEN) ( cd $(srcdir) && \
|
||||
$(GLIB_MKENUMS) \
|
||||
--template $(srcdir)/big/big-enum-types.h.in \
|
||||
$(big_source_h) ) >> xgen-teth && \
|
||||
(cmp xgen-teth big-enum-types.h || cp xgen-teth big-enum-types.h) && \
|
||||
(cmp -s xgen-teth big-enum-types.h || cp xgen-teth big-enum-types.h) && \
|
||||
rm -f xgen-teth && \
|
||||
echo timestamp > $(@F)
|
||||
|
||||
big-enum-types.c: stamp-big-enum-types.h big/big-enum-types.c.in
|
||||
( cd $(srcdir) && \
|
||||
$(AM_V_GEN) ( cd $(srcdir) && \
|
||||
$(GLIB_MKENUMS) \
|
||||
--template $(srcdir)/big/big-enum-types.c.in \
|
||||
$(big_source_h) ) >> xgen-tetc && \
|
||||
|
@ -1,17 +0,0 @@
|
||||
gnomeshell_taskpanel_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src \
|
||||
-DG_DISABLE_DEPRECATED \
|
||||
-DWNCK_I_KNOW_THIS_IS_UNSTABLE \
|
||||
-DG_LOG_DOMAIN=\"gnomeshell-taskpanel\" \
|
||||
$(TASKPANEL_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
gnomeshell_taskpanel_SOURCES = \
|
||||
gnomeshell-taskpanel.c \
|
||||
shell-panel-window.c \
|
||||
shell-panel-window.h \
|
||||
$(NULL)
|
||||
|
||||
gnomeshell_taskpanel_LDADD = $(TASKPANEL_LIBS)
|
||||
|
||||
libexec_PROGRAMS += gnomeshell-taskpanel
|
@ -33,7 +33,7 @@ tidy_source_c = \
|
||||
tidy-marshal.h: stamp-tidy-marshal.h
|
||||
@true
|
||||
stamp-tidy-marshal.h: Makefile tidy/tidy-marshal.list
|
||||
$(GLIB_GENMARSHAL) \
|
||||
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
|
||||
--prefix=_tidy_marshal \
|
||||
--header \
|
||||
$(srcdir)/tidy/tidy-marshal.list > xgen-tmh && \
|
||||
@ -42,7 +42,7 @@ stamp-tidy-marshal.h: Makefile tidy/tidy-marshal.list
|
||||
echo timestamp > $(@F)
|
||||
|
||||
tidy-marshal.c: Makefile tidy/tidy-marshal.list
|
||||
(echo "#include \"tidy-marshal.h\"" ; \
|
||||
$(AM_V_GEN) (echo "#include \"tidy-marshal.h\"" ; \
|
||||
$(GLIB_GENMARSHAL) \
|
||||
--prefix=_tidy_marshal \
|
||||
--body \
|
||||
@ -53,16 +53,16 @@ tidy-marshal.c: Makefile tidy/tidy-marshal.list
|
||||
tidy-enum-types.h: stamp-tidy-enum-types.h Makefile
|
||||
@true
|
||||
stamp-tidy-enum-types.h: $(tidy_source_h) tidy/tidy-enum-types.h.in
|
||||
( cd $(srcdir) && \
|
||||
$(AM_V_GEN) ( cd $(srcdir) && \
|
||||
$(GLIB_MKENUMS) \
|
||||
--template $(srcdir)/tidy/tidy-enum-types.h.in \
|
||||
$(tidy_source_h) ) >> xgen-teth && \
|
||||
(cmp xgen-teth tidy-enum-types.h || cp xgen-teth tidy-enum-types.h) && \
|
||||
(cmp -s xgen-teth tidy-enum-types.h || cp xgen-teth tidy-enum-types.h) && \
|
||||
rm -f xgen-teth && \
|
||||
echo timestamp > $(@F)
|
||||
|
||||
tidy-enum-types.c: stamp-tidy-enum-types.h tidy/tidy-enum-types.c.in
|
||||
( cd $(srcdir) && \
|
||||
$(AM_V_GEN) ( cd $(srcdir) && \
|
||||
$(GLIB_MKENUMS) \
|
||||
--template $(srcdir)/tidy/tidy-enum-types.c.in \
|
||||
$(tidy_source_h) ) >> xgen-tetc && \
|
||||
|
@ -24,7 +24,7 @@ tray_source = \
|
||||
na-marshal.h: stamp-na-marshal.h
|
||||
@true
|
||||
stamp-na-marshal.h: Makefile tray/na-marshal.list
|
||||
$(GLIB_GENMARSHAL) \
|
||||
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
|
||||
--prefix=_na_marshal \
|
||||
--header \
|
||||
$(srcdir)/tray/na-marshal.list > xgen-tmh && \
|
||||
@ -33,7 +33,7 @@ stamp-na-marshal.h: Makefile tray/na-marshal.list
|
||||
echo timestamp > $(@F)
|
||||
|
||||
na-marshal.c: Makefile tray/na-marshal.list
|
||||
(echo "#include \"na-marshal.h\"" ; \
|
||||
$(AM_V_GEN) (echo "#include \"na-marshal.h\"" ; \
|
||||
$(GLIB_GENMARSHAL) \
|
||||
--prefix=_na_marshal \
|
||||
--body \
|
||||
|
@ -8,12 +8,13 @@ noinst_LTLIBRARIES =
|
||||
bin_SCRIPTS = gnome-shell
|
||||
|
||||
gnome-shell: gnome-shell.in
|
||||
sed -e "s|@MUTTER_BIN_DIR[@]|$(MUTTER_BIN_DIR)|" \
|
||||
$(AM_V_GEN) sed -e "s|@MUTTER_BIN_DIR[@]|$(MUTTER_BIN_DIR)|" \
|
||||
-e "s|@GJS_JS_DIR[@]|$(GJS_JS_DIR)|" \
|
||||
-e "s|@GJS_JS_NATIVE_DIR[@]|$(GJS_JS_NATIVE_DIR)|" \
|
||||
-e "s|@libexecdir[@]|$(libexecdir)|" \
|
||||
-e "s|@libdir[@]|$(libdir)|" \
|
||||
-e "s|@pkgdatadir[@]|$(pkgdatadir)|" \
|
||||
-e "s|@sysconfdir[@]|$(sysconfdir)|" \
|
||||
$< > $@ && chmod a+x $@
|
||||
CLEANFILES += gnome-shell
|
||||
EXTRA_DIST += gnome-shell.in
|
||||
@ -22,7 +23,6 @@ include Makefile-tidy.am
|
||||
include Makefile-big.am
|
||||
include Makefile-gdmuser.am
|
||||
include Makefile-tray.am
|
||||
include Makefile-taskpanel.am
|
||||
|
||||
gnome_shell_cflags = \
|
||||
$(MUTTER_PLUGIN_CFLAGS) \
|
||||
@ -57,21 +57,35 @@ libgnome_shell_la_SOURCES = \
|
||||
shell-app-system.h \
|
||||
shell-arrow.c \
|
||||
shell-arrow.h \
|
||||
shell-drawing.c \
|
||||
shell-drawing.h \
|
||||
shell-drawing-area.c \
|
||||
shell-drawing-area.h \
|
||||
shell-embedded-window.c \
|
||||
shell-embedded-window.h \
|
||||
shell-embedded-window-private.h \
|
||||
shell-gconf.c \
|
||||
shell-gconf.h \
|
||||
shell-generic-container.c \
|
||||
shell-generic-container.h \
|
||||
shell-gtk-embed.c \
|
||||
shell-gtk-embed.h \
|
||||
shell-overflow-list.c \
|
||||
shell-overflow-list.h \
|
||||
shell-process.c \
|
||||
shell-process.h \
|
||||
shell-global.c \
|
||||
shell-global.h \
|
||||
shell-status-menu.c \
|
||||
shell-status-menu.h \
|
||||
shell-stack.c \
|
||||
shell-stack.h \
|
||||
shell-tray-manager.c \
|
||||
shell-tray-manager.h \
|
||||
shell-texture-cache.c \
|
||||
shell-texture-cache.h \
|
||||
shell-uri-util.c \
|
||||
shell-uri-util.h \
|
||||
shell-wm.c \
|
||||
shell-wm.h
|
||||
|
||||
@ -109,7 +123,7 @@ libgnome_shell_la_gir_sources = \
|
||||
shell-marshal.h: stamp-shell-marshal.h
|
||||
@true
|
||||
stamp-shell-marshal.h: Makefile shell-marshal.list
|
||||
$(GLIB_GENMARSHAL) \
|
||||
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
|
||||
--prefix=_shell_marshal \
|
||||
--header \
|
||||
$(srcdir)/shell-marshal.list > xgen-tmh && \
|
||||
@ -118,7 +132,7 @@ stamp-shell-marshal.h: Makefile shell-marshal.list
|
||||
echo timestamp > $(@F)
|
||||
|
||||
shell-marshal.c: Makefile shell-marshal.list
|
||||
(echo "#include \"shell-marshal.h\"" ; \
|
||||
$(AM_V_GEN) (echo "#include \"shell-marshal.h\"" ; \
|
||||
$(GLIB_GENMARSHAL) \
|
||||
--prefix=_shell_marshal \
|
||||
--body \
|
||||
@ -140,11 +154,11 @@ typelibdir = $(pkglibdir)
|
||||
typelib_DATA = Shell-0.1.typelib Tidy-1.0.typelib Big-1.0.typelib
|
||||
|
||||
Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
|
||||
$(G_IR_SCANNER) \
|
||||
$(AM_V_GEN) $(G_IR_SCANNER) \
|
||||
--namespace=Shell \
|
||||
--nsversion=0.1 \
|
||||
--add-include-path=$(MUTTER_LIB_DIR)/mutter/ \
|
||||
--include=Clutter-0.9 \
|
||||
--include=Clutter-1.0 \
|
||||
--include=Meta-2.27 \
|
||||
--add-include-path=$(builddir) \
|
||||
--include=Big-1.0 \
|
||||
@ -162,10 +176,10 @@ Shell-0.1.typelib: libgnome-shell.la Shell-0.1.gir Big-1.0.gir
|
||||
CLEANFILES += Shell-0.1.typelib
|
||||
|
||||
Tidy-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libtidy-1.0.la Makefile
|
||||
$(G_IR_SCANNER) \
|
||||
$(AM_V_GEN) $(G_IR_SCANNER) \
|
||||
--namespace=Tidy \
|
||||
--nsversion=1.0 \
|
||||
--include=Clutter-0.9 \
|
||||
--include=Clutter-1.0 \
|
||||
--program=mutter \
|
||||
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
|
||||
$(addprefix $(srcdir)/,$(tidy_source_h)) \
|
||||
@ -180,10 +194,10 @@ Tidy-1.0.typelib: libtidy-1.0.la Tidy-1.0.gir
|
||||
CLEANFILES += Tidy-1.0.typelib
|
||||
|
||||
Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la Makefile
|
||||
$(G_IR_SCANNER) \
|
||||
--namespace=Big \
|
||||
$(AM_V_GEN) $(G_IR_SCANNER) \
|
||||
--namespace=Big \
|
||||
--nsversion=1.0 \
|
||||
--include=Clutter-0.9 \
|
||||
--include=Clutter-1.0 \
|
||||
--include=GdkPixbuf-2.0 \
|
||||
--program=mutter \
|
||||
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
|
||||
|
@ -1,208 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <glib-object.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gdm-user-chooser-widget.h"
|
||||
#include "gdm-user-chooser-dialog.h"
|
||||
|
||||
#define GDM_USER_CHOOSER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialogPrivate))
|
||||
|
||||
struct GdmUserChooserDialogPrivate
|
||||
{
|
||||
GtkWidget *chooser_widget;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
};
|
||||
|
||||
static void gdm_user_chooser_dialog_class_init (GdmUserChooserDialogClass *klass);
|
||||
static void gdm_user_chooser_dialog_init (GdmUserChooserDialog *user_chooser_dialog);
|
||||
static void gdm_user_chooser_dialog_finalize (GObject *object);
|
||||
|
||||
G_DEFINE_TYPE (GdmUserChooserDialog, gdm_user_chooser_dialog, GTK_TYPE_DIALOG)
|
||||
|
||||
char *
|
||||
gdm_user_chooser_dialog_get_chosen_user_name (GdmUserChooserDialog *dialog)
|
||||
{
|
||||
char *user_name;
|
||||
|
||||
g_return_val_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog), NULL);
|
||||
|
||||
user_name = gdm_user_chooser_widget_get_chosen_user_name (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget));
|
||||
|
||||
return user_name;
|
||||
}
|
||||
|
||||
void
|
||||
gdm_user_chooser_dialog_set_show_user_other (GdmUserChooserDialog *dialog,
|
||||
gboolean show_user)
|
||||
{
|
||||
g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog));
|
||||
|
||||
gdm_user_chooser_widget_set_show_user_other (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget), show_user);
|
||||
}
|
||||
|
||||
void
|
||||
gdm_user_chooser_dialog_set_show_user_guest (GdmUserChooserDialog *dialog,
|
||||
gboolean show_user)
|
||||
{
|
||||
g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog));
|
||||
|
||||
gdm_user_chooser_widget_set_show_user_guest (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget), show_user);
|
||||
}
|
||||
|
||||
void
|
||||
gdm_user_chooser_dialog_set_show_user_auto (GdmUserChooserDialog *dialog,
|
||||
gboolean show_user)
|
||||
{
|
||||
g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (dialog));
|
||||
|
||||
gdm_user_chooser_widget_set_show_user_auto (GDM_USER_CHOOSER_WIDGET (dialog->priv->chooser_widget), show_user);
|
||||
}
|
||||
|
||||
static void
|
||||
gdm_user_chooser_dialog_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
switch (prop_id) {
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdm_user_chooser_dialog_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
switch (prop_id) {
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GObject *
|
||||
gdm_user_chooser_dialog_constructor (GType type,
|
||||
guint n_construct_properties,
|
||||
GObjectConstructParam *construct_properties)
|
||||
{
|
||||
GdmUserChooserDialog *user_chooser_dialog;
|
||||
|
||||
user_chooser_dialog = GDM_USER_CHOOSER_DIALOG (G_OBJECT_CLASS (gdm_user_chooser_dialog_parent_class)->constructor (type,
|
||||
n_construct_properties,
|
||||
construct_properties));
|
||||
|
||||
return G_OBJECT (user_chooser_dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
gdm_user_chooser_dialog_dispose (GObject *object)
|
||||
{
|
||||
G_OBJECT_CLASS (gdm_user_chooser_dialog_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gdm_user_chooser_dialog_class_init (GdmUserChooserDialogClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->get_property = gdm_user_chooser_dialog_get_property;
|
||||
object_class->set_property = gdm_user_chooser_dialog_set_property;
|
||||
object_class->constructor = gdm_user_chooser_dialog_constructor;
|
||||
object_class->dispose = gdm_user_chooser_dialog_dispose;
|
||||
object_class->finalize = gdm_user_chooser_dialog_finalize;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GdmUserChooserDialogPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
on_response (GdmUserChooserDialog *dialog,
|
||||
gint response_id)
|
||||
{
|
||||
switch (response_id) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdm_user_chooser_dialog_init (GdmUserChooserDialog *dialog)
|
||||
{
|
||||
|
||||
dialog->priv = GDM_USER_CHOOSER_DIALOG_GET_PRIVATE (dialog);
|
||||
|
||||
dialog->priv->chooser_widget = gdm_user_chooser_widget_new ();
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), dialog->priv->chooser_widget);
|
||||
|
||||
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OK, GTK_RESPONSE_OK,
|
||||
NULL);
|
||||
g_signal_connect (dialog,
|
||||
"response",
|
||||
G_CALLBACK (on_response),
|
||||
dialog);
|
||||
|
||||
gtk_widget_show_all (GTK_WIDGET (dialog));
|
||||
}
|
||||
|
||||
static void
|
||||
gdm_user_chooser_dialog_finalize (GObject *object)
|
||||
{
|
||||
GdmUserChooserDialog *user_chooser_dialog;
|
||||
|
||||
g_return_if_fail (object != NULL);
|
||||
g_return_if_fail (GDM_IS_USER_CHOOSER_DIALOG (object));
|
||||
|
||||
user_chooser_dialog = GDM_USER_CHOOSER_DIALOG (object);
|
||||
|
||||
g_return_if_fail (user_chooser_dialog->priv != NULL);
|
||||
|
||||
G_OBJECT_CLASS (gdm_user_chooser_dialog_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gdm_user_chooser_dialog_new (void)
|
||||
{
|
||||
GObject *object;
|
||||
|
||||
object = g_object_new (GDM_TYPE_USER_CHOOSER_DIALOG,
|
||||
NULL);
|
||||
|
||||
return GTK_WIDGET (object);
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __GDM_USER_CHOOSER_DIALOG_H
|
||||
#define __GDM_USER_CHOOSER_DIALOG_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gtk/gtkdialog.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GDM_TYPE_USER_CHOOSER_DIALOG (gdm_user_chooser_dialog_get_type ())
|
||||
#define GDM_USER_CHOOSER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialog))
|
||||
#define GDM_USER_CHOOSER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialogClass))
|
||||
#define GDM_IS_USER_CHOOSER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_CHOOSER_DIALOG))
|
||||
#define GDM_IS_USER_CHOOSER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_CHOOSER_DIALOG))
|
||||
#define GDM_USER_CHOOSER_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_CHOOSER_DIALOG, GdmUserChooserDialogClass))
|
||||
|
||||
typedef struct GdmUserChooserDialogPrivate GdmUserChooserDialogPrivate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkDialog parent;
|
||||
GdmUserChooserDialogPrivate *priv;
|
||||
} GdmUserChooserDialog;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkDialogClass parent_class;
|
||||
} GdmUserChooserDialogClass;
|
||||
|
||||
GType gdm_user_chooser_dialog_get_type (void);
|
||||
|
||||
GtkWidget * gdm_user_chooser_dialog_new (void);
|
||||
|
||||
char * gdm_user_chooser_dialog_get_chosen_user_name (GdmUserChooserDialog *dialog);
|
||||
void gdm_user_chooser_dialog_set_show_other_user (GdmUserChooserDialog *dialog,
|
||||
gboolean show);
|
||||
void gdm_user_chooser_dialog_set_show_user_guest (GdmUserChooserDialog *dialog,
|
||||
gboolean show);
|
||||
void gdm_user_chooser_dialog_set_show_user_auto (GdmUserChooserDialog *dialog,
|
||||
gboolean show);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDM_USER_CHOOSER_DIALOG_H */
|
@ -1,723 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
|
||||
* Copyright (C) 2007 Ray Strode <rstrode@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <gconf/gconf-client.h>
|
||||
|
||||
#include "gdm-user-manager.h"
|
||||
#include "gdm-user-chooser-widget.h"
|
||||
|
||||
|
||||
#define KEY_DISABLE_USER_LIST "/apps/gdm/simple-greeter/disable_user_list"
|
||||
|
||||
enum {
|
||||
USER_NO_DISPLAY = 1 << 0,
|
||||
USER_ACCOUNT_DISABLED = 1 << 1,
|
||||
};
|
||||
|
||||
#define DEFAULT_USER_ICON "stock_person"
|
||||
|
||||
#define GDM_USER_CHOOSER_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidgetPrivate))
|
||||
|
||||
#define MAX_ICON_SIZE 128
|
||||
|
||||
struct GdmUserChooserWidgetPrivate
|
||||
{
|
||||
GdmUserManager *manager;
|
||||
GtkIconTheme *icon_theme;
|
||||
|
||||
GdkPixbuf *logged_in_pixbuf;
|
||||
GdkPixbuf *stock_person_pixbuf;
|
||||
|
||||
guint loaded : 1;
|
||||
guint show_user_other : 1;
|
||||
guint show_user_guest : 1;
|
||||
guint show_user_auto : 1;
|
||||
guint show_normal_users : 1;
|
||||
|
||||
guint load_idle_id;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_SHOW_USER_GUEST,
|
||||
PROP_SHOW_USER_AUTO,
|
||||
PROP_SHOW_USER_OTHER,
|
||||
};
|
||||
|
||||
static void gdm_user_chooser_widget_class_init (GdmUserChooserWidgetClass *klass);
|
||||
static void gdm_user_chooser_widget_init (GdmUserChooserWidget *user_chooser_widget);
|
||||
static void gdm_user_chooser_widget_finalize (GObject *object);
|
||||
|
||||
G_DEFINE_TYPE (GdmUserChooserWidget, gdm_user_chooser_widget, GDM_TYPE_CHOOSER_WIDGET)
|
||||
|
||||
static int
|
||||
get_font_height_for_widget (GtkWidget *widget)
|
||||
{
|
||||
PangoFontMetrics *metrics;
|
||||
PangoContext *context;
|
||||
int ascent;
|
||||
int descent;
|
||||
int height;
|
||||
|
||||
gtk_widget_ensure_style (widget);
|
||||
context = gtk_widget_get_pango_context (widget);
|
||||
metrics = pango_context_get_metrics (context,
|
||||
widget->style->font_desc,
|
||||
pango_context_get_language (context));
|
||||
|
||||
ascent = pango_font_metrics_get_ascent (metrics);
|
||||
descent = pango_font_metrics_get_descent (metrics);
|
||||
height = PANGO_PIXELS (ascent + descent);
|
||||
pango_font_metrics_unref (metrics);
|
||||
return height;
|
||||
}
|
||||
|
||||
static int
|
||||
get_icon_height_for_widget (GtkWidget *widget)
|
||||
{
|
||||
int font_height;
|
||||
int height;
|
||||
|
||||
font_height = get_font_height_for_widget (widget);
|
||||
height = 3 * font_height;
|
||||
if (height > MAX_ICON_SIZE) {
|
||||
height = MAX_ICON_SIZE;
|
||||
}
|
||||
|
||||
g_debug ("GdmUserChooserWidget: font height %d; using icon size %d", font_height, height);
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
static void
|
||||
add_user_other (GdmUserChooserWidget *widget)
|
||||
{
|
||||
gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
|
||||
GDM_USER_CHOOSER_USER_OTHER,
|
||||
NULL,
|
||||
_("Other..."),
|
||||
_("Choose a different account"),
|
||||
0,
|
||||
FALSE,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
add_user_guest (GdmUserChooserWidget *widget)
|
||||
{
|
||||
gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
|
||||
GDM_USER_CHOOSER_USER_GUEST,
|
||||
widget->priv->stock_person_pixbuf,
|
||||
_("Guest"),
|
||||
_("Login as a temporary guest"),
|
||||
0,
|
||||
FALSE,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
add_user_auto (GdmUserChooserWidget *widget)
|
||||
{
|
||||
gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
|
||||
GDM_USER_CHOOSER_USER_AUTO,
|
||||
NULL,
|
||||
_("Automatic Login"),
|
||||
_("Automatically login to the system after selecting options"),
|
||||
0,
|
||||
FALSE,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_user_other (GdmUserChooserWidget *widget)
|
||||
{
|
||||
gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
|
||||
GDM_USER_CHOOSER_USER_OTHER);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_user_guest (GdmUserChooserWidget *widget)
|
||||
{
|
||||
gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
|
||||
GDM_USER_CHOOSER_USER_GUEST);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_user_auto (GdmUserChooserWidget *widget)
|
||||
{
|
||||
gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
|
||||
GDM_USER_CHOOSER_USER_AUTO);
|
||||
}
|
||||
|
||||
void
|
||||
gdm_user_chooser_widget_set_show_user_other (GdmUserChooserWidget *widget,
|
||||
gboolean show_user)
|
||||
{
|
||||
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
|
||||
|
||||
if (widget->priv->show_user_other != show_user) {
|
||||
widget->priv->show_user_other = show_user;
|
||||
if (show_user) {
|
||||
add_user_other (widget);
|
||||
} else {
|
||||
remove_user_other (widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gdm_user_chooser_widget_set_show_user_guest (GdmUserChooserWidget *widget,
|
||||
gboolean show_user)
|
||||
{
|
||||
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
|
||||
|
||||
if (widget->priv->show_user_guest != show_user) {
|
||||
widget->priv->show_user_guest = show_user;
|
||||
if (show_user) {
|
||||
add_user_guest (widget);
|
||||
} else {
|
||||
remove_user_guest (widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gdm_user_chooser_widget_set_show_user_auto (GdmUserChooserWidget *widget,
|
||||
gboolean show_user)
|
||||
{
|
||||
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
|
||||
|
||||
if (widget->priv->show_user_auto != show_user) {
|
||||
widget->priv->show_user_auto = show_user;
|
||||
if (show_user) {
|
||||
add_user_auto (widget);
|
||||
} else {
|
||||
remove_user_auto (widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
gdm_user_chooser_widget_get_chosen_user_name (GdmUserChooserWidget *widget)
|
||||
{
|
||||
g_return_val_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget), NULL);
|
||||
|
||||
return gdm_chooser_widget_get_active_item (GDM_CHOOSER_WIDGET (widget));
|
||||
}
|
||||
|
||||
void
|
||||
gdm_user_chooser_widget_set_chosen_user_name (GdmUserChooserWidget *widget,
|
||||
const char *name)
|
||||
{
|
||||
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
|
||||
|
||||
gdm_chooser_widget_set_active_item (GDM_CHOOSER_WIDGET (widget), name);
|
||||
}
|
||||
|
||||
void
|
||||
gdm_user_chooser_widget_set_show_only_chosen (GdmUserChooserWidget *widget,
|
||||
gboolean show_only) {
|
||||
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget));
|
||||
|
||||
gdm_chooser_widget_set_hide_inactive_items (GDM_CHOOSER_WIDGET (widget),
|
||||
show_only);
|
||||
|
||||
}
|
||||
static void
|
||||
gdm_user_chooser_widget_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GdmUserChooserWidget *self;
|
||||
|
||||
self = GDM_USER_CHOOSER_WIDGET (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_SHOW_USER_AUTO:
|
||||
gdm_user_chooser_widget_set_show_user_auto (self, g_value_get_boolean (value));
|
||||
break;
|
||||
case PROP_SHOW_USER_GUEST:
|
||||
gdm_user_chooser_widget_set_show_user_guest (self, g_value_get_boolean (value));
|
||||
break;
|
||||
case PROP_SHOW_USER_OTHER:
|
||||
gdm_user_chooser_widget_set_show_user_other (self, g_value_get_boolean (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdm_user_chooser_widget_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GdmUserChooserWidget *self;
|
||||
|
||||
self = GDM_USER_CHOOSER_WIDGET (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_SHOW_USER_AUTO:
|
||||
g_value_set_boolean (value, self->priv->show_user_auto);
|
||||
break;
|
||||
case PROP_SHOW_USER_GUEST:
|
||||
g_value_set_boolean (value, self->priv->show_user_guest);
|
||||
break;
|
||||
case PROP_SHOW_USER_OTHER:
|
||||
g_value_set_boolean (value, self->priv->show_user_other);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_user_list_disabled (GdmUserChooserWidget *widget)
|
||||
{
|
||||
GConfClient *client;
|
||||
GError *error;
|
||||
gboolean result;
|
||||
|
||||
client = gconf_client_get_default ();
|
||||
error = NULL;
|
||||
result = gconf_client_get_bool (client, KEY_DISABLE_USER_LIST, &error);
|
||||
if (error != NULL) {
|
||||
g_debug ("GdmUserChooserWidget: unable to get disable-user-list configuration: %s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
g_object_unref (client);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
add_user (GdmUserChooserWidget *widget,
|
||||
GdmUser *user)
|
||||
{
|
||||
GdkPixbuf *pixbuf;
|
||||
char *tooltip;
|
||||
gboolean is_logged_in;
|
||||
int size;
|
||||
|
||||
if (!widget->priv->show_normal_users) {
|
||||
return;
|
||||
}
|
||||
|
||||
size = get_icon_height_for_widget (widget);
|
||||
pixbuf = gdm_user_render_icon (user, size);
|
||||
if (pixbuf == NULL && widget->priv->stock_person_pixbuf != NULL) {
|
||||
pixbuf = g_object_ref (widget->priv->stock_person_pixbuf);
|
||||
}
|
||||
|
||||
tooltip = g_strdup_printf (_("Log in as %s"),
|
||||
gdm_user_get_user_name (user));
|
||||
|
||||
is_logged_in = gdm_user_get_num_sessions (user) > 0;
|
||||
|
||||
g_debug ("GdmUserChooserWidget: User added name:%s logged-in:%d pixbuf:%p",
|
||||
gdm_user_get_user_name (user),
|
||||
is_logged_in,
|
||||
pixbuf);
|
||||
|
||||
gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget),
|
||||
gdm_user_get_user_name (user),
|
||||
pixbuf,
|
||||
gdm_user_get_real_name (user),
|
||||
tooltip,
|
||||
gdm_user_get_login_frequency (user),
|
||||
is_logged_in,
|
||||
FALSE);
|
||||
g_free (tooltip);
|
||||
|
||||
if (pixbuf != NULL) {
|
||||
g_object_unref (pixbuf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_user_added (GdmUserManager *manager,
|
||||
GdmUser *user,
|
||||
GdmUserChooserWidget *widget)
|
||||
{
|
||||
/* wait for all users to be loaded */
|
||||
if (! widget->priv->loaded) {
|
||||
return;
|
||||
}
|
||||
add_user (widget, user);
|
||||
}
|
||||
|
||||
static void
|
||||
on_user_removed (GdmUserManager *manager,
|
||||
GdmUser *user,
|
||||
GdmUserChooserWidget *widget)
|
||||
{
|
||||
const char *user_name;
|
||||
|
||||
g_debug ("GdmUserChooserWidget: User removed: %s", gdm_user_get_user_name (user));
|
||||
/* wait for all users to be loaded */
|
||||
if (! widget->priv->loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
user_name = gdm_user_get_user_name (user);
|
||||
|
||||
gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget),
|
||||
user_name);
|
||||
}
|
||||
|
||||
static void
|
||||
on_user_is_logged_in_changed (GdmUserManager *manager,
|
||||
GdmUser *user,
|
||||
GdmUserChooserWidget *widget)
|
||||
{
|
||||
const char *user_name;
|
||||
gboolean is_logged_in;
|
||||
|
||||
g_debug ("GdmUserChooserWidget: User logged in changed: %s", gdm_user_get_user_name (user));
|
||||
|
||||
user_name = gdm_user_get_user_name (user);
|
||||
is_logged_in = gdm_user_get_num_sessions (user) > 0;
|
||||
|
||||
gdm_chooser_widget_set_item_in_use (GDM_CHOOSER_WIDGET (widget),
|
||||
user_name,
|
||||
is_logged_in);
|
||||
}
|
||||
|
||||
static void
|
||||
on_user_login_frequency_changed (GdmUserManager *manager,
|
||||
GdmUser *user,
|
||||
GdmUserChooserWidget *widget)
|
||||
{
|
||||
const char *user_name;
|
||||
gulong freq;
|
||||
|
||||
g_debug ("GdmUserChooserWidget: User login frequency changed: %s", gdm_user_get_user_name (user));
|
||||
|
||||
user_name = gdm_user_get_user_name (user);
|
||||
freq = gdm_user_get_login_frequency (user);
|
||||
|
||||
gdm_chooser_widget_set_item_priority (GDM_CHOOSER_WIDGET (widget),
|
||||
user_name,
|
||||
freq);
|
||||
}
|
||||
|
||||
static void
|
||||
on_users_loaded (GdmUserManager *manager,
|
||||
GdmUserChooserWidget *widget)
|
||||
{
|
||||
GSList *users;
|
||||
|
||||
widget->priv->loaded = TRUE;
|
||||
|
||||
g_debug ("GdmUserChooserWidget: Users loaded");
|
||||
|
||||
users = gdm_user_manager_list_users (manager);
|
||||
while (users != NULL) {
|
||||
add_user (widget, users->data);
|
||||
users = g_slist_delete_link (users, users);
|
||||
}
|
||||
|
||||
gtk_widget_grab_focus (GTK_WIDGET (widget));
|
||||
|
||||
gdm_chooser_widget_loaded (GDM_CHOOSER_WIDGET (widget));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
load_users (GdmUserChooserWidget *widget)
|
||||
{
|
||||
|
||||
if (widget->priv->show_normal_users) {
|
||||
widget->priv->manager = gdm_user_manager_ref_default ();
|
||||
g_signal_connect (widget->priv->manager,
|
||||
"user-added",
|
||||
G_CALLBACK (on_user_added),
|
||||
widget);
|
||||
g_signal_connect (widget->priv->manager,
|
||||
"user-removed",
|
||||
G_CALLBACK (on_user_removed),
|
||||
widget);
|
||||
g_signal_connect (widget->priv->manager,
|
||||
"users-loaded",
|
||||
G_CALLBACK (on_users_loaded),
|
||||
widget);
|
||||
g_signal_connect (widget->priv->manager,
|
||||
"user-is-logged-in-changed",
|
||||
G_CALLBACK (on_user_is_logged_in_changed),
|
||||
widget);
|
||||
g_signal_connect (widget->priv->manager,
|
||||
"user-login-frequency-changed",
|
||||
G_CALLBACK (on_user_login_frequency_changed),
|
||||
widget);
|
||||
} else {
|
||||
gdm_chooser_widget_loaded (GDM_CHOOSER_WIDGET (widget));
|
||||
}
|
||||
|
||||
widget->priv->load_idle_id = 0;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GObject *
|
||||
gdm_user_chooser_widget_constructor (GType type,
|
||||
guint n_construct_properties,
|
||||
GObjectConstructParam *construct_properties)
|
||||
{
|
||||
GdmUserChooserWidget *widget;
|
||||
|
||||
widget = GDM_USER_CHOOSER_WIDGET (G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->constructor (type,
|
||||
n_construct_properties,
|
||||
construct_properties));
|
||||
|
||||
widget->priv->show_normal_users = !is_user_list_disabled (widget);
|
||||
|
||||
widget->priv->load_idle_id = g_idle_add ((GSourceFunc)load_users, widget);
|
||||
|
||||
return G_OBJECT (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gdm_user_chooser_widget_dispose (GObject *object)
|
||||
{
|
||||
GdmUserChooserWidget *widget;
|
||||
|
||||
widget = GDM_USER_CHOOSER_WIDGET (object);
|
||||
|
||||
G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->dispose (object);
|
||||
|
||||
if (widget->priv->load_idle_id > 0) {
|
||||
g_source_remove (widget->priv->load_idle_id);
|
||||
widget->priv->load_idle_id = 0;
|
||||
}
|
||||
|
||||
if (widget->priv->logged_in_pixbuf != NULL) {
|
||||
g_object_unref (widget->priv->logged_in_pixbuf);
|
||||
widget->priv->logged_in_pixbuf = NULL;
|
||||
}
|
||||
|
||||
if (widget->priv->stock_person_pixbuf != NULL) {
|
||||
g_object_unref (widget->priv->stock_person_pixbuf);
|
||||
widget->priv->stock_person_pixbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdm_user_chooser_widget_class_init (GdmUserChooserWidgetClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->get_property = gdm_user_chooser_widget_get_property;
|
||||
object_class->set_property = gdm_user_chooser_widget_set_property;
|
||||
object_class->constructor = gdm_user_chooser_widget_constructor;
|
||||
object_class->dispose = gdm_user_chooser_widget_dispose;
|
||||
object_class->finalize = gdm_user_chooser_widget_finalize;
|
||||
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_SHOW_USER_AUTO,
|
||||
g_param_spec_boolean ("show-user-auto",
|
||||
"show user auto",
|
||||
"show user auto",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_SHOW_USER_GUEST,
|
||||
g_param_spec_boolean ("show-user-guest",
|
||||
"show user guest",
|
||||
"show user guest",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_SHOW_USER_OTHER,
|
||||
g_param_spec_boolean ("show-user-other",
|
||||
"show user other",
|
||||
"show user other",
|
||||
TRUE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GdmUserChooserWidgetPrivate));
|
||||
}
|
||||
|
||||
static GdkPixbuf *
|
||||
get_stock_person_pixbuf (GdmUserChooserWidget *widget)
|
||||
{
|
||||
GdkPixbuf *pixbuf;
|
||||
int size;
|
||||
|
||||
size = get_icon_height_for_widget (widget);
|
||||
|
||||
pixbuf = gtk_icon_theme_load_icon (widget->priv->icon_theme,
|
||||
DEFAULT_USER_ICON,
|
||||
size,
|
||||
0,
|
||||
NULL);
|
||||
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
static GdkPixbuf *
|
||||
get_logged_in_pixbuf (GdmUserChooserWidget *widget)
|
||||
{
|
||||
GdkPixbuf *pixbuf;
|
||||
int size;
|
||||
|
||||
size = get_icon_height_for_widget (widget);
|
||||
|
||||
pixbuf = gtk_icon_theme_load_icon (widget->priv->icon_theme,
|
||||
"emblem-default",
|
||||
size / 3,
|
||||
0,
|
||||
NULL);
|
||||
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GdkPixbuf *old_icon;
|
||||
GdkPixbuf *new_icon;
|
||||
} IconUpdateData;
|
||||
|
||||
static gboolean
|
||||
update_icons (GdmChooserWidget *widget,
|
||||
const char *id,
|
||||
GdkPixbuf **image,
|
||||
char **name,
|
||||
char **comment,
|
||||
gulong *priority,
|
||||
gboolean *is_in_use,
|
||||
gboolean *is_separate,
|
||||
IconUpdateData *data)
|
||||
{
|
||||
if (data->old_icon == *image) {
|
||||
*image = data->new_icon;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
load_icons (GdmUserChooserWidget *widget)
|
||||
{
|
||||
GdkPixbuf *old_pixbuf;
|
||||
IconUpdateData data;
|
||||
|
||||
if (widget->priv->logged_in_pixbuf != NULL) {
|
||||
g_object_unref (widget->priv->logged_in_pixbuf);
|
||||
}
|
||||
widget->priv->logged_in_pixbuf = get_logged_in_pixbuf (widget);
|
||||
|
||||
old_pixbuf = widget->priv->stock_person_pixbuf;
|
||||
widget->priv->stock_person_pixbuf = get_stock_person_pixbuf (widget);
|
||||
/* update the icons in the model */
|
||||
data.old_icon = old_pixbuf;
|
||||
data.new_icon = widget->priv->stock_person_pixbuf;
|
||||
gdm_chooser_widget_update_foreach_item (GDM_CHOOSER_WIDGET (widget),
|
||||
(GdmChooserUpdateForeachFunc)update_icons,
|
||||
&data);
|
||||
if (old_pixbuf != NULL) {
|
||||
g_object_unref (old_pixbuf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_icon_theme_changed (GtkIconTheme *icon_theme,
|
||||
GdmUserChooserWidget *widget)
|
||||
{
|
||||
g_debug ("GdmUserChooserWidget: icon theme changed");
|
||||
load_icons (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_icons (GdmUserChooserWidget *widget)
|
||||
{
|
||||
if (gtk_widget_has_screen (GTK_WIDGET (widget))) {
|
||||
widget->priv->icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (widget)));
|
||||
} else {
|
||||
widget->priv->icon_theme = gtk_icon_theme_get_default ();
|
||||
}
|
||||
|
||||
if (widget->priv->icon_theme != NULL) {
|
||||
g_signal_connect (widget->priv->icon_theme,
|
||||
"changed",
|
||||
G_CALLBACK (on_icon_theme_changed),
|
||||
widget);
|
||||
}
|
||||
|
||||
load_icons (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gdm_user_chooser_widget_init (GdmUserChooserWidget *widget)
|
||||
{
|
||||
widget->priv = GDM_USER_CHOOSER_WIDGET_GET_PRIVATE (widget);
|
||||
|
||||
gdm_chooser_widget_set_separator_position (GDM_CHOOSER_WIDGET (widget),
|
||||
GDM_CHOOSER_WIDGET_POSITION_BOTTOM);
|
||||
gdm_chooser_widget_set_in_use_message (GDM_CHOOSER_WIDGET (widget),
|
||||
_("Currently logged in"));
|
||||
|
||||
setup_icons (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gdm_user_chooser_widget_finalize (GObject *object)
|
||||
{
|
||||
GdmUserChooserWidget *widget;
|
||||
|
||||
g_return_if_fail (object != NULL);
|
||||
g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (object));
|
||||
|
||||
widget = GDM_USER_CHOOSER_WIDGET (object);
|
||||
|
||||
g_return_if_fail (widget->priv != NULL);
|
||||
|
||||
G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gdm_user_chooser_widget_new (void)
|
||||
{
|
||||
GObject *object;
|
||||
|
||||
object = g_object_new (GDM_TYPE_USER_CHOOSER_WIDGET,
|
||||
NULL);
|
||||
|
||||
return GTK_WIDGET (object);
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __GDM_USER_CHOOSER_WIDGET_H
|
||||
#define __GDM_USER_CHOOSER_WIDGET_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "gdm-chooser-widget.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GDM_TYPE_USER_CHOOSER_WIDGET (gdm_user_chooser_widget_get_type ())
|
||||
#define GDM_USER_CHOOSER_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidget))
|
||||
#define GDM_USER_CHOOSER_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidgetClass))
|
||||
#define GDM_IS_USER_CHOOSER_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_CHOOSER_WIDGET))
|
||||
#define GDM_IS_USER_CHOOSER_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_CHOOSER_WIDGET))
|
||||
#define GDM_USER_CHOOSER_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidgetClass))
|
||||
|
||||
typedef struct GdmUserChooserWidgetPrivate GdmUserChooserWidgetPrivate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GdmChooserWidget parent;
|
||||
GdmUserChooserWidgetPrivate *priv;
|
||||
} GdmUserChooserWidget;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GdmChooserWidgetClass parent_class;
|
||||
} GdmUserChooserWidgetClass;
|
||||
|
||||
#define GDM_USER_CHOOSER_USER_OTHER "__other"
|
||||
#define GDM_USER_CHOOSER_USER_GUEST "__guest"
|
||||
#define GDM_USER_CHOOSER_USER_AUTO "__auto"
|
||||
|
||||
GType gdm_user_chooser_widget_get_type (void);
|
||||
GtkWidget * gdm_user_chooser_widget_new (void);
|
||||
|
||||
char * gdm_user_chooser_widget_get_chosen_user_name (GdmUserChooserWidget *widget);
|
||||
void gdm_user_chooser_widget_set_chosen_user_name (GdmUserChooserWidget *widget,
|
||||
const char *user_name);
|
||||
void gdm_user_chooser_widget_set_show_only_chosen (GdmUserChooserWidget *widget,
|
||||
gboolean show_only);
|
||||
void gdm_user_chooser_widget_set_show_user_other (GdmUserChooserWidget *widget,
|
||||
gboolean show);
|
||||
void gdm_user_chooser_widget_set_show_user_guest (GdmUserChooserWidget *widget,
|
||||
gboolean show);
|
||||
void gdm_user_chooser_widget_set_show_user_auto (GdmUserChooserWidget *widget,
|
||||
gboolean show);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDM_USER_CHOOSER_WIDGET_H */
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -411,13 +412,16 @@ _gdm_user_update (GdmUser *user,
|
||||
/* Display Name */
|
||||
if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
|
||||
gchar *first_comma;
|
||||
gchar *real_name_utf8;
|
||||
|
||||
first_comma = strchr (pwent->pw_gecos, ',');
|
||||
real_name_utf8 = g_locale_to_utf8 (pwent->pw_gecos, -1, NULL, NULL, NULL);
|
||||
|
||||
first_comma = strchr (real_name_utf8, ',');
|
||||
if (first_comma) {
|
||||
real_name = g_strndup (pwent->pw_gecos,
|
||||
(first_comma - pwent->pw_gecos));
|
||||
real_name = g_strndup (real_name_utf8, first_comma - real_name_utf8);
|
||||
g_free (real_name_utf8);
|
||||
} else {
|
||||
real_name = g_strdup (pwent->pw_gecos);
|
||||
real_name = real_name_utf8;
|
||||
}
|
||||
|
||||
if (real_name[0] == '\0') {
|
||||
@ -881,7 +885,7 @@ curved_rectangle (cairo_t *cr,
|
||||
x1 = x0 + width;
|
||||
y1 = y0 + height;
|
||||
|
||||
if (!width || !height) {
|
||||
if (width < FLT_EPSILON || height < FLT_EPSILON) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1156,8 +1160,8 @@ gdm_user_render_icon (GdmUser *user,
|
||||
} else {
|
||||
pixbuf = NULL;
|
||||
}
|
||||
out:
|
||||
g_free (path);
|
||||
out:
|
||||
|
||||
if (pixbuf != NULL) {
|
||||
framed = frame_pixbuf (pixbuf);
|
||||
|
29
src/gnome-shell.in
Executable file → Normal file
29
src/gnome-shell.in
Executable file → Normal file
@ -13,6 +13,7 @@ import tempfile
|
||||
import termios
|
||||
import time
|
||||
import errno
|
||||
import dbus
|
||||
|
||||
def find_cmd (cmd_list):
|
||||
"""
|
||||
@ -122,25 +123,27 @@ def start_shell():
|
||||
top_dir = os.path.dirname(bin_dir)
|
||||
plugin = os.path.join(top_dir, 'src', 'libgnome-shell.la')
|
||||
typelib_dir = os.path.join(top_dir, "src")
|
||||
taskpanel_dir = os.path.join(top_dir, "src")
|
||||
js_dir = os.path.join(top_dir, "js")
|
||||
data_dir = os.path.join(top_dir, "data")
|
||||
else:
|
||||
running_from_source_tree = False
|
||||
plugin = 'libgnome-shell'
|
||||
js_dir = os.path.join('@pkgdatadir@', 'js')
|
||||
taskpanel_dir = '@libexecdir@'
|
||||
|
||||
# Set up environment
|
||||
env = dict(os.environ)
|
||||
env.update({'GNOME_SHELL_JS' : '@GJS_JS_DIR@:@GJS_JS_NATIVE_DIR@:' + js_dir,
|
||||
'PATH' : '@MUTTER_BIN_DIR@:' + os.environ.get('PATH', '') + ':' + taskpanel_dir,
|
||||
'PATH' : '@MUTTER_BIN_DIR@:' + os.environ.get('PATH', ''),
|
||||
'GNOME_DISABLE_CRASH_DIALOG' : '1'})
|
||||
|
||||
if running_from_source_tree:
|
||||
env.update({'GNOME_SHELL_DATADIR' : data_dir,
|
||||
'GI_TYPELIB_PATH' : typelib_dir})
|
||||
|
||||
jhbuild_gconf_source = os.path.join('@sysconfdir@', 'gconf/2/path.jhbuild')
|
||||
if os.path.exists(jhbuild_gconf_source):
|
||||
env['GCONF_DEFAULT_SOURCE_PATH'] = jhbuild_gconf_source
|
||||
|
||||
# Work around Ubuntu xulrunner bug,
|
||||
# http://bugzilla.gnome.org/show_bug.cgi?id=573413
|
||||
pkgconfig = subprocess.Popen(['pkg-config', '--variable=sdkdir', 'mozilla-js'],
|
||||
@ -226,7 +229,16 @@ if options.wide:
|
||||
|
||||
metacity_pid = pidof("metacity")
|
||||
compiz_pid = pidof("compiz.real") or pidof("compiz")
|
||||
gnome_panel_pid = pidof("gnome-panel")
|
||||
|
||||
# In Gnome 2.26 the panel grabs a dbus name and allows replacement; use that.
|
||||
bus = dbus.Interface(dbus.SessionBus().get_object('org.freedesktop.DBus', '/org/freedesktop/DBus'),
|
||||
'org.freedesktop.DBus')
|
||||
names = bus.ListNames()
|
||||
gnome_panel_dbus = 'org.gnome.Panel' in names
|
||||
if gnome_panel_dbus:
|
||||
gnome_panel_pid = None
|
||||
else:
|
||||
gnome_panel_pid = pidof("gnome-panel")
|
||||
|
||||
# Run in Xephyr if gnome-panel is already running and the user didn't
|
||||
# specify --replace. Otherwise, run fullscreen
|
||||
@ -259,6 +271,8 @@ if options.debug:
|
||||
try:
|
||||
if run_in_xephyr:
|
||||
shell = start_xephyr()
|
||||
# This makes us not grab the org.gnome.Panel name
|
||||
os.environ['GNOME_SHELL_NO_REPLACE_PANEL'] = '1'
|
||||
start_shell()
|
||||
else:
|
||||
if gnome_panel_pid is not None:
|
||||
@ -287,6 +301,11 @@ finally:
|
||||
|
||||
if not run_in_xephyr:
|
||||
# Restart gnome-panel and window manager
|
||||
|
||||
# We don't want to start the new gnome-panel in the current
|
||||
# directory; $HOME is better for stuff launched from it
|
||||
os.chdir(os.path.expanduser("~"))
|
||||
|
||||
if metacity_pid:
|
||||
if options.verbose:
|
||||
print "Restarting Metacity"
|
||||
@ -295,7 +314,7 @@ finally:
|
||||
if options.verbose:
|
||||
print "Restarting Compiz"
|
||||
subprocess.Popen(["/usr/bin/compiz"])
|
||||
if gnome_panel_pid:
|
||||
if gnome_panel_dbus or gnome_panel_pid:
|
||||
if options.verbose:
|
||||
print "Restarting gnome-panel"
|
||||
subprocess.Popen(["/usr/bin/gnome-panel"])
|
||||
|
@ -1,90 +0,0 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include "shell-panel-window.h"
|
||||
#include <libwnck/libwnck.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dbus/dbus-glib.h>
|
||||
|
||||
static void
|
||||
on_name_owner_changed (DBusGProxy *proxy,
|
||||
const char *name,
|
||||
const char *prev_owner,
|
||||
const char *new_owner,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (strcmp (name, "org.gnome.Shell") == 0 && new_owner[0] == '\0')
|
||||
exit (0);
|
||||
}
|
||||
|
||||
static void
|
||||
monitor_main_shell (const char *shell_name)
|
||||
{
|
||||
DBusGConnection *session;
|
||||
DBusGProxy *driver;
|
||||
GError *error = NULL;
|
||||
gboolean have_shell;
|
||||
|
||||
session = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
|
||||
|
||||
driver = dbus_g_proxy_new_for_name (session,
|
||||
DBUS_SERVICE_DBUS,
|
||||
DBUS_PATH_DBUS,
|
||||
DBUS_INTERFACE_DBUS);
|
||||
|
||||
if (!dbus_g_proxy_call (driver, "NameHasOwner", &error, G_TYPE_STRING,
|
||||
shell_name, G_TYPE_INVALID, G_TYPE_BOOLEAN,
|
||||
&have_shell, G_TYPE_INVALID))
|
||||
{
|
||||
/* Shouldn't happen */
|
||||
exit (1);
|
||||
}
|
||||
if (!have_shell)
|
||||
{
|
||||
/* Shell doesn't exist; either crashed or was restarted. Just abort. */
|
||||
exit (0);
|
||||
}
|
||||
|
||||
dbus_g_proxy_add_signal (driver,
|
||||
"NameOwnerChanged",
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_STRING,
|
||||
G_TYPE_INVALID);
|
||||
|
||||
dbus_g_proxy_connect_signal (driver,
|
||||
"NameOwnerChanged",
|
||||
G_CALLBACK (on_name_owner_changed),
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
ShellPanelWindow *panel;
|
||||
WnckScreen *screen;
|
||||
WnckTasklist *tasks;
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
if (argc != 2) {
|
||||
g_printerr ("Usage: gnomeshell-taskpanel [PARENT_DBUS_SERVICE]\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
monitor_main_shell (argv[1]);
|
||||
|
||||
panel = shell_panel_window_new ();
|
||||
|
||||
screen = wnck_screen_get_default();
|
||||
tasks = WNCK_TASKLIST (wnck_tasklist_new (screen));
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (panel), GTK_WIDGET (tasks));
|
||||
|
||||
gtk_widget_show_all (GTK_WIDGET (panel));
|
||||
|
||||
gtk_main ();
|
||||
|
||||
exit (0);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,9 @@
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "shell-app-system.h"
|
||||
|
||||
/*
|
||||
* This object provides monitoring of system application directories (.desktop files)
|
||||
* and activity-based statistics about applications usage
|
||||
@ -35,14 +38,16 @@ GType shell_app_monitor_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ShellAppMonitor* shell_app_monitor_get_default(void);
|
||||
|
||||
/* Get the most popular applications for a given activity */
|
||||
GSList *shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
|
||||
int activity,
|
||||
gint number);
|
||||
ShellAppInfo *shell_app_monitor_get_window_app (ShellAppMonitor *monitor, MetaWindow *metawin);
|
||||
|
||||
GList *shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
|
||||
const char *context,
|
||||
gint number);
|
||||
|
||||
GSList *shell_app_monitor_get_windows_for_app (ShellAppMonitor *monitor, const char *appid);
|
||||
|
||||
/* Get whatever's running right now */
|
||||
GSList *shell_app_monitor_get_running_apps (ShellAppMonitor *monitor,
|
||||
int activity);
|
||||
GSList *shell_app_monitor_get_running_app_ids (ShellAppMonitor *monitor, const char *context);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -1,19 +1,31 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include "shell-app-system.h"
|
||||
#include <string.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gio/gdesktopappinfo.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <gconf/gconf.h>
|
||||
#include <gconf/gconf-client.h>
|
||||
|
||||
#include "shell-global.h"
|
||||
#include "shell-texture-cache.h"
|
||||
#include "display.h"
|
||||
|
||||
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
|
||||
#include <gmenu-tree.h>
|
||||
|
||||
#define SHELL_APP_FAVORITES_KEY "/desktop/gnome/shell/favorite_apps"
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
CHANGED,
|
||||
INSTALLED_CHANGED,
|
||||
FAVORITES_CHANGED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
@ -23,17 +35,101 @@ struct _ShellAppSystemPrivate {
|
||||
GMenuTree *apps_tree;
|
||||
GMenuTree *settings_tree;
|
||||
|
||||
GHashTable *app_id_to_app;
|
||||
|
||||
GSList *cached_app_menus; /* ShellAppMenuEntry */
|
||||
|
||||
GSList *cached_setting_ids; /* utf8 */
|
||||
GSList *cached_settings; /* ShellAppInfo */
|
||||
|
||||
GList *cached_favorites; /* utf8 */
|
||||
|
||||
gint app_monitor_id;
|
||||
};
|
||||
|
||||
static void shell_app_system_finalize (GObject *object);
|
||||
static void on_tree_changed (GMenuTree *tree, gpointer user_data);
|
||||
static void reread_menus (ShellAppSystem *self);
|
||||
static void on_favorite_apps_changed (GConfClient *client, guint id, GConfEntry *entry, gpointer user_data);
|
||||
static void reread_favorite_apps (ShellAppSystem *system);
|
||||
|
||||
G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
|
||||
|
||||
typedef enum {
|
||||
SHELL_APP_INFO_TYPE_ENTRY,
|
||||
SHELL_APP_INFO_TYPE_DESKTOP_FILE
|
||||
} ShellAppInfoType;
|
||||
|
||||
struct _ShellAppInfo {
|
||||
ShellAppInfoType type;
|
||||
|
||||
/* We need this for two reasons. First, GKeyFile doesn't have a refcount.
|
||||
* http://bugzilla.gnome.org/show_bug.cgi?id=590808
|
||||
*
|
||||
* But more generally we'll always need it so we know when to free this
|
||||
* structure (short of weak references on each item).
|
||||
*/
|
||||
guint refcount;
|
||||
|
||||
GMenuTreeItem *entry;
|
||||
|
||||
GKeyFile *keyfile;
|
||||
char *keyfile_path;
|
||||
};
|
||||
|
||||
ShellAppInfo*
|
||||
shell_app_info_ref (ShellAppInfo *info)
|
||||
{
|
||||
info->refcount++;
|
||||
return info;
|
||||
}
|
||||
|
||||
void
|
||||
shell_app_info_unref (ShellAppInfo *info)
|
||||
{
|
||||
if (--info->refcount > 0)
|
||||
return;
|
||||
switch (info->type)
|
||||
{
|
||||
case SHELL_APP_INFO_TYPE_ENTRY:
|
||||
gmenu_tree_item_unref (info->entry);
|
||||
break;
|
||||
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
|
||||
g_key_file_free (info->keyfile);
|
||||
g_free (info->keyfile_path);
|
||||
break;
|
||||
}
|
||||
g_slice_free (ShellAppInfo, info);
|
||||
}
|
||||
|
||||
static ShellAppInfo *
|
||||
shell_app_info_new_from_tree_item (GMenuTreeItem *item)
|
||||
{
|
||||
ShellAppInfo *info;
|
||||
|
||||
if (!item)
|
||||
return NULL;
|
||||
|
||||
info = g_slice_alloc (sizeof (ShellAppInfo));
|
||||
info->type = SHELL_APP_INFO_TYPE_ENTRY;
|
||||
info->refcount = 1;
|
||||
info->entry = gmenu_tree_item_ref (item);
|
||||
return info;
|
||||
}
|
||||
|
||||
static ShellAppInfo *
|
||||
shell_app_info_new_from_keyfile_take_ownership (GKeyFile *keyfile,
|
||||
const char *path)
|
||||
{
|
||||
ShellAppInfo *info;
|
||||
|
||||
info = g_slice_alloc (sizeof (ShellAppInfo));
|
||||
info->type = SHELL_APP_INFO_TYPE_DESKTOP_FILE;
|
||||
info->refcount = 1;
|
||||
info->keyfile = keyfile;
|
||||
info->keyfile_path = g_strdup (path);
|
||||
return info;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
shell_app_menu_entry_copy (gpointer entryp)
|
||||
{
|
||||
@ -63,14 +159,22 @@ static void shell_app_system_class_init(ShellAppSystemClass *klass)
|
||||
|
||||
gobject_class->finalize = shell_app_system_finalize;
|
||||
|
||||
signals[CHANGED] =
|
||||
g_signal_new ("changed",
|
||||
signals[INSTALLED_CHANGED] =
|
||||
g_signal_new ("installed-changed",
|
||||
SHELL_TYPE_APP_SYSTEM,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (ShellAppSystemClass, changed),
|
||||
G_STRUCT_OFFSET (ShellAppSystemClass, installed_changed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
signals[FAVORITES_CHANGED] =
|
||||
g_signal_new ("favorites-changed",
|
||||
SHELL_TYPE_APP_SYSTEM,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (ShellAppSystemClass, favorites_changed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
g_type_class_add_private (gobject_class, sizeof (ShellAppSystemPrivate));
|
||||
}
|
||||
@ -79,17 +183,33 @@ static void
|
||||
shell_app_system_init (ShellAppSystem *self)
|
||||
{
|
||||
ShellAppSystemPrivate *priv;
|
||||
GConfClient *client;
|
||||
|
||||
self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
||||
SHELL_TYPE_APP_SYSTEM,
|
||||
ShellAppSystemPrivate);
|
||||
|
||||
priv->apps_tree = gmenu_tree_lookup ("applications.menu", GMENU_TREE_FLAGS_NONE);
|
||||
/* The key is owned by the value */
|
||||
priv->app_id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
NULL, (GDestroyNotify) shell_app_info_unref);
|
||||
|
||||
/* For now, we want to pick up Evince, Nautilus, etc. We'll
|
||||
* handle NODISPLAY semantics at a higher level or investigate them
|
||||
* case by case.
|
||||
*/
|
||||
priv->apps_tree = gmenu_tree_lookup ("applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
|
||||
priv->settings_tree = gmenu_tree_lookup ("settings.menu", GMENU_TREE_FLAGS_NONE);
|
||||
|
||||
gmenu_tree_add_monitor (priv->apps_tree, on_tree_changed, self);
|
||||
gmenu_tree_add_monitor (priv->settings_tree, on_tree_changed, self);
|
||||
|
||||
reread_menus (self);
|
||||
|
||||
client = gconf_client_get_default ();
|
||||
|
||||
self->priv->app_monitor_id = gconf_client_notify_add (client, SHELL_APP_FAVORITES_KEY,
|
||||
on_favorite_apps_changed, self, NULL, NULL);
|
||||
reread_favorite_apps (self);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -104,13 +224,19 @@ shell_app_system_finalize (GObject *object)
|
||||
gmenu_tree_unref (priv->apps_tree);
|
||||
gmenu_tree_unref (priv->settings_tree);
|
||||
|
||||
g_hash_table_destroy (priv->app_id_to_app);
|
||||
|
||||
g_slist_foreach (priv->cached_app_menus, (GFunc)shell_app_menu_entry_free, NULL);
|
||||
g_slist_free (priv->cached_app_menus);
|
||||
priv->cached_app_menus = NULL;
|
||||
|
||||
g_slist_foreach (priv->cached_setting_ids, (GFunc)g_free, NULL);
|
||||
g_slist_free (priv->cached_setting_ids);
|
||||
priv->cached_setting_ids = NULL;
|
||||
g_slist_foreach (priv->cached_settings, (GFunc)shell_app_info_unref, NULL);
|
||||
g_slist_free (priv->cached_settings);
|
||||
priv->cached_settings = NULL;
|
||||
|
||||
g_list_free (priv->cached_favorites);
|
||||
|
||||
gconf_client_notify_remove (gconf_client_get_default (), priv->app_monitor_id);
|
||||
|
||||
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize(object);
|
||||
}
|
||||
@ -118,7 +244,6 @@ shell_app_system_finalize (GObject *object)
|
||||
static void
|
||||
reread_directories (ShellAppSystem *self, GSList **cache, GMenuTree *tree)
|
||||
{
|
||||
ShellAppSystemPrivate *priv = self->priv;
|
||||
GMenuTreeDirectory *trunk;
|
||||
GSList *entries;
|
||||
GSList *iter;
|
||||
@ -161,7 +286,7 @@ reread_directories (ShellAppSystem *self, GSList **cache, GMenuTree *tree)
|
||||
|
||||
static GSList *
|
||||
gather_entries_recurse (ShellAppSystem *monitor,
|
||||
GSList *ids,
|
||||
GSList *apps,
|
||||
GMenuTreeDirectory *root)
|
||||
{
|
||||
GSList *contents;
|
||||
@ -176,15 +301,14 @@ gather_entries_recurse (ShellAppSystem *monitor,
|
||||
{
|
||||
case GMENU_TREE_ITEM_ENTRY:
|
||||
{
|
||||
GMenuTreeEntry *entry = (GMenuTreeEntry *)item;
|
||||
const char *id = gmenu_tree_entry_get_desktop_file_id (entry);
|
||||
ids = g_slist_prepend (ids, g_strdup (id));
|
||||
ShellAppInfo *app = shell_app_info_new_from_tree_item (item);
|
||||
apps = g_slist_prepend (apps, app);
|
||||
}
|
||||
break;
|
||||
case GMENU_TREE_ITEM_DIRECTORY:
|
||||
{
|
||||
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
|
||||
ids = gather_entries_recurse (monitor, ids, dir);
|
||||
apps = gather_entries_recurse (monitor, apps, dir);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -195,7 +319,7 @@ gather_entries_recurse (ShellAppSystem *monitor,
|
||||
|
||||
g_slist_free (contents);
|
||||
|
||||
return ids;
|
||||
return apps;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -207,7 +331,7 @@ reread_entries (ShellAppSystem *self,
|
||||
|
||||
trunk = gmenu_tree_get_root_directory (tree);
|
||||
|
||||
g_slist_foreach (*cache, (GFunc)g_free, NULL);
|
||||
g_slist_foreach (*cache, (GFunc)shell_app_info_unref, NULL);
|
||||
g_slist_free (*cache);
|
||||
*cache = NULL;
|
||||
|
||||
@ -216,11 +340,41 @@ reread_entries (ShellAppSystem *self,
|
||||
gmenu_tree_item_unref (trunk);
|
||||
}
|
||||
|
||||
static void
|
||||
cache_by_id (ShellAppSystem *self, GSList *apps, gboolean ref)
|
||||
{
|
||||
GSList *iter;
|
||||
|
||||
for (iter = apps; iter; iter = iter->next)
|
||||
{
|
||||
ShellAppInfo *info = iter->data;
|
||||
if (ref)
|
||||
shell_app_info_ref (info);
|
||||
/* the name is owned by the info itself */
|
||||
g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (info),
|
||||
info);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reread_menus (ShellAppSystem *self)
|
||||
{
|
||||
GSList *apps;
|
||||
GMenuTreeDirectory *trunk;
|
||||
|
||||
reread_directories (self, &(self->priv->cached_app_menus), self->priv->apps_tree);
|
||||
reread_entries (self, &(self->priv->cached_setting_ids), self->priv->settings_tree);
|
||||
|
||||
reread_entries (self, &(self->priv->cached_settings), self->priv->settings_tree);
|
||||
|
||||
/* Now loop over applications.menu and settings.menu, inserting each by desktop file
|
||||
* ID into a hash */
|
||||
g_hash_table_remove_all (self->priv->app_id_to_app);
|
||||
trunk = gmenu_tree_get_root_directory (self->priv->apps_tree);
|
||||
apps = gather_entries_recurse (self, NULL, trunk);
|
||||
gmenu_tree_item_unref (trunk);
|
||||
cache_by_id (self, apps, FALSE);
|
||||
g_slist_free (apps);
|
||||
cache_by_id (self, self->priv->cached_settings, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -228,11 +382,81 @@ on_tree_changed (GMenuTree *monitor, gpointer user_data)
|
||||
{
|
||||
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
|
||||
|
||||
g_signal_emit (self, signals[CHANGED], 0);
|
||||
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
|
||||
|
||||
reread_menus (self);
|
||||
}
|
||||
|
||||
static GList *
|
||||
convert_gconf_value_string_list_to_list_uniquify (GConfValue *value )
|
||||
{
|
||||
GSList *list;
|
||||
GSList *tmp;
|
||||
GList *result = NULL;
|
||||
GHashTable *tmp_table = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
list = gconf_value_get_list (value);
|
||||
|
||||
for (tmp = list ; tmp; tmp = tmp->next)
|
||||
{
|
||||
GConfValue *value = tmp->data;
|
||||
char *str = g_strdup (gconf_value_get_string (value));
|
||||
if (!str)
|
||||
continue;
|
||||
if (g_hash_table_lookup (tmp_table, str))
|
||||
{
|
||||
g_free (str);
|
||||
continue;
|
||||
}
|
||||
g_hash_table_insert (tmp_table, str, GUINT_TO_POINTER(1));
|
||||
result = g_list_prepend (result, str);
|
||||
}
|
||||
g_hash_table_destroy (tmp_table);
|
||||
return g_list_reverse (result);
|
||||
}
|
||||
|
||||
static void
|
||||
reread_favorite_apps (ShellAppSystem *system)
|
||||
{
|
||||
GConfClient *client = gconf_client_get_default ();
|
||||
GConfValue *val;
|
||||
|
||||
val = gconf_client_get (client, SHELL_APP_FAVORITES_KEY, NULL);
|
||||
|
||||
if (!(val && val->type == GCONF_VALUE_LIST && gconf_value_get_list_type (val) == GCONF_VALUE_STRING))
|
||||
return;
|
||||
|
||||
g_list_foreach (system->priv->cached_favorites, (GFunc) g_free, NULL);
|
||||
g_list_free (system->priv->cached_favorites);
|
||||
system->priv->cached_favorites = convert_gconf_value_string_list_to_list_uniquify (val);
|
||||
|
||||
gconf_value_free (val);
|
||||
}
|
||||
|
||||
void
|
||||
on_favorite_apps_changed (GConfClient *client,
|
||||
guint id,
|
||||
GConfEntry *entry,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellAppSystem *system = SHELL_APP_SYSTEM (user_data);
|
||||
reread_favorite_apps (system);
|
||||
g_signal_emit (G_OBJECT (system), signals[FAVORITES_CHANGED], 0);
|
||||
}
|
||||
|
||||
GType
|
||||
shell_app_info_get_type (void)
|
||||
{
|
||||
static GType gtype = G_TYPE_INVALID;
|
||||
if (gtype == G_TYPE_INVALID)
|
||||
{
|
||||
gtype = g_boxed_type_register_static ("ShellAppInfo",
|
||||
(GBoxedCopyFunc)shell_app_info_ref,
|
||||
(GBoxedFreeFunc)shell_app_info_unref);
|
||||
}
|
||||
return gtype;
|
||||
}
|
||||
|
||||
GType
|
||||
shell_app_menu_entry_get_type (void)
|
||||
{
|
||||
@ -252,7 +476,7 @@ shell_app_menu_entry_get_type (void)
|
||||
* Traverses a toplevel menu, and returns all items under it. Nested items
|
||||
* are flattened.
|
||||
*
|
||||
* Return value: (transfer full) (element-type utf8): List of desktop file ids
|
||||
* Return value: (transfer container) (element-type ShellAppInfo): List of applications
|
||||
*/
|
||||
GSList *
|
||||
shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
|
||||
@ -277,7 +501,7 @@ shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
|
||||
/**
|
||||
* shell_app_system_get_menus:
|
||||
*
|
||||
* Returns a list of toplevel menu names, like "Accessories", "Programming", etc.
|
||||
* Returns a list of toplevel #ShellAppMenuEntry items
|
||||
*
|
||||
* Return value: (transfer none) (element-type AppMenuEntry): List of toplevel menus
|
||||
*/
|
||||
@ -290,14 +514,14 @@ shell_app_system_get_menus (ShellAppSystem *monitor)
|
||||
/**
|
||||
* shell_app_system_get_all_settings:
|
||||
*
|
||||
* Returns a list of all desktop file ids under "settings.menu".
|
||||
* Returns a list of application items under "settings.menu".
|
||||
*
|
||||
* Return value: (transfer none) (element-type utf8): List of desktop file ids
|
||||
* Return value: (transfer none) (element-type ShellAppInfo): List of applications
|
||||
*/
|
||||
GSList *
|
||||
shell_app_system_get_all_settings (ShellAppSystem *monitor)
|
||||
{
|
||||
return monitor->priv->cached_setting_ids;
|
||||
return monitor->priv->cached_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,3 +539,407 @@ shell_app_system_get_default ()
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_system_get_favorites:
|
||||
*
|
||||
* Return the list of applications which have been explicitly added to the
|
||||
* favorites.
|
||||
*
|
||||
* Return value: (transfer none) (element-type utf8): List of favorite application ids
|
||||
*/
|
||||
GList *
|
||||
shell_app_system_get_favorites (ShellAppSystem *system)
|
||||
{
|
||||
return system->priv->cached_favorites;
|
||||
}
|
||||
|
||||
static void
|
||||
set_gconf_value_string_list (GConfValue *val, GList *items)
|
||||
{
|
||||
GList *iter;
|
||||
GSList *tmp = NULL;
|
||||
|
||||
for (iter = items; iter; iter = iter->next)
|
||||
{
|
||||
const char *str = iter->data;
|
||||
GConfValue *strval = gconf_value_new (GCONF_VALUE_STRING);
|
||||
gconf_value_set_string (strval, str);
|
||||
tmp = g_slist_prepend (tmp, strval);
|
||||
}
|
||||
tmp = g_slist_reverse (tmp);
|
||||
|
||||
gconf_value_set_list (val, tmp);
|
||||
g_slist_free (tmp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
shell_app_system_add_favorite (ShellAppSystem *system, const char *id)
|
||||
{
|
||||
GConfClient *client = gconf_client_get_default ();
|
||||
GConfValue *val;
|
||||
GList *iter;
|
||||
|
||||
iter = g_list_find_custom (system->priv->cached_favorites, id, (GCompareFunc)strcmp);
|
||||
if (iter)
|
||||
return;
|
||||
|
||||
val = gconf_value_new (GCONF_VALUE_LIST);
|
||||
gconf_value_set_list_type (val, GCONF_VALUE_STRING);
|
||||
|
||||
system->priv->cached_favorites = g_list_append (system->priv->cached_favorites, g_strdup (id));
|
||||
|
||||
set_gconf_value_string_list (val, system->priv->cached_favorites);
|
||||
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
shell_app_system_remove_favorite (ShellAppSystem *system, const char *id)
|
||||
{
|
||||
GConfClient *client = gconf_client_get_default ();
|
||||
GConfValue *val;
|
||||
GList *iter;
|
||||
|
||||
iter = g_list_find_custom (system->priv->cached_favorites, id, (GCompareFunc)strcmp);
|
||||
if (!iter)
|
||||
return;
|
||||
g_free (iter->data);
|
||||
system->priv->cached_favorites = g_list_delete_link (system->priv->cached_favorites, iter);
|
||||
|
||||
val = gconf_value_new (GCONF_VALUE_LIST);
|
||||
gconf_value_set_list_type (val, GCONF_VALUE_STRING);
|
||||
|
||||
set_gconf_value_string_list (val, system->priv->cached_favorites);
|
||||
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_system_lookup_app:
|
||||
*
|
||||
* Return value: (transfer full): The #ShellAppInfo for id, or %NULL if none
|
||||
*/
|
||||
ShellAppInfo *
|
||||
shell_app_system_lookup_cached_app (ShellAppSystem *self, const char *id)
|
||||
{
|
||||
ShellAppInfo *info;
|
||||
|
||||
info = g_hash_table_lookup (self->priv->app_id_to_app, id);
|
||||
if (info)
|
||||
shell_app_info_ref (info);
|
||||
return info;
|
||||
}
|
||||
|
||||
ShellAppInfo *
|
||||
shell_app_system_load_from_desktop_file (ShellAppSystem *system,
|
||||
const char *filename,
|
||||
GError **error)
|
||||
{
|
||||
ShellAppInfo *appinfo;
|
||||
GKeyFile *keyfile;
|
||||
char *full_path = NULL;
|
||||
gboolean success;
|
||||
|
||||
keyfile = g_key_file_new ();
|
||||
|
||||
if (strchr (filename, '/') != NULL)
|
||||
{
|
||||
success = g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, error);
|
||||
full_path = g_strdup (filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *app_path = g_build_filename ("applications", filename, NULL);
|
||||
success = g_key_file_load_from_data_dirs (keyfile, app_path, &full_path,
|
||||
G_KEY_FILE_NONE, error);
|
||||
g_free (app_path);
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
g_key_file_free (keyfile);
|
||||
g_free (full_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
appinfo = shell_app_info_new_from_keyfile_take_ownership (keyfile, full_path);
|
||||
g_free (full_path);
|
||||
|
||||
return appinfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_system_lookup_heuristic_basename:
|
||||
* @name: Probable application identifier
|
||||
*
|
||||
* Find a valid application corresponding to a given
|
||||
* heuristically determined application identifier
|
||||
* string, or %NULL if none.
|
||||
*
|
||||
* Returns: (transfer full): A #ShellAppInfo for name
|
||||
*/
|
||||
ShellAppInfo *
|
||||
shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
|
||||
const char *name)
|
||||
{
|
||||
char *tmpid;
|
||||
ShellAppInfo *result;
|
||||
|
||||
result = shell_app_system_lookup_cached_app (system, name);
|
||||
if (result != NULL)
|
||||
return result;
|
||||
|
||||
/* These are common "vendor prefixes". But using
|
||||
* WM_CLASS as a source, we don't get the vendor
|
||||
* prefix. So try stripping them.
|
||||
*/
|
||||
tmpid = g_strjoin ("", "gnome-", name, NULL);
|
||||
result = shell_app_system_lookup_cached_app (system, tmpid);
|
||||
g_free (tmpid);
|
||||
if (result != NULL)
|
||||
return result;
|
||||
|
||||
tmpid = g_strjoin ("", "fedora-", name, NULL);
|
||||
result = shell_app_system_lookup_cached_app (system, tmpid);
|
||||
g_free (tmpid);
|
||||
if (result != NULL)
|
||||
return result;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
shell_app_info_get_id (ShellAppInfo *info)
|
||||
{
|
||||
switch (info->type)
|
||||
{
|
||||
case SHELL_APP_INFO_TYPE_ENTRY:
|
||||
return gmenu_tree_entry_get_desktop_file_id ((GMenuTreeEntry*)info->entry);
|
||||
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
|
||||
return info->keyfile_path;
|
||||
}
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define DESKTOP_ENTRY_GROUP "Desktop Entry"
|
||||
|
||||
char *
|
||||
shell_app_info_get_name (ShellAppInfo *info)
|
||||
{
|
||||
switch (info->type)
|
||||
{
|
||||
case SHELL_APP_INFO_TYPE_ENTRY:
|
||||
return g_strdup (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info->entry));
|
||||
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
|
||||
return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Name", NULL, NULL);
|
||||
}
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
shell_app_info_get_description (ShellAppInfo *info)
|
||||
{
|
||||
switch (info->type)
|
||||
{
|
||||
case SHELL_APP_INFO_TYPE_ENTRY:
|
||||
return g_strdup (gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info->entry));
|
||||
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
|
||||
return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL);
|
||||
}
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
shell_app_info_get_executable (ShellAppInfo *info)
|
||||
{
|
||||
switch (info->type)
|
||||
{
|
||||
case SHELL_APP_INFO_TYPE_ENTRY:
|
||||
return g_strdup (gmenu_tree_entry_get_exec ((GMenuTreeEntry*)info->entry));
|
||||
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
|
||||
return g_key_file_get_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Exec", NULL);
|
||||
}
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
shell_app_info_get_desktop_file_path (ShellAppInfo *info)
|
||||
{
|
||||
switch (info->type)
|
||||
{
|
||||
case SHELL_APP_INFO_TYPE_ENTRY:
|
||||
return g_strdup (gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*)info->entry));
|
||||
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
|
||||
return g_strdup (info->keyfile_path);;
|
||||
}
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GIcon *
|
||||
shell_app_info_get_icon (ShellAppInfo *info)
|
||||
{
|
||||
char *iconname = NULL;
|
||||
GIcon *icon;
|
||||
|
||||
/* This code adapted from gdesktopappinfo.c
|
||||
* Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
* Copyright © 2007 Ryan Lortie
|
||||
* LGPL
|
||||
*/
|
||||
|
||||
switch (info->type)
|
||||
{
|
||||
case SHELL_APP_INFO_TYPE_ENTRY:
|
||||
iconname = g_strdup (gmenu_tree_entry_get_icon ((GMenuTreeEntry*)info->entry));
|
||||
break;
|
||||
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
|
||||
iconname = g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Icon", NULL, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!iconname)
|
||||
return NULL;
|
||||
|
||||
if (g_path_is_absolute (iconname))
|
||||
{
|
||||
GFile *file;
|
||||
|
||||
file = g_file_new_for_path (iconname);
|
||||
icon = G_ICON (g_file_icon_new (file));
|
||||
g_object_unref (file);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *tmp_name, *p;
|
||||
tmp_name = strdup (iconname);
|
||||
/* Work around a common mistake in desktop files */
|
||||
if ((p = strrchr (tmp_name, '.')) != NULL &&
|
||||
(strcmp (p, ".png") == 0 ||
|
||||
strcmp (p, ".xpm") == 0 ||
|
||||
strcmp (p, ".svg") == 0))
|
||||
{
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
icon = g_themed_icon_new (tmp_name);
|
||||
g_free (tmp_name);
|
||||
}
|
||||
g_free (iconname);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
GSList *
|
||||
shell_app_info_get_categories (ShellAppInfo *info)
|
||||
{
|
||||
return NULL; /* TODO */
|
||||
}
|
||||
|
||||
gboolean
|
||||
shell_app_info_get_is_nodisplay (ShellAppInfo *info)
|
||||
{
|
||||
switch (info->type)
|
||||
{
|
||||
case SHELL_APP_INFO_TYPE_ENTRY:
|
||||
return gmenu_tree_entry_get_is_nodisplay ((GMenuTreeEntry*)info);
|
||||
case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
|
||||
return FALSE;
|
||||
}
|
||||
g_assert_not_reached ();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_info_create_icon_texture:
|
||||
*
|
||||
* Look up the icon for this application, and create a #ClutterTexture
|
||||
* for it at the given size.
|
||||
*
|
||||
* Return value: (transfer none): A floating #ClutterActor
|
||||
*/
|
||||
ClutterActor *
|
||||
shell_app_info_create_icon_texture (ShellAppInfo *info, float size)
|
||||
{
|
||||
GIcon *icon;
|
||||
ClutterActor *ret;
|
||||
|
||||
icon = shell_app_info_get_icon (info);
|
||||
if (!icon)
|
||||
{
|
||||
ret = clutter_texture_new ();
|
||||
g_object_set (ret, "opacity", 0, "width", size, "height", size, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return shell_texture_cache_load_gicon (shell_texture_cache_get_default (), icon, (int)size);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_info_launch_full:
|
||||
* @timestamp: Event timestamp, or 0 for current event timestamp
|
||||
* @uris: List of uris to pass to application
|
||||
* @workspace: Start on this workspace, or -1 for default
|
||||
* @startup_id: (out): Returned startup notification ID, or %NULL if none
|
||||
* @error: A #GError
|
||||
*/
|
||||
gboolean
|
||||
shell_app_info_launch_full (ShellAppInfo *info,
|
||||
guint timestamp,
|
||||
GList *uris,
|
||||
int workspace,
|
||||
char **startup_id,
|
||||
GError **error)
|
||||
{
|
||||
GDesktopAppInfo *gapp;
|
||||
char *filename;
|
||||
GdkAppLaunchContext *context;
|
||||
gboolean ret;
|
||||
ShellGlobal *global;
|
||||
MetaScreen *screen;
|
||||
MetaDisplay *display;
|
||||
|
||||
if (startup_id)
|
||||
*startup_id = NULL;
|
||||
|
||||
filename = shell_app_info_get_desktop_file_path (info);
|
||||
gapp = g_desktop_app_info_new_from_filename (filename);
|
||||
g_free (filename);
|
||||
|
||||
if (!gapp)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Not found");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
global = shell_global_get ();
|
||||
screen = shell_global_get_screen (global);
|
||||
display = meta_screen_get_display (screen);
|
||||
|
||||
if (timestamp == 0)
|
||||
timestamp = meta_display_get_current_time (display);
|
||||
if (workspace < 0)
|
||||
workspace = meta_screen_get_active_workspace_index (screen);
|
||||
|
||||
context = gdk_app_launch_context_new ();
|
||||
gdk_app_launch_context_set_timestamp (context, timestamp);
|
||||
gdk_app_launch_context_set_desktop (context, workspace);
|
||||
|
||||
ret = g_app_info_launch (G_APP_INFO (gapp), uris, (GAppLaunchContext*) context, error);
|
||||
|
||||
g_object_unref (G_OBJECT (gapp));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
shell_app_info_launch (ShellAppInfo *info,
|
||||
GError **error)
|
||||
{
|
||||
return shell_app_info_launch_full (info, 0, NULL, -1, NULL, error);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
#ifndef __SHELL_APP_SYSTEM_H__
|
||||
#define __SHELL_APP_SYSTEM_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#define SHELL_TYPE_APP_SYSTEM (shell_app_system_get_type ())
|
||||
#define SHELL_APP_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_APP_SYSTEM, ShellAppSystem))
|
||||
@ -25,7 +26,8 @@ struct _ShellAppSystemClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*changed)(ShellAppSystem *appsys, gpointer data);
|
||||
void (*installed_changed)(ShellAppSystem *appsys, gpointer user_data);
|
||||
void (*favorites_changed)(ShellAppSystem *appsys, gpointer user_data);
|
||||
};
|
||||
|
||||
GType shell_app_system_get_type (void) G_GNUC_CONST;
|
||||
@ -43,8 +45,46 @@ struct _ShellAppMenuEntry {
|
||||
|
||||
GType shell_app_menu_entry_get_type (void);
|
||||
|
||||
typedef struct _ShellAppInfo ShellAppInfo;
|
||||
|
||||
#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ())
|
||||
GType shell_app_info_get_type (void);
|
||||
|
||||
ShellAppInfo* shell_app_info_ref (ShellAppInfo *info);
|
||||
void shell_app_info_unref (ShellAppInfo *info);
|
||||
|
||||
const char *shell_app_info_get_id (ShellAppInfo *info);
|
||||
char *shell_app_info_get_name (ShellAppInfo *info);
|
||||
char *shell_app_info_get_description (ShellAppInfo *info);
|
||||
char *shell_app_info_get_executable (ShellAppInfo *info);
|
||||
char *shell_app_info_get_desktop_file_path (ShellAppInfo *info);
|
||||
GIcon *shell_app_info_get_icon (ShellAppInfo *info);
|
||||
ClutterActor *shell_app_info_create_icon_texture (ShellAppInfo *info, float size);
|
||||
GSList *shell_app_info_get_categories (ShellAppInfo *info);
|
||||
gboolean shell_app_info_get_is_nodisplay (ShellAppInfo *info);
|
||||
gboolean shell_app_info_launch_full (ShellAppInfo *info,
|
||||
guint timestamp,
|
||||
GList *uris,
|
||||
int workspace,
|
||||
char **startup_id,
|
||||
GError **error);
|
||||
gboolean shell_app_info_launch (ShellAppInfo *info,
|
||||
GError **error);
|
||||
|
||||
ShellAppInfo *shell_app_system_load_from_desktop_file (ShellAppSystem *system, const char *filename, GError **error);
|
||||
|
||||
ShellAppInfo *shell_app_system_lookup_cached_app (ShellAppSystem *system, const char *id);
|
||||
|
||||
ShellAppInfo *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, const char *id);
|
||||
|
||||
GSList *shell_app_system_get_menus (ShellAppSystem *system);
|
||||
|
||||
GSList *shell_app_system_get_all_settings (ShellAppSystem *system);
|
||||
|
||||
GList *shell_app_system_get_favorites (ShellAppSystem *system);
|
||||
|
||||
void shell_app_system_add_favorite (ShellAppSystem *system, const char *id);
|
||||
|
||||
void shell_app_system_remove_favorite (ShellAppSystem *system, const char *id);
|
||||
|
||||
#endif /* __SHELL_APP_SYSTEM_H__ */
|
||||
|
92
src/shell-drawing-area.c
Normal file
92
src/shell-drawing-area.c
Normal file
@ -0,0 +1,92 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/**
|
||||
* SECTION:shell-drawing-area
|
||||
* @short_description: A dynamically-sized Cairo drawing area
|
||||
*
|
||||
* #ShellDrawingArea is similar to #ClutterCairoTexture in that
|
||||
* it allows drawing via Cairo; the primary difference is that
|
||||
* it is dynamically sized. To use, connect to the @redraw
|
||||
* signal, and inside the signal handler, call
|
||||
* clutter_cairo_texture_create() to begin drawing.
|
||||
*/
|
||||
|
||||
#include "shell-drawing-area.h"
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <cairo.h>
|
||||
|
||||
G_DEFINE_TYPE(ShellDrawingArea, shell_drawing_area, CLUTTER_TYPE_GROUP);
|
||||
|
||||
struct _ShellDrawingAreaPrivate {
|
||||
ClutterCairoTexture *texture;
|
||||
};
|
||||
|
||||
/* Signals */
|
||||
enum
|
||||
{
|
||||
REDRAW,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint shell_drawing_area_signals [LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
shell_drawing_area_allocate (ClutterActor *self,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
ShellDrawingArea *area = SHELL_DRAWING_AREA (self);
|
||||
int width = box->x2 - box->x1;
|
||||
int height = box->y2 - box->y1;
|
||||
|
||||
clutter_actor_allocate (CLUTTER_ACTOR (area->priv->texture), box, flags);
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
clutter_cairo_texture_set_surface_size (area->priv->texture,
|
||||
width, height);
|
||||
g_signal_emit (G_OBJECT (self), shell_drawing_area_signals[REDRAW], 0,
|
||||
area->priv->texture);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
shell_drawing_area_class_init (ShellDrawingAreaClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
|
||||
actor_class->allocate = shell_drawing_area_allocate;
|
||||
|
||||
shell_drawing_area_signals[REDRAW] =
|
||||
g_signal_new ("redraw",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (ShellDrawingAreaClass, redraw),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1, G_TYPE_OBJECT);
|
||||
|
||||
g_type_class_add_private (gobject_class, sizeof (ShellDrawingAreaPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
shell_drawing_area_init (ShellDrawingArea *area)
|
||||
{
|
||||
area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_DRAWING_AREA,
|
||||
ShellDrawingAreaPrivate);
|
||||
area->priv->texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (1, 1));
|
||||
clutter_container_add_actor (CLUTTER_CONTAINER (area), CLUTTER_ACTOR (area->priv->texture));
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_drawing_area_get_texture:
|
||||
*
|
||||
* Return Value: (transfer none):
|
||||
*/
|
||||
ClutterCairoTexture *
|
||||
shell_drawing_area_get_texture (ShellDrawingArea *area)
|
||||
{
|
||||
return area->priv->texture;
|
||||
}
|
37
src/shell-drawing-area.h
Normal file
37
src/shell-drawing-area.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef __SHELL_DRAWING_AREA_H__
|
||||
#define __SHELL_DRAWING_AREA_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define SHELL_TYPE_DRAWING_AREA (shell_drawing_area_get_type ())
|
||||
#define SHELL_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_DRAWING_AREA, ShellDrawingArea))
|
||||
#define SHELL_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_DRAWING_AREA, ShellDrawingAreaClass))
|
||||
#define SHELL_IS_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_DRAWING_AREA))
|
||||
#define SHELL_IS_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_DRAWING_AREA))
|
||||
#define SHELL_DRAWING_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_DRAWING_AREA, ShellDrawingAreaClass))
|
||||
|
||||
typedef struct _ShellDrawingArea ShellDrawingArea;
|
||||
typedef struct _ShellDrawingAreaClass ShellDrawingAreaClass;
|
||||
|
||||
typedef struct _ShellDrawingAreaPrivate ShellDrawingAreaPrivate;
|
||||
|
||||
struct _ShellDrawingArea
|
||||
{
|
||||
ClutterGroup parent;
|
||||
|
||||
ShellDrawingAreaPrivate *priv;
|
||||
};
|
||||
|
||||
struct _ShellDrawingAreaClass
|
||||
{
|
||||
ClutterGroupClass parent_class;
|
||||
|
||||
void (*redraw) (ShellDrawingArea *area, ClutterCairoTexture *texture);
|
||||
};
|
||||
|
||||
GType shell_drawing_area_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ClutterCairoTexture *shell_drawing_area_get_texture (ShellDrawingArea *area);
|
||||
|
||||
#endif /* __SHELL_DRAWING_AREA_H__ */
|
215
src/shell-drawing.c
Normal file
215
src/shell-drawing.c
Normal file
@ -0,0 +1,215 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include "shell-drawing.h"
|
||||
#include <math.h>
|
||||
|
||||
/**
|
||||
* shell_create_vertical_gradient:
|
||||
* @top: the color at the top
|
||||
* @bottom: the color at the bottom
|
||||
*
|
||||
* Creates a vertical gradient actor.
|
||||
*
|
||||
* Return value: (transfer none): a #ClutterCairoTexture actor with the
|
||||
* gradient. The texture actor is floating, hence (transfer none).
|
||||
*/
|
||||
ClutterCairoTexture *
|
||||
shell_create_vertical_gradient (ClutterColor *top,
|
||||
ClutterColor *bottom)
|
||||
{
|
||||
ClutterCairoTexture *texture;
|
||||
cairo_t *cr;
|
||||
cairo_pattern_t *pattern;
|
||||
|
||||
/* Draw the gradient on an 8x8 pixel texture. Because the gradient is drawn
|
||||
* from the uppermost to the lowermost row, after stretching 1/16 of the
|
||||
* texture height has the top color and 1/16 has the bottom color. The 8
|
||||
* pixel width is chosen for reasons related to graphics hardware internals.
|
||||
*/
|
||||
texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 8));
|
||||
cr = clutter_cairo_texture_create (texture);
|
||||
|
||||
pattern = cairo_pattern_create_linear (0, 0, 0, 8);
|
||||
cairo_pattern_add_color_stop_rgba (pattern, 0,
|
||||
top->red / 255.,
|
||||
top->green / 255.,
|
||||
top->blue / 255.,
|
||||
top->alpha / 255.);
|
||||
cairo_pattern_add_color_stop_rgba (pattern, 1,
|
||||
bottom->red / 255.,
|
||||
bottom->green / 255.,
|
||||
bottom->blue / 255.,
|
||||
bottom->alpha / 255.);
|
||||
|
||||
cairo_set_source (cr, pattern);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_pattern_destroy (pattern);
|
||||
cairo_destroy (cr);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_create_horizontal_gradient:
|
||||
* @left: the color on the left
|
||||
* @right: the color on the right
|
||||
*
|
||||
* Creates a horizontal gradient actor.
|
||||
*
|
||||
* Return value: (transfer none): a #ClutterCairoTexture actor with the
|
||||
* gradient. The texture actor is floating, hence (transfer none).
|
||||
*/
|
||||
ClutterCairoTexture *
|
||||
shell_create_horizontal_gradient (ClutterColor *left,
|
||||
ClutterColor *right)
|
||||
{
|
||||
ClutterCairoTexture *texture;
|
||||
cairo_t *cr;
|
||||
cairo_pattern_t *pattern;
|
||||
|
||||
/* Draw the gradient on an 8x1 pixel texture. Because the gradient is drawn
|
||||
* from the left to the right column, after stretching 1/16 of the
|
||||
* texture width has the left side color and 1/16 has the right side color.
|
||||
* There is no reason to use the 8 pixel height that would be similar to the
|
||||
* reason we are using the 8 pixel width for the vertical gradient, so we
|
||||
* are just using the 1 pixel height instead.
|
||||
*/
|
||||
texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 1));
|
||||
cr = clutter_cairo_texture_create (texture);
|
||||
|
||||
pattern = cairo_pattern_create_linear (0, 0, 8, 0);
|
||||
cairo_pattern_add_color_stop_rgba (pattern, 0,
|
||||
left->red / 255.,
|
||||
left->green / 255.,
|
||||
left->blue / 255.,
|
||||
left->alpha / 255.);
|
||||
cairo_pattern_add_color_stop_rgba (pattern, 1,
|
||||
right->red / 255.,
|
||||
right->green / 255.,
|
||||
right->blue / 255.,
|
||||
right->alpha / 255.);
|
||||
|
||||
cairo_set_source (cr, pattern);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_pattern_destroy (pattern);
|
||||
cairo_destroy (cr);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
void
|
||||
shell_draw_clock (ClutterCairoTexture *texture,
|
||||
int hour,
|
||||
int minute)
|
||||
{
|
||||
cairo_t *cr;
|
||||
guint width, height;
|
||||
double xc, yc, radius, hour_radius, minute_radius;
|
||||
double angle;
|
||||
|
||||
clutter_cairo_texture_get_surface_size (texture, &width, &height);
|
||||
xc = (double)width / 2;
|
||||
yc = (double)height / 2;
|
||||
radius = (double)(MIN(width, height)) / 2 - 2;
|
||||
minute_radius = radius - 3;
|
||||
hour_radius = radius / 2;
|
||||
|
||||
clutter_cairo_texture_clear (texture);
|
||||
cr = clutter_cairo_texture_create (texture);
|
||||
cairo_set_line_width (cr, 1.0);
|
||||
|
||||
/* Outline */
|
||||
cairo_arc (cr, xc, yc, radius, 0.0, 2.0 * M_PI);
|
||||
cairo_stroke (cr);
|
||||
|
||||
/* Hour hand. (We add a fraction to @hour for the minutes, then
|
||||
* convert to radians, and then subtract pi/2 because cairo's origin
|
||||
* is at 3:00, not 12:00.)
|
||||
*/
|
||||
angle = ((hour + minute / 60.0) / 12.0) * 2.0 * M_PI - M_PI / 2.0;
|
||||
cairo_move_to (cr, xc, yc);
|
||||
cairo_line_to (cr,
|
||||
xc + hour_radius * cos (angle),
|
||||
yc + hour_radius * sin (angle));
|
||||
cairo_stroke (cr);
|
||||
|
||||
/* Minute hand */
|
||||
angle = (minute / 60.0) * 2.0 * M_PI - M_PI / 2.0;
|
||||
cairo_move_to (cr, xc, yc);
|
||||
cairo_line_to (cr,
|
||||
xc + minute_radius * cos (angle),
|
||||
yc + minute_radius * sin (angle));
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_destroy (cr);
|
||||
}
|
||||
|
||||
void
|
||||
shell_draw_glow (ClutterCairoTexture *texture,
|
||||
double red,
|
||||
double green,
|
||||
double blue,
|
||||
double alpha)
|
||||
{
|
||||
cairo_t *cr;
|
||||
guint width, height;
|
||||
cairo_pattern_t *gradient;
|
||||
|
||||
clutter_cairo_texture_get_surface_size (texture, &width, &height);
|
||||
|
||||
clutter_cairo_texture_clear (texture);
|
||||
cr = clutter_cairo_texture_create (texture);
|
||||
|
||||
cairo_save (cr);
|
||||
cairo_translate (cr, width / 2.0, height / 2.0);
|
||||
cairo_scale (cr, width / 2.0, height / 2.0);
|
||||
|
||||
gradient = cairo_pattern_create_radial (0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
|
||||
cairo_pattern_add_color_stop_rgba (gradient, 0.0, red, green, blue, alpha);
|
||||
cairo_pattern_add_color_stop_rgba (gradient, 0.7, red, green, blue, alpha * 0.7);
|
||||
cairo_pattern_add_color_stop_rgba (gradient, 1.0, red, green, blue, alpha * 0.3);
|
||||
cairo_set_source (cr, gradient);
|
||||
|
||||
cairo_arc (cr, 0.0, 0.0, 1.0, 0.0, 2.0 * M_PI);
|
||||
cairo_fill (cr);
|
||||
cairo_restore (cr);
|
||||
cairo_pattern_destroy (gradient);
|
||||
cairo_destroy (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
hook_paint_red_border (ClutterActor *actor,
|
||||
gpointer user_data)
|
||||
{
|
||||
CoglColor color;
|
||||
ClutterGeometry geom;
|
||||
float width = 2;
|
||||
float x2;
|
||||
float y2;
|
||||
|
||||
cogl_color_set_from_4ub (&color, 0xff, 0, 0, 0xc4);
|
||||
cogl_set_source_color (&color);
|
||||
|
||||
clutter_actor_get_allocation_geometry (actor, &geom);
|
||||
x2 = geom.x + geom.width;
|
||||
y2 = geom.y + geom.height;
|
||||
|
||||
/** clockwise order **/
|
||||
cogl_rectangle (geom.x, geom.y,
|
||||
x2, geom.y + width);
|
||||
cogl_rectangle (x2 - width, geom.y + width,
|
||||
x2, y2);
|
||||
cogl_rectangle (x2 - width, y2,
|
||||
geom.x, y2 - width);
|
||||
cogl_rectangle (geom.x + width, y2 - width,
|
||||
geom.x, geom.y + width);
|
||||
}
|
||||
|
||||
guint
|
||||
shell_add_hook_paint_red_border (ClutterActor *actor)
|
||||
{
|
||||
return g_signal_connect_after (G_OBJECT (actor), "paint",
|
||||
G_CALLBACK (hook_paint_red_border), NULL);
|
||||
}
|
30
src/shell-drawing.h
Normal file
30
src/shell-drawing.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#ifndef __SHELL_DRAWING_H__
|
||||
#define __SHELL_DRAWING_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
ClutterCairoTexture *shell_create_vertical_gradient (ClutterColor *top,
|
||||
ClutterColor *bottom);
|
||||
|
||||
ClutterCairoTexture *shell_create_horizontal_gradient (ClutterColor *left,
|
||||
ClutterColor *right);
|
||||
|
||||
void shell_draw_clock (ClutterCairoTexture *texture,
|
||||
int hour,
|
||||
int minute);
|
||||
|
||||
void shell_draw_glow (ClutterCairoTexture *texture,
|
||||
double red,
|
||||
double blue,
|
||||
double green,
|
||||
double alpha);
|
||||
|
||||
guint shell_add_hook_paint_red_border (ClutterActor *actor);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_GLOBAL_H__ */
|
396
src/shell-gconf.c
Normal file
396
src/shell-gconf.c
Normal file
@ -0,0 +1,396 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include "shell-gconf.h"
|
||||
|
||||
#include <gconf/gconf-client.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* ShellGConf:
|
||||
*
|
||||
* A wrapper around #GConfClient that cleans up some of its
|
||||
* non-gjs-bindable bits and makes a few gnome-shell-specific
|
||||
* assumptions.
|
||||
*
|
||||
* For all #ShellGConf methods that take a GConf key path as an
|
||||
* argument, you can pass either a full path (eg,
|
||||
* "/desktop/gnome/shell/sidebar/visible"), or just a relative path
|
||||
* starting from the root of the gnome-shell GConf key hierarchy (eg,
|
||||
* "sidebar/visible").
|
||||
*/
|
||||
|
||||
struct _ShellGConf
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GConfClient *client;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (ShellGConf, shell_gconf, G_TYPE_OBJECT);
|
||||
|
||||
/* Signals */
|
||||
enum
|
||||
{
|
||||
CHANGED,
|
||||
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint shell_gconf_signals [LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void gconf_value_changed (GConfClient *client, const char *key,
|
||||
GConfValue *new_value, gpointer user_data);
|
||||
|
||||
static void
|
||||
shell_gconf_init (ShellGConf *gconf)
|
||||
{
|
||||
gconf->client = gconf_client_get_default ();
|
||||
gconf_client_add_dir (gconf->client, SHELL_GCONF_DIR,
|
||||
GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);
|
||||
g_signal_connect (gconf->client, "value_changed",
|
||||
G_CALLBACK (gconf_value_changed), gconf);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_gconf_finalize (GObject *object)
|
||||
{
|
||||
ShellGConf *gconf = SHELL_GCONF (object);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (gconf->client,
|
||||
gconf_value_changed, gconf);
|
||||
g_object_unref (gconf->client);
|
||||
|
||||
G_OBJECT_CLASS (shell_gconf_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_gconf_class_init (ShellGConfClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = shell_gconf_finalize;
|
||||
|
||||
/**
|
||||
* ShellGConf::changed:
|
||||
* @gconf: the #ShellGConf
|
||||
*
|
||||
* Emitted when a key in a watched directory is changed. The signal
|
||||
* detail indicates which key changed. Eg, connect to
|
||||
* "changed::sidebar/visible" to be notified when "sidebar/visible"
|
||||
* changes. For gnome-shell's own GConf keys, the signal detail will
|
||||
* be the relative path from the top of the gnome-shell GConf
|
||||
* hierarchy ("/desktop/gnome/shell"). If you want to be notified
|
||||
* about the value of a non-gnome-shell key, you must first call
|
||||
* shell_gconf_watch_directory(), and then use the full GConf key path
|
||||
* as the signal detail.
|
||||
*/
|
||||
shell_gconf_signals[CHANGED] =
|
||||
g_signal_new ("changed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
|
||||
G_STRUCT_OFFSET (ShellGConfClass, changed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_gconf_get_default:
|
||||
*
|
||||
* Gets the default #ShellGConf
|
||||
*
|
||||
* Return value: (transfer none): the default #ShellGConf
|
||||
*/
|
||||
ShellGConf *
|
||||
shell_gconf_get_default (void)
|
||||
{
|
||||
static ShellGConf *gconf = NULL;
|
||||
|
||||
if (!gconf)
|
||||
gconf = g_object_new (SHELL_TYPE_GCONF, NULL);
|
||||
|
||||
return gconf;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_gconf_watch_directory:
|
||||
* @gconf: a #ShellGConf
|
||||
* @directory: the path of a GConf directory to watch for changes in
|
||||
*
|
||||
* Adds @directory to the list of directories to watch; you must call
|
||||
* this before connecting to #ShellGConf::changed for a key outside of
|
||||
* the gnome-shell GConf tree.
|
||||
*/
|
||||
void
|
||||
shell_gconf_watch_directory (ShellGConf *gconf, const char *directory)
|
||||
{
|
||||
gconf_client_add_dir (gconf->client, directory,
|
||||
GCONF_CLIENT_PRELOAD_NONE, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gconf_value_changed (GConfClient *client, const char *key,
|
||||
GConfValue *new_value, gpointer user_data)
|
||||
{
|
||||
ShellGConf *gconf = user_data;
|
||||
GQuark detail;
|
||||
|
||||
if (g_str_has_prefix (key, SHELL_GCONF_DIR "/"))
|
||||
key += strlen (SHELL_GCONF_DIR "/");
|
||||
|
||||
/* This will create a lot of junk quarks, but it's the best we
|
||||
* can do with gjs's current callback support.
|
||||
*/
|
||||
detail = g_quark_from_string (key);
|
||||
g_signal_emit (gconf, shell_gconf_signals[CHANGED], detail);
|
||||
}
|
||||
|
||||
static char *
|
||||
resolve_key (const char *key)
|
||||
{
|
||||
if (*key == '/')
|
||||
return g_strdup (key);
|
||||
else
|
||||
return g_build_filename (SHELL_GCONF_DIR, key, NULL);
|
||||
}
|
||||
|
||||
|
||||
#define SIMPLE_GETTER(NAME, TYPE, GCONF_GETTER) \
|
||||
TYPE \
|
||||
NAME (ShellGConf *gconf, const char *key, GError **error) \
|
||||
{ \
|
||||
char *get_key = resolve_key (key); \
|
||||
TYPE value; \
|
||||
\
|
||||
value = GCONF_GETTER (gconf->client, get_key, error); \
|
||||
g_free (get_key); \
|
||||
return value; \
|
||||
}
|
||||
|
||||
#define LIST_GETTER(NAME, ELEMENT_TYPE) \
|
||||
GSList * \
|
||||
NAME (ShellGConf *gconf, const char *key, GError **error) \
|
||||
{ \
|
||||
char *get_key = resolve_key (key); \
|
||||
GSList *value; \
|
||||
\
|
||||
value = gconf_client_get_list (gconf->client, get_key, \
|
||||
ELEMENT_TYPE, error); \
|
||||
g_free (get_key); \
|
||||
return value; \
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_gconf_get_boolean:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Gets the value of @key, which must be boolean-valued.
|
||||
*
|
||||
* Return value: @key's value. If an error occurs, @error will be set
|
||||
* and the return value is undefined.
|
||||
**/
|
||||
SIMPLE_GETTER(shell_gconf_get_boolean, gboolean, gconf_client_get_bool)
|
||||
|
||||
/**
|
||||
* shell_gconf_get_int:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Gets the value of @key, which must be integer-valued.
|
||||
*
|
||||
* Return value: @key's value. If an error occurs, @error will be set
|
||||
* and the return value is undefined.
|
||||
**/
|
||||
SIMPLE_GETTER(shell_gconf_get_int, int, gconf_client_get_int)
|
||||
|
||||
/**
|
||||
* shell_gconf_get_float:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Gets the value of @key, which must be float-valued.
|
||||
*
|
||||
* Return value: @key's value. If an error occurs, @error will be set
|
||||
* and the return value is undefined.
|
||||
**/
|
||||
SIMPLE_GETTER(shell_gconf_get_float, float, gconf_client_get_float)
|
||||
|
||||
/**
|
||||
* shell_gconf_get_string:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Gets the value of @key, which must be string-valued.
|
||||
*
|
||||
* Return value: (transfer full): @key's value, or %NULL if an error
|
||||
* occurs.
|
||||
**/
|
||||
SIMPLE_GETTER(shell_gconf_get_string, char *, gconf_client_get_string)
|
||||
|
||||
/**
|
||||
* shell_gconf_get_boolean_list:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Gets the value of @key, which must be boolean-list-valued.
|
||||
*
|
||||
* Return value: (element-type gboolean) (transfer full): @key's
|
||||
* value, or %NULL if an error occurs.
|
||||
**/
|
||||
LIST_GETTER(shell_gconf_get_boolean_list, GCONF_VALUE_BOOL)
|
||||
|
||||
/**
|
||||
* shell_gconf_get_int_list:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Gets the value of @key, which must be integer-list-valued.
|
||||
*
|
||||
* Return value: (element-type int) (transfer full): @key's
|
||||
* value, or %NULL if an error occurs.
|
||||
**/
|
||||
LIST_GETTER(shell_gconf_get_int_list, GCONF_VALUE_INT)
|
||||
|
||||
/**
|
||||
* shell_gconf_get_float_list:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Gets the value of @key, which must be float-list-valued.
|
||||
*
|
||||
* Return value: (element-type float) (transfer full): @key's
|
||||
* value, or %NULL if an error occurs.
|
||||
**/
|
||||
LIST_GETTER(shell_gconf_get_float_list, GCONF_VALUE_FLOAT)
|
||||
|
||||
/**
|
||||
* shell_gconf_get_string_list:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Gets the value of @key, which must be string-list-valued.
|
||||
*
|
||||
* Return value: (element-type utf8) (transfer full): @key's
|
||||
* value, or %NULL if an error occurs.
|
||||
**/
|
||||
LIST_GETTER(shell_gconf_get_string_list, GCONF_VALUE_STRING)
|
||||
|
||||
|
||||
#define SIMPLE_SETTER(NAME, TYPE, GCONF_SETTER) \
|
||||
void \
|
||||
NAME (ShellGConf *gconf, const char *key, TYPE value, GError **error) \
|
||||
{ \
|
||||
char *set_key = resolve_key (key); \
|
||||
\
|
||||
GCONF_SETTER (gconf->client, set_key, value, error); \
|
||||
g_free (set_key); \
|
||||
}
|
||||
|
||||
#define LIST_SETTER(NAME, ELEMENT_TYPE) \
|
||||
void \
|
||||
NAME (ShellGConf *gconf, const char *key, \
|
||||
GSList *value, GError **error) \
|
||||
{ \
|
||||
char *set_key = resolve_key (key); \
|
||||
\
|
||||
gconf_client_set_list (gconf->client, set_key, ELEMENT_TYPE, \
|
||||
value, error); \
|
||||
g_free (set_key); \
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_gconf_set_boolean:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @value: value to set @key to
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Sets the value of @key to @value.
|
||||
**/
|
||||
SIMPLE_SETTER(shell_gconf_set_boolean, gboolean, gconf_client_set_bool)
|
||||
|
||||
/**
|
||||
* shell_gconf_set_int:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @value: value to set @key to
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Sets the value of @key to @value.
|
||||
**/
|
||||
SIMPLE_SETTER(shell_gconf_set_int, int, gconf_client_set_int)
|
||||
|
||||
/**
|
||||
* shell_gconf_set_float:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @value: value to set @key to
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Sets the value of @key to @value.
|
||||
**/
|
||||
SIMPLE_SETTER(shell_gconf_set_float, float, gconf_client_set_float)
|
||||
|
||||
/**
|
||||
* shell_gconf_set_string:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @value: value to set @key to
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Sets the value of @key to @value.
|
||||
**/
|
||||
SIMPLE_SETTER(shell_gconf_set_string, const char *, gconf_client_set_string)
|
||||
|
||||
/**
|
||||
* shell_gconf_set_boolean_list:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @value: (transfer none): value to set @key to
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Sets the value of @key to @value.
|
||||
**/
|
||||
LIST_SETTER(shell_gconf_set_boolean_list, GCONF_VALUE_BOOL)
|
||||
|
||||
/**
|
||||
* shell_gconf_set_int_list:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @value: (transfer none): value to set @key to
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Sets the value of @key to @value.
|
||||
**/
|
||||
LIST_SETTER(shell_gconf_set_int_list, GCONF_VALUE_INT)
|
||||
|
||||
/**
|
||||
* shell_gconf_set_float_list:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @value: (transfer none): value to set @key to
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Sets the value of @key to @value.
|
||||
**/
|
||||
LIST_SETTER(shell_gconf_set_float_list, GCONF_VALUE_FLOAT)
|
||||
|
||||
/**
|
||||
* shell_gconf_set_string_list:
|
||||
* @gconf: a #ShellGConf
|
||||
* @key: a GConf key (as described in the #ShellGConf docs)
|
||||
* @value: (transfer none): value to set @key to
|
||||
* @error: a #GError, which will be set on error
|
||||
*
|
||||
* Sets the value of @key to @value.
|
||||
**/
|
||||
LIST_SETTER(shell_gconf_set_string_list, GCONF_VALUE_STRING)
|
95
src/shell-gconf.h
Normal file
95
src/shell-gconf.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#ifndef SHELL_GCONF_H
|
||||
#define SHELL_GCONF_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _ShellGConf ShellGConf;
|
||||
typedef struct _ShellGConfClass ShellGConfClass;
|
||||
|
||||
#define SHELL_TYPE_GCONF (shell_gconf_get_type ())
|
||||
#define SHELL_GCONF(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_GCONF, ShellGConf))
|
||||
#define SHELL_GCONF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_GCONF, ShellGConfClass))
|
||||
#define SHELL_IS_GCONF(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_GCONF))
|
||||
#define SHELL_IS_GCONF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_GCONF))
|
||||
#define SHELL_GCONF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_GCONF, ShellGConfClass))
|
||||
|
||||
struct _ShellGConfClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* signals */
|
||||
void (*changed) (ShellGConf *gconf);
|
||||
};
|
||||
|
||||
#define SHELL_GCONF_DIR "/desktop/gnome/shell"
|
||||
|
||||
GType shell_gconf_get_type (void) G_GNUC_CONST;
|
||||
ShellGConf *shell_gconf_get_default (void);
|
||||
|
||||
void shell_gconf_watch_directory (ShellGConf *gconf,
|
||||
const char *directory);
|
||||
|
||||
gboolean shell_gconf_get_boolean (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GError **error);
|
||||
int shell_gconf_get_int (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GError **error);
|
||||
float shell_gconf_get_float (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GError **error);
|
||||
char *shell_gconf_get_string (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GError **error);
|
||||
GSList *shell_gconf_get_boolean_list (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GError **error);
|
||||
GSList *shell_gconf_get_int_list (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GError **error);
|
||||
GSList *shell_gconf_get_float_list (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GError **error);
|
||||
GSList *shell_gconf_get_string_list (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GError **error);
|
||||
|
||||
void shell_gconf_set_boolean (ShellGConf *gconf,
|
||||
const char *key,
|
||||
gboolean value,
|
||||
GError **error);
|
||||
void shell_gconf_set_int (ShellGConf *gconf,
|
||||
const char *key,
|
||||
int value,
|
||||
GError **error);
|
||||
void shell_gconf_set_float (ShellGConf *gconf,
|
||||
const char *key,
|
||||
float value,
|
||||
GError **error);
|
||||
void shell_gconf_set_string (ShellGConf *gconf,
|
||||
const char *key,
|
||||
const char *value,
|
||||
GError **error);
|
||||
void shell_gconf_set_boolean_list (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GSList *value,
|
||||
GError **error);
|
||||
void shell_gconf_set_int_list (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GSList *value,
|
||||
GError **error);
|
||||
void shell_gconf_set_float_list (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GSList *value,
|
||||
GError **error);
|
||||
void shell_gconf_set_string_list (ShellGConf *gconf,
|
||||
const char *key,
|
||||
GSList *value,
|
||||
GError **error);
|
||||
|
||||
#endif
|
||||
|
240
src/shell-generic-container.c
Normal file
240
src/shell-generic-container.c
Normal file
@ -0,0 +1,240 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/**
|
||||
* SECTION:shell-generic-container
|
||||
* @short_description: A container class with signals for allocation
|
||||
*
|
||||
* #ShellGenericContainer is mainly a workaround for the current
|
||||
* lack of GObject subclassing + vfunc overrides in gjs. We
|
||||
* implement the container interface, but proxy the virtual functions
|
||||
* into signals, which gjs can catch.
|
||||
*/
|
||||
|
||||
/* Example implementation of a horzontal box with PACK_EXPAND for all,
|
||||
vertically and horizontally centering.
|
||||
|
||||
function TestFixedBox() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
TestFixedBox.prototype = {
|
||||
_init : function () {
|
||||
this.actor = new Shell.GenericContainer();
|
||||
this.spacing = 4;
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, function (actor, for_height, alloc) {
|
||||
let children = this.actor.get_children();
|
||||
let max_child_min = 0;
|
||||
let max_child_nat = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let spacing = i > 0 && i < children.length-1 ? this.spacing : 0;
|
||||
let [child_min, child_nat] = children[i].get_preferred_width(for_height);
|
||||
if (child_min > max_child_min)
|
||||
max_child_min = child_min;
|
||||
if (child_nat > max_child_nat)
|
||||
max_child_nat = child_nat;
|
||||
}
|
||||
let totalSpacing = this.spacing * Math.abs(children.length - 1);
|
||||
alloc.min_size = children.length * max_child_min + totalSpacing;
|
||||
alloc.nat_size = children.length * max_child_nat + totalSpacing;
|
||||
}));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, function (actor, for_width, alloc) {
|
||||
let children = this.actor.get_children();
|
||||
let max_child_min = 0;
|
||||
let max_child_nat = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [child_min, child_nat] = children[i].get_preferred_height(for_width);
|
||||
if (child_min > max_child_min)
|
||||
max_child_min = child_min;
|
||||
if (child_nat > max_child_nat)
|
||||
max_child_nat = child_nat;
|
||||
}
|
||||
alloc.min_size = max_child_min;
|
||||
alloc.nat_size = max_child_nat;
|
||||
}));
|
||||
this.actor.connect('allocate', Lang.bind(this, function (actor, box, flags) {
|
||||
let children = this.actor.get_children();
|
||||
let totalSpacing = (this.spacing * Math.abs(children.length - 1));
|
||||
let child_width = (box.x2 - box.x1 - totalSpacing) / (children.length);
|
||||
let child_height = box.y2 - box.y1;
|
||||
|
||||
let x = box.x1;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [child_min, child_nat] = children[i].get_preferred_height(child_width);
|
||||
let vSpacing = Math.abs(child_height - child_nat) / 2;
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.x1 = x;
|
||||
childBox.y1 = vSpacing;
|
||||
childBox.x2 = x+child_width;
|
||||
childBox.y2 = child_height - vSpacing;
|
||||
children[i].allocate(childBox, flags);
|
||||
x += this.spacing + child_width;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function runTestFixedBox() {
|
||||
let testBox = new TestFixedBox();
|
||||
let c = new Clutter.Color();
|
||||
c.from_pixel(0xff0000a0);
|
||||
let r = new Clutter.Rectangle({ width: 50, height: 100, color: c });
|
||||
testBox.actor.add_actor(r);
|
||||
r = new Clutter.Rectangle({ width: 90, height: 70, color: c });
|
||||
testBox.actor.add_actor(r);
|
||||
r = new Clutter.Rectangle({ width: 90, height: 70, color: c });
|
||||
testBox.actor.add_actor(r);
|
||||
r = new Clutter.Rectangle({ width: 30, height: 10, color: c });
|
||||
testBox.actor.add_actor(r);
|
||||
|
||||
c.from_pixel(0x00ff00a0);
|
||||
let borderBox = new Big.Box({ border: 1, border_color: c });
|
||||
borderBox.set_position(100, 100);
|
||||
borderBox.append(testBox.actor, Big.BoxPackFlags.NONE);
|
||||
Shell.Global.get().stage.add_actor(borderBox);
|
||||
}
|
||||
*/
|
||||
|
||||
#include "shell-generic-container.h"
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <girepository.h>
|
||||
|
||||
G_DEFINE_TYPE(ShellGenericContainer, shell_generic_container, CLUTTER_TYPE_GROUP);
|
||||
|
||||
struct _ShellGenericContainerPrivate {
|
||||
gpointer dummy;
|
||||
};
|
||||
|
||||
/* Signals */
|
||||
enum
|
||||
{
|
||||
GET_PREFERRED_WIDTH,
|
||||
GET_PREFERRED_HEIGHT,
|
||||
ALLOCATE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint shell_generic_container_signals [LAST_SIGNAL] = { 0 };
|
||||
|
||||
|
||||
static gpointer
|
||||
shell_generic_container_allocation_ref (ShellGenericContainerAllocation *alloc)
|
||||
{
|
||||
alloc->_refcount++;
|
||||
return alloc;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_allocation_unref (ShellGenericContainerAllocation *alloc)
|
||||
{
|
||||
if (--alloc->_refcount == 0)
|
||||
{
|
||||
g_slice_free1 (sizeof (ShellGenericContainerAllocation), alloc);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_allocate (ClutterActor *self,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
/* chain up to set actor->allocation */
|
||||
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->allocate (self, box, flags);
|
||||
|
||||
g_signal_emit (G_OBJECT (self), shell_generic_container_signals[ALLOCATE], 0,
|
||||
box, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_get_preferred_width (ClutterActor *actor,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
ShellGenericContainerAllocation *alloc = g_slice_alloc0 (sizeof (ShellGenericContainerAllocation));
|
||||
alloc->_refcount = 1;
|
||||
g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_WIDTH], 0,
|
||||
for_height, alloc);
|
||||
if (min_width_p)
|
||||
*min_width_p = alloc->min_size;
|
||||
if (natural_width_p)
|
||||
*natural_width_p = alloc->natural_size;
|
||||
shell_generic_container_allocation_unref (alloc);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_get_preferred_height (ClutterActor *actor,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
ShellGenericContainerAllocation *alloc = g_slice_alloc0 (sizeof (ShellGenericContainerAllocation));
|
||||
alloc->_refcount = 1;
|
||||
g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_HEIGHT], 0,
|
||||
for_width, alloc);
|
||||
if (min_height_p)
|
||||
*min_height_p = alloc->min_size;
|
||||
if (natural_height_p)
|
||||
*natural_height_p = alloc->natural_size;
|
||||
shell_generic_container_allocation_unref (alloc);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_class_init (ShellGenericContainerClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
|
||||
actor_class->get_preferred_width = shell_generic_container_get_preferred_width;
|
||||
actor_class->get_preferred_height = shell_generic_container_get_preferred_height;
|
||||
actor_class->allocate = shell_generic_container_allocate;
|
||||
|
||||
shell_generic_container_signals[GET_PREFERRED_WIDTH] =
|
||||
g_signal_new ("get-preferred-width",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
gi_cclosure_marshal_generic,
|
||||
G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
|
||||
|
||||
shell_generic_container_signals[GET_PREFERRED_HEIGHT] =
|
||||
g_signal_new ("get-preferred-height",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
gi_cclosure_marshal_generic,
|
||||
G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
|
||||
|
||||
shell_generic_container_signals[ALLOCATE] =
|
||||
g_signal_new ("allocate",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
gi_cclosure_marshal_generic,
|
||||
G_TYPE_NONE, 2, CLUTTER_TYPE_ACTOR_BOX, CLUTTER_TYPE_ALLOCATION_FLAGS);
|
||||
|
||||
g_type_class_add_private (gobject_class, sizeof (ShellGenericContainerPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_init (ShellGenericContainer *area)
|
||||
{
|
||||
area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_GENERIC_CONTAINER,
|
||||
ShellGenericContainerPrivate);
|
||||
}
|
||||
|
||||
GType shell_generic_container_allocation_get_type (void)
|
||||
{
|
||||
static GType gtype = G_TYPE_INVALID;
|
||||
if (gtype == G_TYPE_INVALID)
|
||||
{
|
||||
gtype = g_boxed_type_register_static ("ShellGenericContainerAllocation",
|
||||
(GBoxedCopyFunc)shell_generic_container_allocation_ref,
|
||||
(GBoxedFreeFunc)shell_generic_container_allocation_unref);
|
||||
}
|
||||
return gtype;
|
||||
}
|
44
src/shell-generic-container.h
Normal file
44
src/shell-generic-container.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef __SHELL_GENERIC_CONTAINER_H__
|
||||
#define __SHELL_GENERIC_CONTAINER_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define SHELL_TYPE_GENERIC_CONTAINER (shell_generic_container_get_type ())
|
||||
#define SHELL_GENERIC_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_GENERIC_CONTAINER, ShellGenericContainer))
|
||||
#define SHELL_GENERIC_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_GENERIC_CONTAINER, ShellGenericContainerClass))
|
||||
#define SHELL_IS_GENERIC_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_GENERIC_CONTAINER))
|
||||
#define SHELL_IS_GENERIC_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_GENERIC_CONTAINER))
|
||||
#define SHELL_GENERIC_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_GENERIC_CONTAINER, ShellGenericContainerClass))
|
||||
|
||||
typedef struct {
|
||||
float min_size;
|
||||
float natural_size;
|
||||
|
||||
/* <private> */
|
||||
guint _refcount;
|
||||
} ShellGenericContainerAllocation;
|
||||
|
||||
#define SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION (shell_generic_container_allocation_get_type ())
|
||||
GType shell_generic_container_allocation_get_type (void);
|
||||
|
||||
typedef struct _ShellGenericContainer ShellGenericContainer;
|
||||
typedef struct _ShellGenericContainerClass ShellGenericContainerClass;
|
||||
|
||||
typedef struct _ShellGenericContainerPrivate ShellGenericContainerPrivate;
|
||||
|
||||
struct _ShellGenericContainer
|
||||
{
|
||||
ClutterGroup parent;
|
||||
|
||||
ShellGenericContainerPrivate *priv;
|
||||
};
|
||||
|
||||
struct _ShellGenericContainerClass
|
||||
{
|
||||
ClutterGroupClass parent_class;
|
||||
};
|
||||
|
||||
GType shell_generic_container_get_type (void) G_GNUC_CONST;
|
||||
|
||||
#endif /* __SHELL_GENERIC_CONTAINER_H__ */
|
@ -14,7 +14,8 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <dbus/dbus-glib.h>
|
||||
#include <libgnomeui/gnome-thumbnail.h>
|
||||
#include <gio/gio.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <math.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
|
||||
@ -101,7 +102,7 @@ shell_global_get_property(GObject *object,
|
||||
g_value_set_object (value, mutter_plugin_get_overlay_group (global->plugin));
|
||||
break;
|
||||
case PROP_SCREEN:
|
||||
g_value_set_object (value, mutter_plugin_get_screen (global->plugin));
|
||||
g_value_set_object (value, shell_global_get_screen (global));
|
||||
break;
|
||||
case PROP_SCREEN_WIDTH:
|
||||
{
|
||||
@ -270,49 +271,6 @@ shell_global_class_init (ShellGlobalClass *klass)
|
||||
G_PARAM_READABLE));
|
||||
}
|
||||
|
||||
/**
|
||||
* search_path_init:
|
||||
*
|
||||
* search_path_init and get_applications_search_path below were copied from glib/gio/gdesktopappinfo.c
|
||||
* copyright Red Hat, Inc., written by Alex Larsson, licensed under the LGPL
|
||||
*
|
||||
* Return value: location of an array with user and system application directories.
|
||||
*/
|
||||
static gpointer
|
||||
search_path_init (gpointer data)
|
||||
{
|
||||
char **args = NULL;
|
||||
const char * const *data_dirs;
|
||||
const char *user_data_dir;
|
||||
int i, length, j;
|
||||
|
||||
data_dirs = g_get_system_data_dirs ();
|
||||
length = g_strv_length ((char **)data_dirs);
|
||||
|
||||
args = g_new (char *, length + 2);
|
||||
|
||||
j = 0;
|
||||
user_data_dir = g_get_user_data_dir ();
|
||||
args[j++] = g_build_filename (user_data_dir, "applications", NULL);
|
||||
for (i = 0; i < length; i++)
|
||||
args[j++] = g_build_filename (data_dirs[i],
|
||||
"applications", NULL);
|
||||
args[j++] = NULL;
|
||||
|
||||
return args;
|
||||
}
|
||||
/**
|
||||
* get_applications_search_path:
|
||||
*
|
||||
* Return value: location of an array with user and system application directories.
|
||||
*/
|
||||
static const char * const *
|
||||
get_applications_search_path (void)
|
||||
{
|
||||
static GOnce once_init = G_ONCE_INIT;
|
||||
return g_once (&once_init, search_path_init, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_clutter_texture_set_from_pixbuf:
|
||||
* texture: #ClutterTexture to be modified
|
||||
@ -338,135 +296,6 @@ shell_clutter_texture_set_from_pixbuf (ClutterTexture *texture,
|
||||
0, NULL);
|
||||
}
|
||||
|
||||
static GnomeThumbnailFactory *thumbnail_factory;
|
||||
|
||||
/**
|
||||
* shell_get_thumbnail:
|
||||
*
|
||||
* @uri: URI of the file to thumbnail
|
||||
*
|
||||
* @mime_type: Mime-Type of the file to thumbnail
|
||||
*
|
||||
* Return value: #GdkPixbuf containing a thumbnail for file @uri
|
||||
* if the thumbnail exists or can be generated, %NULL otherwise
|
||||
*/
|
||||
GdkPixbuf *
|
||||
shell_get_thumbnail(const gchar *uri,
|
||||
const gchar *mime_type)
|
||||
{
|
||||
char *existing_thumbnail;
|
||||
GdkPixbuf *pixbuf = NULL;
|
||||
GError *error = NULL;
|
||||
GFile *file = NULL;
|
||||
GFileInfo *file_info = NULL;
|
||||
GTimeVal mtime_g;
|
||||
time_t mtime = 0;
|
||||
|
||||
file = g_file_new_for_uri (uri);
|
||||
file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
|
||||
g_object_unref (file);
|
||||
if (file_info) {
|
||||
g_file_info_get_modification_time (file_info, &mtime_g);
|
||||
g_object_unref (file_info);
|
||||
mtime = (time_t) mtime_g.tv_sec;
|
||||
}
|
||||
|
||||
if (thumbnail_factory == NULL)
|
||||
thumbnail_factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
|
||||
|
||||
existing_thumbnail = gnome_thumbnail_factory_lookup (thumbnail_factory, uri, mtime);
|
||||
|
||||
if (existing_thumbnail != NULL)
|
||||
{
|
||||
pixbuf = gdk_pixbuf_new_from_file(existing_thumbnail, &error);
|
||||
if (error != NULL)
|
||||
{
|
||||
g_warning("Could not generate a pixbuf from file %s: %s", existing_thumbnail, error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
else if (gnome_thumbnail_factory_has_valid_failed_thumbnail (thumbnail_factory, uri, mtime))
|
||||
return NULL;
|
||||
else if (gnome_thumbnail_factory_can_thumbnail (thumbnail_factory, uri, mime_type, mtime))
|
||||
{
|
||||
pixbuf = gnome_thumbnail_factory_generate_thumbnail (thumbnail_factory, uri, mime_type);
|
||||
if (pixbuf)
|
||||
{
|
||||
// we need to save the thumbnail so that we don't need to generate it again in the future
|
||||
gnome_thumbnail_factory_save_thumbnail (thumbnail_factory, pixbuf, uri, mtime);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("Could not generate thumbnail for %s", uri);
|
||||
gnome_thumbnail_factory_create_failed_thumbnail (thumbnail_factory, uri, mtime);
|
||||
}
|
||||
}
|
||||
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* shell_get_categories_for_desktop_file:
|
||||
*
|
||||
* @desktop_file_name: name of the desktop file for which to retrieve categories
|
||||
*
|
||||
* Return value: (element-type char*) (transfer full): List of categories
|
||||
*
|
||||
*/
|
||||
GSList *
|
||||
shell_get_categories_for_desktop_file(const char *desktop_file_name)
|
||||
{
|
||||
GKeyFile *key_file;
|
||||
const char * const *search_dirs;
|
||||
char **categories = NULL;
|
||||
GSList *categories_list = NULL;
|
||||
GError *error = NULL;
|
||||
gsize len;
|
||||
int i;
|
||||
|
||||
key_file = g_key_file_new ();
|
||||
search_dirs = get_applications_search_path();
|
||||
|
||||
g_key_file_load_from_dirs (key_file, desktop_file_name, (const char **)search_dirs, NULL, 0, &error);
|
||||
|
||||
if (error != NULL)
|
||||
{
|
||||
g_warning ("Error when loading a key file for %s: %s", desktop_file_name, error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
else
|
||||
{
|
||||
categories = g_key_file_get_string_list (key_file,
|
||||
"Desktop Entry",
|
||||
"Categories",
|
||||
&len,
|
||||
&error);
|
||||
if (error != NULL)
|
||||
{
|
||||
// "Categories" is not a required key in the desktop files, so it's ok if we didn't find it
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
|
||||
g_key_file_free (key_file);
|
||||
|
||||
if (categories == NULL)
|
||||
return NULL;
|
||||
|
||||
// gjs currently does not support returning arrays (other than a NULL value for an array), so we need
|
||||
// to convert the array we are returning to GSList, returning which gjs supports.
|
||||
// See http://bugzilla.gnome.org/show_bug.cgi?id=560567 for more info on gjs array support.
|
||||
for (i = 0; categories[i]; i++)
|
||||
{
|
||||
categories_list = g_slist_prepend (categories_list, g_strdup (categories[i]));
|
||||
}
|
||||
|
||||
g_strfreev (categories);
|
||||
|
||||
return categories_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_get_event_key_symbol:
|
||||
*
|
||||
@ -608,6 +437,17 @@ shell_global_set_stage_input_region (ShellGlobal *global,
|
||||
shell_global_set_stage_input_mode (global, global->input_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_screen:
|
||||
*
|
||||
* Return value: (transfer none): The default #MetaScreen
|
||||
*/
|
||||
MetaScreen *
|
||||
shell_global_get_screen (ShellGlobal *global)
|
||||
{
|
||||
return mutter_plugin_get_screen (global->plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_windows:
|
||||
*
|
||||
@ -857,22 +697,24 @@ shell_global_grab_dbus_service (ShellGlobal *global)
|
||||
*/
|
||||
exit (0);
|
||||
}
|
||||
|
||||
g_object_unref (bus);
|
||||
}
|
||||
|
||||
void
|
||||
shell_global_start_task_panel (ShellGlobal *global)
|
||||
{
|
||||
const char* panel_args[] = {"gnomeshell-taskpanel", SHELL_DBUS_SERVICE, NULL};
|
||||
GError *error = NULL;
|
||||
|
||||
if (!g_spawn_async (NULL, (char**)panel_args, NULL, G_SPAWN_SEARCH_PATH, NULL,
|
||||
NULL, NULL, &error))
|
||||
/* Also grab org.gnome.Panel to replace any existing panel process,
|
||||
* unless a special environment variable is passed. The environment
|
||||
* variable is used by the gnome-shell (no --replace) launcher in
|
||||
* Xephyr */
|
||||
if (!g_getenv ("GNOME_SHELL_NO_REPLACE_PANEL"))
|
||||
{
|
||||
g_critical ("failed to execute %s: %s", panel_args[0], error->message);
|
||||
g_clear_error (&error);
|
||||
if (!dbus_g_proxy_call (bus, "RequestName", &error, G_TYPE_STRING,
|
||||
"org.gnome.Panel", G_TYPE_UINT,
|
||||
DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE,
|
||||
G_TYPE_INVALID, G_TYPE_UINT,
|
||||
&request_name_result, G_TYPE_INVALID))
|
||||
{
|
||||
g_print ("failed to acquire org.gnome.Panel: %s\n", error->message);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
g_object_unref (bus);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -886,53 +728,6 @@ grab_notify (GtkWidget *widget, gboolean was_grabbed, gpointer user_data)
|
||||
shell_global_set_stage_input_mode (global, global->input_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_create_vertical_gradient:
|
||||
* @top: the color at the top
|
||||
* @bottom: the color at the bottom
|
||||
*
|
||||
* Creates a vertical gradient actor.
|
||||
*
|
||||
* Return value: (transfer none): a #ClutterCairoTexture actor with the
|
||||
* gradient. The texture actor is floating, hence (transfer none).
|
||||
*/
|
||||
ClutterCairoTexture *
|
||||
shell_global_create_vertical_gradient (ClutterColor *top,
|
||||
ClutterColor *bottom)
|
||||
{
|
||||
ClutterCairoTexture *texture;
|
||||
cairo_t *cr;
|
||||
cairo_pattern_t *pattern;
|
||||
|
||||
/* Draw the gradient on an 8x8 pixel texture. Because the gradient is drawn
|
||||
* from the uppermost to the lowermost row, after stretching 1/16 of the
|
||||
* texture height has the top color and 1/16 has the bottom color. The 8
|
||||
* pixel width is chosen for reasons related to graphics hardware internals.
|
||||
*/
|
||||
texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 8));
|
||||
cr = clutter_cairo_texture_create (texture);
|
||||
|
||||
pattern = cairo_pattern_create_linear (0, 0, 0, 8);
|
||||
cairo_pattern_add_color_stop_rgba (pattern, 0,
|
||||
top->red / 255.,
|
||||
top->green / 255.,
|
||||
top->blue / 255.,
|
||||
top->alpha / 255.);
|
||||
cairo_pattern_add_color_stop_rgba (pattern, 1,
|
||||
bottom->red / 255.,
|
||||
bottom->green / 255.,
|
||||
bottom->blue / 255.,
|
||||
bottom->alpha / 255.);
|
||||
|
||||
cairo_set_source (cr, pattern);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_pattern_destroy (pattern);
|
||||
cairo_destroy (cr);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the global->root_pixmap actor with the root window's pixmap or fails
|
||||
* with a warning.
|
||||
@ -1020,6 +815,46 @@ root_pixmap_destroy (GObject *sender, gpointer data)
|
||||
global->root_pixmap = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_format_time_relative_pretty:
|
||||
* @global:
|
||||
* @delta: Time in seconds since the current time
|
||||
* @text: (out): Relative human-consumption-only time string
|
||||
* @next_update: (out): Time in seconds until we should redisplay the time
|
||||
*
|
||||
* Format a time value for human consumption only. The passed time
|
||||
* value is a delta in terms of seconds from the current time.
|
||||
* This function needs to be in C because of its use of ngettext() which
|
||||
* is not accessible from JavaScript.
|
||||
*/
|
||||
void
|
||||
shell_global_format_time_relative_pretty (ShellGlobal *global,
|
||||
guint delta,
|
||||
char **text,
|
||||
guint *next_update)
|
||||
{
|
||||
#define MINUTE (60)
|
||||
#define HOUR (MINUTE*60)
|
||||
#define DAY (HOUR*24)
|
||||
#define WEEK (DAY*7)
|
||||
if (delta < MINUTE) {
|
||||
*text = g_strdup (_("Less than a minute ago"));
|
||||
*next_update = MINUTE - delta;
|
||||
} else if (delta < HOUR) {
|
||||
*text = g_strdup_printf (ngettext ("%d minute ago", "%d minutes ago", delta / MINUTE), delta / MINUTE);
|
||||
*next_update = MINUTE - (delta % MINUTE);
|
||||
} else if (delta < DAY) {
|
||||
*text = g_strdup_printf (ngettext ("%d hour ago", "%d hours ago", delta / HOUR), delta / HOUR);
|
||||
*next_update = HOUR - (delta % HOUR);
|
||||
} else if (delta < WEEK) {
|
||||
*text = g_strdup_printf (ngettext ("%d day ago", "%d days ago", delta / DAY), delta / DAY);
|
||||
*next_update = DAY - (delta % DAY);
|
||||
} else {
|
||||
*text = g_strdup_printf (ngettext ("%d week ago", "%d weeks ago", delta / WEEK), delta / WEEK);
|
||||
*next_update = WEEK - (delta % WEEK);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_create_root_pixmap_actor:
|
||||
* @global: a #ShellGlobal
|
||||
@ -1076,50 +911,3 @@ shell_global_create_root_pixmap_actor (ShellGlobal *global)
|
||||
|
||||
return clutter_clone_new (global->root_pixmap);
|
||||
}
|
||||
|
||||
void
|
||||
shell_global_clutter_cairo_texture_draw_clock (ClutterCairoTexture *texture,
|
||||
int hour,
|
||||
int minute)
|
||||
{
|
||||
cairo_t *cr;
|
||||
guint width, height;
|
||||
double xc, yc, radius, hour_radius, minute_radius;
|
||||
double angle;
|
||||
|
||||
clutter_cairo_texture_get_surface_size (texture, &width, &height);
|
||||
xc = (double)width / 2;
|
||||
yc = (double)height / 2;
|
||||
radius = (double)(MIN(width, height)) / 2 - 2;
|
||||
minute_radius = radius - 3;
|
||||
hour_radius = radius / 2;
|
||||
|
||||
clutter_cairo_texture_clear (texture);
|
||||
cr = clutter_cairo_texture_create (texture);
|
||||
cairo_set_line_width (cr, 1.0);
|
||||
|
||||
/* Outline */
|
||||
cairo_arc (cr, xc, yc, radius, 0.0, 2.0 * M_PI);
|
||||
cairo_stroke (cr);
|
||||
|
||||
/* Hour hand. (We add a fraction to @hour for the minutes, then
|
||||
* convert to radians, and then subtract pi/2 because cairo's origin
|
||||
* is at 3:00, not 12:00.)
|
||||
*/
|
||||
angle = ((hour + minute / 60.0) / 12.0) * 2.0 * M_PI - M_PI / 2.0;
|
||||
cairo_move_to (cr, xc, yc);
|
||||
cairo_line_to (cr,
|
||||
xc + hour_radius * cos (angle),
|
||||
yc + hour_radius * sin (angle));
|
||||
cairo_stroke (cr);
|
||||
|
||||
/* Minute hand */
|
||||
angle = (minute / 60.0) * 2.0 * M_PI - M_PI / 2.0;
|
||||
cairo_move_to (cr, xc, yc);
|
||||
cairo_line_to (cr,
|
||||
xc + minute_radius * cos (angle),
|
||||
yc + minute_radius * sin (angle));
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_destroy (cr);
|
||||
}
|
||||
|
@ -36,10 +36,6 @@ GType shell_global_get_type (void) G_GNUC_CONST;
|
||||
gboolean shell_clutter_texture_set_from_pixbuf (ClutterTexture *texture,
|
||||
GdkPixbuf *pixbuf);
|
||||
|
||||
GdkPixbuf *shell_get_thumbnail(const gchar *uri, const gchar *mime_type);
|
||||
|
||||
GSList *shell_get_categories_for_desktop_file(const char *desktop_file_name);
|
||||
|
||||
guint16 shell_get_event_key_symbol(ClutterEvent *event);
|
||||
|
||||
guint16 shell_get_button_event_click_count(ClutterEvent *event);
|
||||
@ -48,9 +44,9 @@ ClutterActor *shell_get_event_related(ClutterEvent *event);
|
||||
|
||||
ShellGlobal *shell_global_get (void);
|
||||
|
||||
void shell_global_grab_dbus_service (ShellGlobal *global);
|
||||
MetaScreen *shell_global_get_screen (ShellGlobal *global);
|
||||
|
||||
void shell_global_start_task_panel (ShellGlobal *global);
|
||||
void shell_global_grab_dbus_service (ShellGlobal *global);
|
||||
|
||||
typedef enum {
|
||||
SHELL_STAGE_INPUT_MODE_NONREACTIVE,
|
||||
@ -73,15 +69,10 @@ void shell_global_ungrab_keyboard (ShellGlobal *global);
|
||||
|
||||
void shell_global_reexec_self (ShellGlobal *global);
|
||||
|
||||
ClutterCairoTexture *shell_global_create_vertical_gradient (ClutterColor *top,
|
||||
ClutterColor *bottom);
|
||||
void shell_global_format_time_relative_pretty (ShellGlobal *global, guint delta, char **text, guint *update_time);
|
||||
|
||||
ClutterActor *shell_global_create_root_pixmap_actor (ShellGlobal *global);
|
||||
|
||||
void shell_global_clutter_cairo_texture_draw_clock (ClutterCairoTexture *texture,
|
||||
int hour,
|
||||
int minute);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_GLOBAL_H__ */
|
||||
|
426
src/shell-overflow-list.c
Normal file
426
src/shell-overflow-list.c
Normal file
@ -0,0 +1,426 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include "shell-overflow-list.h"
|
||||
|
||||
G_DEFINE_TYPE (ShellOverflowList,
|
||||
shell_overflow_list,
|
||||
CLUTTER_TYPE_GROUP);
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_SPACING,
|
||||
PROP_ITEM_HEIGHT,
|
||||
PROP_DISPLAYED_COUNT,
|
||||
PROP_PAGE,
|
||||
PROP_N_PAGES
|
||||
};
|
||||
|
||||
struct _ShellOverflowListPrivate {
|
||||
guint item_height;
|
||||
guint spacing;
|
||||
guint page;
|
||||
guint n_pages;
|
||||
guint items_per_page;
|
||||
guint displayed_count;
|
||||
};
|
||||
|
||||
static void
|
||||
recalc_displayed_count (ShellOverflowList *self)
|
||||
{
|
||||
GList *children;
|
||||
int n_children;
|
||||
int displayed_count;
|
||||
int page, n_pages;
|
||||
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
|
||||
n_children = g_list_length (children);
|
||||
g_list_free (children);
|
||||
|
||||
page = self->priv->page;
|
||||
n_pages = self->priv->n_pages;
|
||||
if (page < n_pages-1)
|
||||
displayed_count = self->priv->items_per_page;
|
||||
else if (n_pages > 0)
|
||||
displayed_count = n_children - (self->priv->items_per_page * (n_pages-1));
|
||||
else
|
||||
displayed_count = 0;
|
||||
if (displayed_count != self->priv->displayed_count)
|
||||
{
|
||||
self->priv->displayed_count = displayed_count;
|
||||
g_object_notify (G_OBJECT (self), "displayed-count");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
shell_overflow_list_set_property(GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
ShellOverflowList *self = SHELL_OVERFLOW_LIST (object);
|
||||
ShellOverflowListPrivate *priv = self->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_SPACING:
|
||||
priv->spacing = g_value_get_float (value);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||||
break;
|
||||
case PROP_ITEM_HEIGHT:
|
||||
priv->item_height = g_value_get_float (value);
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||||
break;
|
||||
case PROP_PAGE:
|
||||
priv->page = g_value_get_uint (value);
|
||||
recalc_displayed_count (self);
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
shell_overflow_list_get_property(GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
ShellOverflowList *self = SHELL_OVERFLOW_LIST (object);
|
||||
ShellOverflowListPrivate *priv = self->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_SPACING:
|
||||
g_value_set_float (value, priv->spacing);
|
||||
break;
|
||||
case PROP_ITEM_HEIGHT:
|
||||
g_value_set_float (value, priv->spacing);
|
||||
break;
|
||||
case PROP_DISPLAYED_COUNT:
|
||||
g_value_set_uint (value, priv->displayed_count);
|
||||
break;
|
||||
case PROP_PAGE:
|
||||
g_value_set_uint (value, priv->page);
|
||||
break;
|
||||
case PROP_N_PAGES:
|
||||
g_value_set_uint (value, priv->n_pages);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
shell_overflow_list_allocate (ClutterActor *actor,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
ShellOverflowList *self = SHELL_OVERFLOW_LIST (actor);
|
||||
ShellOverflowListPrivate *priv = self->priv;
|
||||
GList *children, *iter;
|
||||
int n_pages;
|
||||
int n_children;
|
||||
int n_fits;
|
||||
float width;
|
||||
float curheight;
|
||||
float avail_height;
|
||||
gboolean overflow;
|
||||
|
||||
/* chain up to set actor->allocation */
|
||||
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->allocate (actor, box, flags);
|
||||
|
||||
width = box->x2 - box->x1;
|
||||
curheight = 0;
|
||||
avail_height = box->y2 - box->y1;
|
||||
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
|
||||
n_children = g_list_length (children);
|
||||
|
||||
n_fits = 0;
|
||||
n_pages = 1;
|
||||
overflow = FALSE;
|
||||
for (iter = children; iter; iter = iter->next)
|
||||
{
|
||||
ClutterActor *actor = CLUTTER_ACTOR (iter->data);
|
||||
ClutterActorBox child_box;
|
||||
|
||||
if (iter != children)
|
||||
curheight += priv->spacing;
|
||||
|
||||
if ((curheight + priv->item_height) > avail_height)
|
||||
{
|
||||
overflow = TRUE;
|
||||
curheight = 0;
|
||||
n_pages++;
|
||||
}
|
||||
else if (!overflow)
|
||||
n_fits++;
|
||||
|
||||
child_box.x1 = 0;
|
||||
child_box.x2 = width;
|
||||
child_box.y1 = curheight;
|
||||
child_box.y2 = child_box.y1 + priv->item_height;
|
||||
clutter_actor_allocate (actor, &child_box, flags);
|
||||
|
||||
curheight += priv->item_height;
|
||||
}
|
||||
|
||||
priv->items_per_page = n_fits;
|
||||
|
||||
if (n_pages != priv->n_pages)
|
||||
{
|
||||
priv->n_pages = n_pages;
|
||||
g_object_notify (G_OBJECT (self), "n-pages");
|
||||
}
|
||||
|
||||
recalc_displayed_count (self);
|
||||
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_overflow_list_paint (ClutterActor *actor)
|
||||
{
|
||||
ShellOverflowList *self = SHELL_OVERFLOW_LIST (actor);
|
||||
ShellOverflowListPrivate *priv = self->priv;
|
||||
GList *children, *iter;
|
||||
int i;
|
||||
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
|
||||
|
||||
if (children == NULL)
|
||||
return;
|
||||
|
||||
iter = g_list_nth (children, (priv->page) * priv->items_per_page);
|
||||
|
||||
i = 0;
|
||||
for (;iter && i < priv->items_per_page; iter = iter->next, i++)
|
||||
{
|
||||
ClutterActor *actor = CLUTTER_ACTOR (iter->data);
|
||||
|
||||
clutter_actor_paint (actor);
|
||||
}
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_overflow_list_pick (ClutterActor *actor,
|
||||
const ClutterColor *color)
|
||||
{
|
||||
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->pick (actor, color);
|
||||
|
||||
shell_overflow_list_paint (actor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
shell_overflow_list_get_preferred_height (ClutterActor *actor,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
ShellOverflowList *self = SHELL_OVERFLOW_LIST (actor);
|
||||
ShellOverflowListPrivate *priv = self->priv;
|
||||
GList *children;
|
||||
|
||||
if (min_height_p)
|
||||
*min_height_p = 0;
|
||||
|
||||
if (natural_height_p)
|
||||
{
|
||||
int n_children;
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
|
||||
n_children = g_list_length (children);
|
||||
if (n_children == 0)
|
||||
*natural_height_p = 0;
|
||||
else
|
||||
*natural_height_p = (n_children - 1) * (priv->item_height + priv->spacing) + priv->item_height;
|
||||
g_list_free (children);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
shell_overflow_list_get_preferred_width (ClutterActor *actor,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
ShellOverflowList *self = SHELL_OVERFLOW_LIST (actor);
|
||||
gboolean first = TRUE;
|
||||
float min = 0, natural = 0;
|
||||
GList *iter;
|
||||
GList *children;
|
||||
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
|
||||
|
||||
for (iter = children; iter; iter = iter->next)
|
||||
{
|
||||
ClutterActor *child = iter->data;
|
||||
float child_min, child_natural;
|
||||
|
||||
clutter_actor_get_preferred_width (child,
|
||||
for_height,
|
||||
&child_min,
|
||||
&child_natural);
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = FALSE;
|
||||
min = child_min;
|
||||
natural = child_natural;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (child_min > min)
|
||||
min = child_min;
|
||||
|
||||
if (child_natural > natural)
|
||||
natural = child_natural;
|
||||
}
|
||||
}
|
||||
|
||||
if (min_width_p)
|
||||
*min_width_p = min;
|
||||
|
||||
if (natural_width_p)
|
||||
*natural_width_p = natural;
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_overflow_list_class_init (ShellOverflowListClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = shell_overflow_list_get_property;
|
||||
gobject_class->set_property = shell_overflow_list_set_property;
|
||||
|
||||
actor_class->get_preferred_width = shell_overflow_list_get_preferred_width;
|
||||
actor_class->get_preferred_height = shell_overflow_list_get_preferred_height;
|
||||
actor_class->allocate = shell_overflow_list_allocate;
|
||||
actor_class->paint = shell_overflow_list_paint;
|
||||
actor_class->pick = shell_overflow_list_pick;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_SPACING,
|
||||
g_param_spec_float ("spacing",
|
||||
"Spacing",
|
||||
"Space between items",
|
||||
0.0, G_MAXFLOAT, 0.0,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_ITEM_HEIGHT,
|
||||
g_param_spec_float ("item-height",
|
||||
"Item height",
|
||||
"Fixed item height value",
|
||||
0.0, G_MAXFLOAT, 0.0,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_DISPLAYED_COUNT,
|
||||
g_param_spec_uint ("displayed-count",
|
||||
"Displayed count",
|
||||
"Number of items displayed on current page",
|
||||
0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_PAGE,
|
||||
g_param_spec_uint ("page",
|
||||
"Page number",
|
||||
"Page number",
|
||||
0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_N_PAGES,
|
||||
g_param_spec_uint ("n-pages",
|
||||
"Number of pages",
|
||||
"Number of pages",
|
||||
0, G_MAXUINT, 0,
|
||||
G_PARAM_READABLE));
|
||||
|
||||
g_type_class_add_private (gobject_class, sizeof (ShellOverflowListPrivate));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
shell_overflow_list_init (ShellOverflowList *self)
|
||||
{
|
||||
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
||||
SHELL_TYPE_OVERFLOW_LIST,
|
||||
ShellOverflowListPrivate);
|
||||
self->priv->n_pages = 1;
|
||||
self->priv->page = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_overflow_list_get_displayed_actor:
|
||||
* @self:
|
||||
* @index: 0-based index for displayed list
|
||||
*
|
||||
* Returns the actor at the given index on the current page.
|
||||
*
|
||||
* Return value: (transfer none): #ClutterActor at index
|
||||
*/
|
||||
ClutterActor *
|
||||
shell_overflow_list_get_displayed_actor (ShellOverflowList *self,
|
||||
guint index)
|
||||
{
|
||||
GList *children, *iter;
|
||||
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
|
||||
|
||||
if (children == NULL)
|
||||
return NULL;
|
||||
|
||||
iter = g_list_nth (children, index + (self->priv->page * self->priv->items_per_page));
|
||||
|
||||
if (!iter)
|
||||
return NULL;
|
||||
|
||||
return iter->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_overflow_list_get_actor_index:
|
||||
* @self:
|
||||
* @actor: a child of the list
|
||||
*
|
||||
* Returns the index on the current page of the given actor.
|
||||
*
|
||||
* Return value: index of @actor in
|
||||
* the currently visible page
|
||||
*/
|
||||
int
|
||||
shell_overflow_list_get_actor_index (ShellOverflowList *self,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
GList *children, *iter;
|
||||
int i;
|
||||
int result;
|
||||
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
|
||||
|
||||
if (children == NULL)
|
||||
return -1;
|
||||
|
||||
iter = g_list_nth (children, (self->priv->page) * self->priv->items_per_page);
|
||||
|
||||
result = -1;
|
||||
for (i = 0; iter; iter = iter->next, i++)
|
||||
if (iter->data == actor)
|
||||
{
|
||||
result = i;
|
||||
break;
|
||||
}
|
||||
|
||||
g_list_free (children);
|
||||
|
||||
return result;
|
||||
}
|
44
src/shell-overflow-list.h
Normal file
44
src/shell-overflow-list.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __SHELL_OVERFLOW_LIST_H__
|
||||
#define __SHELL_OVERFLOW_LIST_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _ShellOverflowList ShellOverflowList;
|
||||
typedef struct _ShellOverflowListClass ShellOverflowListClass;
|
||||
typedef struct _ShellOverflowListPrivate ShellOverflowListPrivate;
|
||||
|
||||
#define SHELL_TYPE_OVERFLOW_LIST (shell_overflow_list_get_type ())
|
||||
#define SHELL_OVERFLOW_LIST(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_OVERFLOW_LIST, ShellOverflowList))
|
||||
#define SHELL_OVERFLOW_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_OVERFLOW_LIST, ShellOverflowListClass))
|
||||
#define SHELL_IS_OVERFLOW_LIST(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_OVERFLOW_LIST))
|
||||
#define SHELL_IS_OVERFLOW_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_OVERFLOW_LIST))
|
||||
#define SHELL_OVERFLOW_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_OVERFLOW_LIST, ShellOverflowListClass))
|
||||
|
||||
struct _ShellOverflowList
|
||||
{
|
||||
ClutterGroup parent_instance;
|
||||
|
||||
ShellOverflowListPrivate *priv;
|
||||
};
|
||||
|
||||
struct _ShellOverflowListClass
|
||||
{
|
||||
ClutterGroupClass parent_class;
|
||||
|
||||
ShellOverflowListPrivate *priv;
|
||||
};
|
||||
|
||||
GType shell_overflow_list_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ClutterActor *shell_overflow_list_get_displayed_actor (ShellOverflowList *list, guint index);
|
||||
int shell_overflow_list_get_actor_index (ShellOverflowList *list, ClutterActor *actor);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_OVERFLOW_LIST_H__ */
|
@ -52,8 +52,6 @@ static void shell_process_init (ShellProcess *self)
|
||||
|
||||
static void shell_process_dispose (GObject *object)
|
||||
{
|
||||
ShellProcess *self = (ShellProcess*)object;
|
||||
|
||||
G_OBJECT_CLASS (shell_process_parent_class)->dispose(object);
|
||||
}
|
||||
|
||||
|
@ -221,7 +221,8 @@ get_memory_target (void)
|
||||
return mem_total / 2;
|
||||
}
|
||||
/* Skip to the next line and discard what we read */
|
||||
fgets(line_buffer, sizeof(line_buffer), f);
|
||||
if (fgets(line_buffer, sizeof(line_buffer), f) == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
@ -232,6 +233,9 @@ get_memory_target (void)
|
||||
static void
|
||||
shell_recorder_init (ShellRecorder *recorder)
|
||||
{
|
||||
/* Calling gst_init() is a no-op if GStreamer was previously initialized */
|
||||
gst_init (NULL, NULL);
|
||||
|
||||
shell_recorder_src_register ();
|
||||
|
||||
recorder->recording_icon = create_recording_icon ();
|
||||
|
150
src/shell-stack.c
Normal file
150
src/shell-stack.c
Normal file
@ -0,0 +1,150 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/**
|
||||
* SECTION:shell-stack
|
||||
* @short_description: Pure "Z-axis" container class
|
||||
*
|
||||
* A #ShellStack draws its children on top of each other,
|
||||
* aligned to the top left. It will be sized in width/height
|
||||
* according to the largest such dimension of its children, and
|
||||
* all children will be allocated that size. This differs
|
||||
* from #ClutterGroup which allocates its children their natural
|
||||
* size, even if that would overflow the size allocated to the stack.
|
||||
*/
|
||||
|
||||
#include "shell-stack.h"
|
||||
|
||||
G_DEFINE_TYPE (ShellStack,
|
||||
shell_stack,
|
||||
CLUTTER_TYPE_GROUP);
|
||||
|
||||
static void
|
||||
shell_stack_allocate (ClutterActor *self,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
GList *children, *iter;
|
||||
|
||||
/* chain up to set actor->allocation */
|
||||
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->allocate (self, box, flags);
|
||||
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (self));
|
||||
for (iter = children; iter; iter = iter->next)
|
||||
{
|
||||
ClutterActor *actor = CLUTTER_ACTOR (iter->data);
|
||||
clutter_actor_allocate (actor, box, flags);
|
||||
}
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_stack_get_preferred_height (ClutterActor *actor,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
ShellStack *stack = SHELL_STACK (actor);
|
||||
gboolean first = TRUE;
|
||||
float min = 0, natural = 0;
|
||||
GList *children;
|
||||
GList *iter;
|
||||
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (stack));
|
||||
|
||||
for (iter = children; iter; iter = iter->next)
|
||||
{
|
||||
ClutterActor *child = iter->data;
|
||||
float child_min, child_natural;
|
||||
|
||||
clutter_actor_get_preferred_height (child,
|
||||
for_width,
|
||||
&child_min,
|
||||
&child_natural);
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = FALSE;
|
||||
min = child_min;
|
||||
natural = child_natural;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (child_min > min)
|
||||
min = child_min;
|
||||
|
||||
if (child_natural > natural)
|
||||
natural = child_natural;
|
||||
}
|
||||
}
|
||||
|
||||
if (min_height_p)
|
||||
*min_height_p = min;
|
||||
|
||||
if (natural_height_p)
|
||||
*natural_height_p = natural;
|
||||
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_stack_get_preferred_width (ClutterActor *actor,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
ShellStack *stack = SHELL_STACK (actor);
|
||||
gboolean first = TRUE;
|
||||
float min = 0, natural = 0;
|
||||
GList *iter;
|
||||
GList *children;
|
||||
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (stack));
|
||||
|
||||
for (iter = children; iter; iter = iter->next)
|
||||
{
|
||||
ClutterActor *child = iter->data;
|
||||
float child_min, child_natural;
|
||||
|
||||
clutter_actor_get_preferred_width (child,
|
||||
for_height,
|
||||
&child_min,
|
||||
&child_natural);
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = FALSE;
|
||||
min = child_min;
|
||||
natural = child_natural;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (child_min > min)
|
||||
min = child_min;
|
||||
|
||||
if (child_natural > natural)
|
||||
natural = child_natural;
|
||||
}
|
||||
}
|
||||
|
||||
if (min_width_p)
|
||||
*min_width_p = min;
|
||||
|
||||
if (natural_width_p)
|
||||
*natural_width_p = natural;
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_stack_class_init (ShellStackClass *klass)
|
||||
{
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
|
||||
actor_class->get_preferred_width = shell_stack_get_preferred_width;
|
||||
actor_class->get_preferred_height = shell_stack_get_preferred_height;
|
||||
actor_class->allocate = shell_stack_allocate;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_stack_init (ShellStack *actor)
|
||||
{
|
||||
}
|
33
src/shell-stack.h
Normal file
33
src/shell-stack.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef __SHELL_STACK_H__
|
||||
#define __SHELL_STACK_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define SHELL_TYPE_STACK (shell_stack_get_type ())
|
||||
#define SHELL_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_STACK, ShellStack))
|
||||
#define SHELL_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_STACK, ShellStackClass))
|
||||
#define SHELL_IS_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_STACK))
|
||||
#define SHELL_IS_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_STACK))
|
||||
#define SHELL_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_STACK, ShellStackClass))
|
||||
|
||||
typedef struct _ShellStack ShellStack;
|
||||
typedef struct _ShellStackClass ShellStackClass;
|
||||
|
||||
typedef struct _ShellStackPrivate ShellStackPrivate;
|
||||
|
||||
struct _ShellStack
|
||||
{
|
||||
ClutterGroup parent;
|
||||
|
||||
ShellStackPrivate *priv;
|
||||
};
|
||||
|
||||
struct _ShellStackClass
|
||||
{
|
||||
ClutterGroupClass parent_class;
|
||||
};
|
||||
|
||||
GType shell_stack_get_type (void) G_GNUC_CONST;
|
||||
|
||||
#endif /* __SHELL_STACK_H__ */
|
@ -39,10 +39,13 @@
|
||||
#include <gdmuser/gdm-user-manager.h>
|
||||
|
||||
#include "shell-global.h"
|
||||
#include "shell-gconf.h"
|
||||
|
||||
#define LOCKDOWN_DIR "/desktop/gnome/lockdown"
|
||||
#define LOCKDOWN_KEY LOCKDOWN_DIR "/disable_user_switching"
|
||||
|
||||
#define SIDEBAR_VISIBLE_KEY SHELL_GCONF_DIR "/sidebar/visible"
|
||||
|
||||
struct _ShellStatusMenuPrivate {
|
||||
GConfClient *client;
|
||||
GdmUserManager *manager;
|
||||
@ -54,10 +57,12 @@ struct _ShellStatusMenuPrivate {
|
||||
|
||||
GtkWidget *menu;
|
||||
GtkWidget *account_item;
|
||||
GtkWidget *sidebar_item;
|
||||
GtkWidget *control_panel_item;
|
||||
GtkWidget *lock_screen_item;
|
||||
GtkWidget *login_screen_item;
|
||||
GtkWidget *quit_session_item;
|
||||
GtkWidget *shut_down_item;
|
||||
|
||||
guint client_notify_lockdown_id;
|
||||
|
||||
@ -113,12 +118,9 @@ static void
|
||||
update_name_text (ShellStatusMenu *status)
|
||||
{
|
||||
ShellStatusMenuPrivate *priv = status->priv;
|
||||
char *markup;
|
||||
|
||||
markup = g_markup_printf_escaped("<b>%s</b>",
|
||||
gdm_user_get_real_name (GDM_USER (priv->user)));
|
||||
clutter_text_set_markup (priv->name, markup);
|
||||
g_free (markup);
|
||||
clutter_text_set_text (priv->name,
|
||||
gdm_user_get_real_name (GDM_USER (priv->user)));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -314,10 +316,18 @@ on_account_activate (GtkMenuItem *item,
|
||||
spawn_external (status, "gnome-about-me");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
on_quit_session_activate (GtkMenuItem *item,
|
||||
ShellStatusMenu *status)
|
||||
on_sidebar_toggled (GtkCheckMenuItem *item,
|
||||
ShellStatusMenu *status)
|
||||
{
|
||||
gconf_client_set_bool (status->priv->client, SIDEBAR_VISIBLE_KEY,
|
||||
gtk_check_menu_item_get_active (item), NULL);
|
||||
}
|
||||
|
||||
|
||||
/* Calls 'gnome-session-save arg' */
|
||||
static void
|
||||
gnome_session_save_command (const char *arg)
|
||||
{
|
||||
char *args[3];
|
||||
GError *error;
|
||||
@ -328,7 +338,7 @@ on_quit_session_activate (GtkMenuItem *item,
|
||||
if (args[0] == NULL)
|
||||
return;
|
||||
|
||||
args[1] = "--logout-dialog";
|
||||
args[1] = (char *)arg;
|
||||
args[2] = NULL;
|
||||
|
||||
screen = gdk_screen_get_default ();
|
||||
@ -345,6 +355,21 @@ on_quit_session_activate (GtkMenuItem *item,
|
||||
g_free (args[0]);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
on_quit_session_activate (GtkMenuItem *item,
|
||||
ShellStatusMenu *status)
|
||||
{
|
||||
gnome_session_save_command ("--logout-dialog");
|
||||
}
|
||||
|
||||
static void
|
||||
on_shut_down_activate (GtkMenuItem *item,
|
||||
ShellStatusMenu *status)
|
||||
{
|
||||
gnome_session_save_command ("--shutdown-dialog");
|
||||
}
|
||||
|
||||
static void
|
||||
update_switch_user (ShellStatusMenu *status)
|
||||
{
|
||||
@ -430,6 +455,8 @@ menuitem_style_set_cb (GtkWidget *menuitem,
|
||||
icon_name = "user-info";
|
||||
else if (menuitem == priv->control_panel_item)
|
||||
icon_name = "preferences-desktop";
|
||||
else if (menuitem == priv->shut_down_item)
|
||||
icon_name = "system-shutdown";
|
||||
else
|
||||
icon_name = GTK_STOCK_MISSING_IMAGE;
|
||||
|
||||
@ -472,6 +499,14 @@ create_sub_menu (ShellStatusMenu *status)
|
||||
G_CALLBACK (on_account_activate), status);
|
||||
gtk_widget_show (priv->account_item);
|
||||
|
||||
priv->sidebar_item = gtk_check_menu_item_new_with_label (_("Sidebar"));
|
||||
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->sidebar_item),
|
||||
gconf_client_get_bool (priv->client, SIDEBAR_VISIBLE_KEY, NULL));
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->sidebar_item);
|
||||
g_signal_connect (priv->sidebar_item, "toggled",
|
||||
G_CALLBACK (on_sidebar_toggled), status);
|
||||
gtk_widget_show (priv->sidebar_item);
|
||||
|
||||
priv->control_panel_item = gtk_image_menu_item_new_with_label (_("System Preferences..."));
|
||||
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->control_panel_item),
|
||||
gtk_image_new ());
|
||||
@ -507,7 +542,8 @@ create_sub_menu (ShellStatusMenu *status)
|
||||
G_CALLBACK (on_login_screen_activate), status);
|
||||
/* Only show switch user if there are other users */
|
||||
|
||||
priv->quit_session_item = gtk_image_menu_item_new_with_label (_("Quit..."));
|
||||
/* Log Out */
|
||||
priv->quit_session_item = gtk_image_menu_item_new_with_label (_("Log Out..."));
|
||||
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->quit_session_item),
|
||||
gtk_image_new ());
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->quit_session_item);
|
||||
@ -517,6 +553,17 @@ create_sub_menu (ShellStatusMenu *status)
|
||||
G_CALLBACK (on_quit_session_activate), status);
|
||||
gtk_widget_show (priv->quit_session_item);
|
||||
|
||||
/* Shut down */
|
||||
priv->shut_down_item = gtk_image_menu_item_new_with_label (_("Shut Down..."));
|
||||
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->shut_down_item),
|
||||
gtk_image_new ());
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->shut_down_item);
|
||||
g_signal_connect (priv->shut_down_item, "style-set",
|
||||
G_CALLBACK (menuitem_style_set_cb), status);
|
||||
g_signal_connect (priv->shut_down_item, "activate",
|
||||
G_CALLBACK (on_shut_down_activate), status);
|
||||
gtk_widget_show (priv->shut_down_item);
|
||||
|
||||
g_signal_connect (G_OBJECT (priv->menu), "deactivate",
|
||||
G_CALLBACK (on_deactivate), status);
|
||||
}
|
||||
@ -588,22 +635,29 @@ shell_status_menu_class_init (ShellStatusMenuClass *klass)
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
ShellStatusMenu *
|
||||
shell_status_menu_new (void)
|
||||
{
|
||||
return g_object_new (SHELL_TYPE_STATUS_MENU, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
position_menu (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data)
|
||||
{
|
||||
ShellStatusMenu *status = SHELL_STATUS_MENU (user_data);
|
||||
ClutterActor *parent;
|
||||
float src_x, src_y;
|
||||
float width, height;
|
||||
int menu_width;
|
||||
|
||||
clutter_actor_get_transformed_position (CLUTTER_ACTOR (status), &src_x, &src_y);
|
||||
gtk_widget_get_size_request (GTK_WIDGET (menu), &menu_width, NULL);
|
||||
|
||||
*x = (gint)(0.5 + src_x);
|
||||
*y = (gint)(0.5 + src_y);
|
||||
/* Encapsulation breakage: it looks better if the menu is
|
||||
* aligned with the bottom of the actor's grandparent - the
|
||||
* panel, rather than with the bottom of the actor. We just
|
||||
* assume what the hierarchy is and where we are positioned
|
||||
* in the panel.
|
||||
*/
|
||||
parent = clutter_actor_get_parent (CLUTTER_ACTOR (status));
|
||||
parent = clutter_actor_get_parent (parent);
|
||||
clutter_actor_get_transformed_position (parent, &src_x, &src_y);
|
||||
clutter_actor_get_transformed_size (parent, &width, &height);
|
||||
*x = (gint)(0.5 + src_x + width - menu_width);
|
||||
*y = (gint)(0.5 + src_y + height);
|
||||
}
|
||||
|
||||
void
|
||||
@ -622,3 +676,27 @@ shell_status_menu_toggle (ShellStatusMenu *status, ClutterEvent *event)
|
||||
status, 1, event->button.time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_status_menu_get_name:
|
||||
* @menu: a #ShellStatusMenu
|
||||
*
|
||||
* Return value: (transfer none): the #ClutterText actor with the user's name.
|
||||
*/
|
||||
ClutterText *
|
||||
shell_status_menu_get_name (ShellStatusMenu *menu)
|
||||
{
|
||||
return menu->priv->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_status_menu_get_icon:
|
||||
* @menu: a #ShellStatusMenu
|
||||
*
|
||||
* Return value: (transfer none): the #ClutterTexture actor with the user icon.
|
||||
*/
|
||||
ClutterTexture *
|
||||
shell_status_menu_get_icon (ShellStatusMenu *menu)
|
||||
{
|
||||
return menu->priv->user_icon;
|
||||
}
|
||||
|
@ -37,6 +37,9 @@ GType shell_status_menu_get_type (void);
|
||||
|
||||
void shell_status_menu_toggle (ShellStatusMenu *menu, ClutterEvent *event);
|
||||
|
||||
ClutterText *shell_status_menu_get_name (ShellStatusMenu *menu);
|
||||
ClutterTexture *shell_status_menu_get_icon (ShellStatusMenu *menu);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_STATUS_MENU_H__ */
|
||||
|
@ -1,16 +1,26 @@
|
||||
#include "shell-texture-cache.h"
|
||||
#include "shell-global.h"
|
||||
#include <gtk/gtk.h>
|
||||
#include <libgnomeui/gnome-thumbnail.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ShellTextureCachePolicy policy;
|
||||
|
||||
/* These are exclusive */
|
||||
GIcon *icon;
|
||||
gchar *uri;
|
||||
gchar *thumbnail_uri;
|
||||
|
||||
/* This one is common to all */
|
||||
guint size;
|
||||
} CacheKey;
|
||||
|
||||
struct _ShellTextureCachePrivate
|
||||
{
|
||||
GHashTable *gicon_cache; /* CacheKey -> CoglTexture* */
|
||||
GHashTable *keyed_cache; /* CacheKey -> CoglTexture* */
|
||||
GnomeThumbnailFactory *thumbnails;
|
||||
};
|
||||
|
||||
static void shell_texture_cache_dispose (GObject *object);
|
||||
@ -22,10 +32,17 @@ static guint
|
||||
cache_key_hash (gconstpointer a)
|
||||
{
|
||||
CacheKey *akey = (CacheKey *)a;
|
||||
guint base_hash;
|
||||
|
||||
if (akey->icon)
|
||||
return g_icon_hash (akey->icon) + 31*akey->size;
|
||||
g_assert_not_reached ();
|
||||
base_hash = g_icon_hash (akey->icon);
|
||||
else if (akey->uri)
|
||||
base_hash = g_str_hash (akey->uri);
|
||||
else if (akey->thumbnail_uri)
|
||||
base_hash = g_str_hash (akey->thumbnail_uri);
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
return base_hash + 31*akey->size;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -35,11 +52,34 @@ cache_key_equal (gconstpointer a,
|
||||
CacheKey *akey = (CacheKey*)a;
|
||||
CacheKey *bkey = (CacheKey*)b;
|
||||
|
||||
/* We don't compare policy here, since we need
|
||||
* a way to look up a cache key without respect to
|
||||
* the policy. */
|
||||
|
||||
if (akey->size != bkey->size)
|
||||
return FALSE;
|
||||
|
||||
if (akey->icon && bkey->icon)
|
||||
return g_icon_equal (akey->icon, bkey->icon);
|
||||
g_assert_not_reached ();
|
||||
else if (akey->uri && bkey->uri)
|
||||
return strcmp (akey->uri, bkey->uri) == 0;
|
||||
else if (akey->thumbnail_uri && bkey->thumbnail_uri)
|
||||
return strcmp (akey->thumbnail_uri, bkey->thumbnail_uri) == 0;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static CacheKey *
|
||||
cache_key_dup (CacheKey *key)
|
||||
{
|
||||
CacheKey *ret = g_new0 (CacheKey, 1);
|
||||
ret->policy = key->policy;
|
||||
if (key->icon)
|
||||
ret->icon = g_object_ref (key->icon);
|
||||
ret->uri = g_strdup (key->uri);
|
||||
ret->thumbnail_uri = g_strdup (key->thumbnail_uri);
|
||||
ret->size = key->size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -48,9 +88,32 @@ cache_key_destroy (gpointer a)
|
||||
CacheKey *akey = (CacheKey*)a;
|
||||
if (akey->icon)
|
||||
g_object_unref (akey->icon);
|
||||
g_free (akey->uri);
|
||||
g_free (akey->thumbnail_uri);
|
||||
g_free (akey);
|
||||
}
|
||||
|
||||
|
||||
/* We want to preserve the aspect ratio by default, also the default
|
||||
* material for an empty texture is full opacity white, which we
|
||||
* definitely don't want. Skip that by setting 0 opacity.
|
||||
*/
|
||||
static ClutterTexture *
|
||||
create_default_texture (ShellTextureCache *self)
|
||||
{
|
||||
ClutterTexture * texture = CLUTTER_TEXTURE (clutter_texture_new ());
|
||||
g_object_set (texture, "keep-aspect-ratio", TRUE, "opacity", 0, NULL);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/* Reverse the opacity we added while loading */
|
||||
static void
|
||||
set_texture_cogl_texture (ClutterTexture *clutter_texture, CoglHandle cogl_texture)
|
||||
{
|
||||
clutter_texture_set_cogl_texture (clutter_texture, cogl_texture);
|
||||
g_object_set (clutter_texture, "opacity", 255, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_texture_cache_class_init (ShellTextureCacheClass *klass)
|
||||
{
|
||||
@ -64,8 +127,9 @@ static void
|
||||
shell_texture_cache_init (ShellTextureCache *self)
|
||||
{
|
||||
self->priv = g_new0 (ShellTextureCachePrivate, 1);
|
||||
self->priv->gicon_cache = g_hash_table_new_full (cache_key_hash, cache_key_equal,
|
||||
self->priv->keyed_cache = g_hash_table_new_full (cache_key_hash, cache_key_equal,
|
||||
cache_key_destroy, cogl_handle_unref);
|
||||
self->priv->thumbnails = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -73,9 +137,13 @@ shell_texture_cache_dispose (GObject *object)
|
||||
{
|
||||
ShellTextureCache *self = (ShellTextureCache*)object;
|
||||
|
||||
if (self->priv->gicon_cache)
|
||||
g_hash_table_destroy (self->priv->gicon_cache);
|
||||
self->priv->gicon_cache = NULL;
|
||||
if (self->priv->keyed_cache)
|
||||
g_hash_table_destroy (self->priv->keyed_cache);
|
||||
self->priv->keyed_cache = NULL;
|
||||
|
||||
if (self->priv->thumbnails)
|
||||
g_object_unref (self->priv->thumbnails);
|
||||
self->priv->thumbnails = NULL;
|
||||
|
||||
G_OBJECT_CLASS (shell_texture_cache_parent_class)->dispose (object);
|
||||
}
|
||||
@ -86,23 +154,19 @@ shell_texture_cache_finalize (GObject *object)
|
||||
G_OBJECT_CLASS (shell_texture_cache_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
ShellTextureCache*
|
||||
shell_texture_cache_new ()
|
||||
{
|
||||
return SHELL_TEXTURE_CACHE (g_object_new (SHELL_TYPE_TEXTURE_CACHE,
|
||||
NULL));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ShellTextureCache *cache;
|
||||
char *uri;
|
||||
char *mimetype;
|
||||
gboolean thumbnail;
|
||||
GIcon *icon;
|
||||
GtkRecentInfo *recent_info;
|
||||
GtkIconInfo *icon_info;
|
||||
gint width;
|
||||
gint height;
|
||||
gpointer user_data;
|
||||
} AsyncIconLookupData;
|
||||
|
||||
|
||||
static gboolean
|
||||
compute_pixbuf_scale (gint width,
|
||||
gint height,
|
||||
@ -200,6 +264,10 @@ icon_lookup_data_destroy (gpointer p)
|
||||
}
|
||||
else if (data->uri)
|
||||
g_free (data->uri);
|
||||
if (data->mimetype)
|
||||
g_free (data->mimetype);
|
||||
if (data->recent_info)
|
||||
gtk_recent_info_unref (data->recent_info);
|
||||
|
||||
g_free (data);
|
||||
}
|
||||
@ -328,6 +396,71 @@ out:
|
||||
return rotated_pixbuf;
|
||||
}
|
||||
|
||||
static GdkPixbuf *
|
||||
impl_load_thumbnail (ShellTextureCache *cache,
|
||||
const char *uri,
|
||||
const char *mime_type,
|
||||
guint size,
|
||||
GError **error)
|
||||
{
|
||||
GnomeThumbnailFactory *thumbnail_factory;
|
||||
GdkPixbuf *pixbuf = NULL;
|
||||
GFile *file;
|
||||
GFileInfo *file_info;
|
||||
GTimeVal mtime_g;
|
||||
time_t mtime = 0;
|
||||
char *existing_thumbnail;
|
||||
|
||||
file = g_file_new_for_uri (uri);
|
||||
file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
|
||||
g_object_unref (file);
|
||||
if (file_info)
|
||||
{
|
||||
g_file_info_get_modification_time (file_info, &mtime_g);
|
||||
g_object_unref (file_info);
|
||||
mtime = (time_t) mtime_g.tv_sec;
|
||||
}
|
||||
|
||||
thumbnail_factory = cache->priv->thumbnails;
|
||||
|
||||
existing_thumbnail = gnome_thumbnail_factory_lookup (thumbnail_factory, uri, mtime);
|
||||
|
||||
if (existing_thumbnail != NULL)
|
||||
pixbuf = gdk_pixbuf_new_from_file_at_size (existing_thumbnail, size, size, error);
|
||||
else if (gnome_thumbnail_factory_has_valid_failed_thumbnail (thumbnail_factory, uri, mtime))
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Has failed thumbnail");
|
||||
else if (gnome_thumbnail_factory_can_thumbnail (thumbnail_factory, uri, mime_type, mtime))
|
||||
{
|
||||
pixbuf = gnome_thumbnail_factory_generate_thumbnail (thumbnail_factory, uri, mime_type);
|
||||
if (pixbuf)
|
||||
{
|
||||
// we need to save the thumbnail so that we don't need to generate it again in the future
|
||||
gnome_thumbnail_factory_save_thumbnail (thumbnail_factory, pixbuf, uri, mtime);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to generate thumbnail");
|
||||
gnome_thumbnail_factory_create_failed_thumbnail (thumbnail_factory, uri, mtime);
|
||||
}
|
||||
}
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
static GIcon *
|
||||
icon_for_mimetype (const char *mimetype)
|
||||
{
|
||||
char *content_type;
|
||||
GIcon *icon;
|
||||
|
||||
content_type = g_content_type_from_mime_type (mimetype);
|
||||
if (!content_type)
|
||||
return NULL;
|
||||
|
||||
icon = g_content_type_get_icon (content_type);
|
||||
g_free (content_type);
|
||||
return icon;
|
||||
}
|
||||
|
||||
static void
|
||||
load_pixbuf_thread (GSimpleAsyncResult *result,
|
||||
GObject *object,
|
||||
@ -337,9 +470,27 @@ load_pixbuf_thread (GSimpleAsyncResult *result,
|
||||
AsyncIconLookupData *data;
|
||||
GError *error = NULL;
|
||||
|
||||
data = g_object_get_data (G_OBJECT (result), "load_icon_pixbuf_async");
|
||||
data = g_object_get_data (G_OBJECT (result), "load_pixbuf_async");
|
||||
g_assert (data != NULL);
|
||||
|
||||
if (data->uri)
|
||||
if (data->thumbnail)
|
||||
{
|
||||
const char *uri;
|
||||
const char *mimetype;
|
||||
|
||||
if (data->recent_info)
|
||||
{
|
||||
uri = gtk_recent_info_get_uri (data->recent_info);
|
||||
mimetype = gtk_recent_info_get_mime_type (data->recent_info);
|
||||
}
|
||||
else
|
||||
{
|
||||
uri = data->uri;
|
||||
mimetype = data->mimetype;
|
||||
}
|
||||
pixbuf = impl_load_thumbnail (data->cache, uri, mimetype, data->width, &error);
|
||||
}
|
||||
else if (data->uri)
|
||||
pixbuf = impl_load_pixbuf_file (data->uri, data->width, data->height, &error);
|
||||
else if (data->icon)
|
||||
pixbuf = impl_load_pixbuf_gicon (data->icon, data->icon_info, data->width, &error);
|
||||
@ -352,8 +503,9 @@ load_pixbuf_thread (GSimpleAsyncResult *result,
|
||||
return;
|
||||
}
|
||||
|
||||
g_simple_async_result_set_op_res_gpointer (result, g_object_ref (pixbuf),
|
||||
g_object_unref);
|
||||
if (pixbuf)
|
||||
g_simple_async_result_set_op_res_gpointer (result, g_object_ref (pixbuf),
|
||||
g_object_unref);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -375,6 +527,7 @@ load_icon_pixbuf_async (ShellTextureCache *cache,
|
||||
AsyncIconLookupData *data;
|
||||
|
||||
data = g_new0 (AsyncIconLookupData, 1);
|
||||
data->cache = cache;
|
||||
data->icon = g_object_ref (icon);
|
||||
data->icon_info = gtk_icon_info_copy (icon_info);
|
||||
data->width = data->height = size;
|
||||
@ -382,7 +535,7 @@ load_icon_pixbuf_async (ShellTextureCache *cache,
|
||||
|
||||
result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_icon_pixbuf_async);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (result), "load_icon_pixbuf_async", data, icon_lookup_data_destroy);
|
||||
g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
|
||||
g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
|
||||
|
||||
g_object_unref (result);
|
||||
@ -401,6 +554,7 @@ load_uri_pixbuf_async (ShellTextureCache *cache,
|
||||
AsyncIconLookupData *data;
|
||||
|
||||
data = g_new0 (AsyncIconLookupData, 1);
|
||||
data->cache = cache;
|
||||
data->uri = g_strdup (uri);
|
||||
data->width = width;
|
||||
data->height = height;
|
||||
@ -408,7 +562,63 @@ load_uri_pixbuf_async (ShellTextureCache *cache,
|
||||
|
||||
result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_uri_pixbuf_async);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (result), "load_uri_pixbuf_async", data, icon_lookup_data_destroy);
|
||||
g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
|
||||
g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
|
||||
|
||||
g_object_unref (result);
|
||||
}
|
||||
|
||||
static void
|
||||
load_thumbnail_async (ShellTextureCache *cache,
|
||||
const char *uri,
|
||||
const char *mimetype,
|
||||
guint size,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GSimpleAsyncResult *result;
|
||||
AsyncIconLookupData *data;
|
||||
|
||||
data = g_new0 (AsyncIconLookupData, 1);
|
||||
data->cache = cache;
|
||||
data->uri = g_strdup (uri);
|
||||
data->mimetype = g_strdup (mimetype);
|
||||
data->thumbnail = TRUE;
|
||||
data->width = size;
|
||||
data->height = size;
|
||||
data->user_data = user_data;
|
||||
|
||||
result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_thumbnail_async);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
|
||||
g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
|
||||
|
||||
g_object_unref (result);
|
||||
}
|
||||
|
||||
static void
|
||||
load_recent_thumbnail_async (ShellTextureCache *cache,
|
||||
GtkRecentInfo *info,
|
||||
guint size,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GSimpleAsyncResult *result;
|
||||
AsyncIconLookupData *data;
|
||||
|
||||
data = g_new0 (AsyncIconLookupData, 1);
|
||||
data->cache = cache;
|
||||
data->thumbnail = TRUE;
|
||||
data->recent_info = gtk_recent_info_ref (info);
|
||||
data->width = size;
|
||||
data->height = size;
|
||||
data->user_data = user_data;
|
||||
|
||||
result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_recent_thumbnail_async);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
|
||||
g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
|
||||
|
||||
g_object_unref (result);
|
||||
@ -424,7 +634,11 @@ load_pixbuf_async_finish (ShellTextureCache *cache, GAsyncResult *result, GError
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ShellTextureCachePolicy policy;
|
||||
char *uri;
|
||||
gboolean thumbnail;
|
||||
char *mimetype;
|
||||
GtkRecentInfo *recent_info;
|
||||
GIcon *icon;
|
||||
GtkIconInfo *icon_info;
|
||||
guint width;
|
||||
@ -444,6 +658,45 @@ pixbuf_to_cogl_handle (GdkPixbuf *pixbuf)
|
||||
gdk_pixbuf_get_pixels (pixbuf));
|
||||
}
|
||||
|
||||
static GdkPixbuf *
|
||||
load_pixbuf_fallback(AsyncTextureLoadData *data)
|
||||
{
|
||||
GdkPixbuf *pixbuf = NULL;
|
||||
|
||||
if (data->thumbnail)
|
||||
{
|
||||
|
||||
GtkIconTheme *theme = gtk_icon_theme_get_default ();
|
||||
|
||||
if (data->recent_info)
|
||||
pixbuf = gtk_recent_info_get_icon (data->recent_info, data->width);
|
||||
else
|
||||
{
|
||||
GIcon *icon = icon_for_mimetype (data->mimetype);
|
||||
if (icon != NULL)
|
||||
{
|
||||
GtkIconInfo *icon_info = gtk_icon_theme_lookup_by_gicon (theme,
|
||||
icon,
|
||||
data->width,
|
||||
GTK_ICON_LOOKUP_USE_BUILTIN);
|
||||
g_object_unref (icon);
|
||||
if (icon_info != NULL)
|
||||
pixbuf = gtk_icon_info_load_icon (icon_info, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (pixbuf == NULL)
|
||||
pixbuf = gtk_icon_theme_load_icon (theme,
|
||||
"gtk-file",
|
||||
data->width,
|
||||
GTK_ICON_LOOKUP_USE_BUILTIN,
|
||||
NULL);
|
||||
}
|
||||
/* Maybe we could need a fallback for outher image types? */
|
||||
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
static void
|
||||
on_pixbuf_loaded (GObject *source,
|
||||
GAsyncResult *result,
|
||||
@ -460,32 +713,39 @@ on_pixbuf_loaded (GObject *source,
|
||||
cache = SHELL_TEXTURE_CACHE (source);
|
||||
pixbuf = load_pixbuf_async_finish (cache, result, &error);
|
||||
if (pixbuf == NULL)
|
||||
{
|
||||
/* TODO - we need a "broken image" display of some sort */
|
||||
goto out;
|
||||
}
|
||||
pixbuf = load_pixbuf_fallback(data);
|
||||
if (pixbuf == NULL)
|
||||
goto out;
|
||||
|
||||
texdata = pixbuf_to_cogl_handle (pixbuf);
|
||||
|
||||
g_object_unref (pixbuf);
|
||||
|
||||
if (data->icon)
|
||||
if (data->policy != SHELL_TEXTURE_CACHE_POLICY_NONE)
|
||||
{
|
||||
gpointer orig_key, value;
|
||||
|
||||
key = g_new0 (CacheKey, 1);
|
||||
key->icon = g_object_ref (data->icon);
|
||||
key->policy = data->policy;
|
||||
if (data->icon)
|
||||
key->icon = g_object_ref (data->icon);
|
||||
else if (data->recent_info && data->thumbnail)
|
||||
key->thumbnail_uri = g_strdup (gtk_recent_info_get_uri (data->recent_info));
|
||||
else if (data->thumbnail)
|
||||
key->thumbnail_uri = g_strdup (data->uri);
|
||||
else if (data->uri)
|
||||
key->uri = g_strdup (data->uri);
|
||||
key->size = data->width;
|
||||
|
||||
if (!g_hash_table_lookup_extended (cache->priv->gicon_cache, key,
|
||||
if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, key,
|
||||
&orig_key, &value))
|
||||
g_hash_table_insert (cache->priv->gicon_cache, key,
|
||||
g_hash_table_insert (cache->priv->keyed_cache, key,
|
||||
texdata);
|
||||
else
|
||||
cache_key_destroy (key);
|
||||
}
|
||||
|
||||
clutter_texture_set_cogl_texture (data->texture, texdata);
|
||||
set_texture_cogl_texture (data->texture, texdata);
|
||||
|
||||
out:
|
||||
if (data->icon)
|
||||
@ -495,10 +755,17 @@ out:
|
||||
}
|
||||
else if (data->uri)
|
||||
g_free (data->uri);
|
||||
|
||||
if (data->recent_info)
|
||||
gtk_recent_info_unref (data->recent_info);
|
||||
if (data->mimetype)
|
||||
g_free (data->mimetype);
|
||||
|
||||
/* Alternatively we could weakref and just do nothing if the texture
|
||||
is destroyed */
|
||||
g_object_unref (data->texture);
|
||||
|
||||
g_clear_error (&error);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
@ -519,12 +786,13 @@ shell_texture_cache_load_gicon (ShellTextureCache *cache,
|
||||
CoglHandle texdata;
|
||||
CacheKey key;
|
||||
|
||||
texture = CLUTTER_TEXTURE (clutter_texture_new ());
|
||||
texture = create_default_texture (cache);
|
||||
clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
|
||||
|
||||
memset (&key, 0, sizeof(key));
|
||||
key.icon = icon;
|
||||
key.size = size;
|
||||
texdata = g_hash_table_lookup (cache->priv->gicon_cache, &key);
|
||||
texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
|
||||
|
||||
if (texdata == NULL)
|
||||
{
|
||||
@ -539,7 +807,9 @@ shell_texture_cache_load_gicon (ShellTextureCache *cache,
|
||||
{
|
||||
AsyncTextureLoadData *data;
|
||||
data = g_new0 (AsyncTextureLoadData, 1);
|
||||
|
||||
/* hardcoded here for now; we should actually blow this away on
|
||||
* icon theme changes probably */
|
||||
data->policy = SHELL_TEXTURE_CACHE_POLICY_FOREVER;
|
||||
data->icon = g_object_ref (icon);
|
||||
data->icon_info = info;
|
||||
data->texture = g_object_ref (texture);
|
||||
@ -549,14 +819,39 @@ shell_texture_cache_load_gicon (ShellTextureCache *cache,
|
||||
}
|
||||
else
|
||||
{
|
||||
clutter_texture_set_cogl_texture (texture, texdata);
|
||||
set_texture_cogl_texture (texture, texdata);
|
||||
}
|
||||
|
||||
return CLUTTER_ACTOR (texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_texture_cache_load_uri:
|
||||
* shell_texture_cache_load_icon_name:
|
||||
* @cache: The texture cache instance
|
||||
* @name: Name of a themed icon
|
||||
* @size: Size of themed
|
||||
*
|
||||
* Load a themed icon into a texture.
|
||||
*
|
||||
* Return Value: (transfer none): A new #ClutterTexture for the icon
|
||||
*/
|
||||
ClutterActor *
|
||||
shell_texture_cache_load_icon_name (ShellTextureCache *cache,
|
||||
const char *name,
|
||||
gint size)
|
||||
{
|
||||
ClutterActor *texture;
|
||||
GIcon *themed;
|
||||
|
||||
themed = g_themed_icon_new (name);
|
||||
texture = shell_texture_cache_load_gicon (cache, themed, size);
|
||||
g_object_unref (themed);
|
||||
|
||||
return CLUTTER_ACTOR (texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_texture_cache_load_uri_async:
|
||||
*
|
||||
* @cache: The texture cache instance
|
||||
* @uri: uri of the image file from which to create a pixbuf
|
||||
@ -578,9 +873,10 @@ shell_texture_cache_load_uri_async (ShellTextureCache *cache,
|
||||
ClutterTexture *texture;
|
||||
AsyncTextureLoadData *data;
|
||||
|
||||
texture = CLUTTER_TEXTURE (clutter_texture_new ());
|
||||
texture = create_default_texture (cache);
|
||||
|
||||
data = g_new0 (AsyncTextureLoadData, 1);
|
||||
data->policy = SHELL_TEXTURE_CACHE_POLICY_NONE;
|
||||
data->uri = g_strdup (uri);
|
||||
data->width = available_width;
|
||||
data->height = available_height;
|
||||
@ -594,6 +890,7 @@ shell_texture_cache_load_uri_async (ShellTextureCache *cache,
|
||||
* shell_texture_cache_load_uri_sync:
|
||||
*
|
||||
* @cache: The texture cache instance
|
||||
* @policy: Requested lifecycle of cached data
|
||||
* @uri: uri of the image file from which to create a pixbuf
|
||||
* @available_width: available width for the image, can be -1 if not limited
|
||||
* @available_height: available height for the image, can be -1 if not limited
|
||||
@ -609,27 +906,236 @@ shell_texture_cache_load_uri_async (ShellTextureCache *cache,
|
||||
*/
|
||||
ClutterActor *
|
||||
shell_texture_cache_load_uri_sync (ShellTextureCache *cache,
|
||||
ShellTextureCachePolicy policy,
|
||||
const gchar *uri,
|
||||
int available_width,
|
||||
int available_height,
|
||||
GError **error)
|
||||
{
|
||||
ClutterTexture *texture;
|
||||
GdkPixbuf *pixbuf;
|
||||
CoglHandle texdata;
|
||||
GdkPixbuf *pixbuf;
|
||||
CacheKey key;
|
||||
|
||||
pixbuf = impl_load_pixbuf_file (uri, available_width, available_height, error);
|
||||
if (!pixbuf)
|
||||
return NULL;
|
||||
texture = create_default_texture (cache);
|
||||
|
||||
texture = CLUTTER_TEXTURE (clutter_texture_new ());
|
||||
texdata = pixbuf_to_cogl_handle (pixbuf);
|
||||
g_object_unref (pixbuf);
|
||||
clutter_texture_set_cogl_texture (texture, texdata);
|
||||
memset (&key, 0, sizeof (CacheKey));
|
||||
key.policy = policy;
|
||||
key.uri = (char*)uri;
|
||||
key.size = available_width;
|
||||
texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
|
||||
|
||||
if (texdata == NULL)
|
||||
{
|
||||
pixbuf = impl_load_pixbuf_file (uri, available_width, available_height, error);
|
||||
if (!pixbuf)
|
||||
{
|
||||
g_object_unref (texture);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
texdata = pixbuf_to_cogl_handle (pixbuf);
|
||||
g_object_unref (pixbuf);
|
||||
|
||||
set_texture_cogl_texture (texture, texdata);
|
||||
|
||||
if (policy == SHELL_TEXTURE_CACHE_POLICY_FOREVER)
|
||||
{
|
||||
g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key), texdata);
|
||||
}
|
||||
else
|
||||
cogl_handle_unref (texdata);
|
||||
}
|
||||
else
|
||||
set_texture_cogl_texture (texture, texdata);
|
||||
|
||||
return CLUTTER_ACTOR (texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_texture_cache_load_thumbnail:
|
||||
* @cache:
|
||||
* @size: Size in pixels to use for thumbnail
|
||||
* @uri: Source URI
|
||||
* @mimetype: Source mime type
|
||||
*
|
||||
* Asynchronously load a thumbnail image of a URI into a texture. The
|
||||
* returned texture object will be a new instance; however, its texture data
|
||||
* may be shared with other objects. This implies the texture data is cached.
|
||||
*
|
||||
* The current caching policy is permanent; to uncache, you must explicitly
|
||||
* call shell_texture_cache_unref_thumbnail().
|
||||
*
|
||||
* Returns: (transfer none): A new #ClutterActor
|
||||
*/
|
||||
ClutterActor *
|
||||
shell_texture_cache_load_thumbnail (ShellTextureCache *cache,
|
||||
int size,
|
||||
const char *uri,
|
||||
const char *mimetype)
|
||||
{
|
||||
ClutterTexture *texture;
|
||||
AsyncTextureLoadData *data;
|
||||
CacheKey key;
|
||||
CoglHandle texdata;
|
||||
|
||||
/* Don't attempt to load thumbnails for non-local URIs */
|
||||
if (!g_str_has_prefix (uri, "file://"))
|
||||
{
|
||||
GIcon *icon = icon_for_mimetype (mimetype);
|
||||
return shell_texture_cache_load_gicon (cache, icon, size);
|
||||
}
|
||||
|
||||
texture = create_default_texture (cache);
|
||||
clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
|
||||
|
||||
memset (&key, 0, sizeof(key));
|
||||
key.size = size;
|
||||
key.thumbnail_uri = (char*)uri;
|
||||
|
||||
texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
|
||||
if (!texdata)
|
||||
{
|
||||
data = g_new0 (AsyncTextureLoadData, 1);
|
||||
data->policy = SHELL_TEXTURE_CACHE_POLICY_FOREVER;
|
||||
data->uri = g_strdup (uri);
|
||||
data->mimetype = g_strdup (mimetype);
|
||||
data->thumbnail = TRUE;
|
||||
data->width = size;
|
||||
data->height = size;
|
||||
data->texture = g_object_ref (texture);
|
||||
load_thumbnail_async (cache, uri, mimetype, size, NULL, on_pixbuf_loaded, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_texture_cogl_texture (texture, texdata);
|
||||
}
|
||||
|
||||
return CLUTTER_ACTOR (texture);
|
||||
}
|
||||
|
||||
static GIcon *
|
||||
icon_for_recent (GtkRecentInfo *info)
|
||||
{
|
||||
const char *mimetype;
|
||||
|
||||
mimetype = gtk_recent_info_get_mime_type (info);
|
||||
if (!mimetype)
|
||||
{
|
||||
return g_themed_icon_new (GTK_STOCK_FILE);
|
||||
}
|
||||
|
||||
return icon_for_mimetype (mimetype);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_texture_cache_load_recent_thumbnail:
|
||||
* @cache:
|
||||
* @size: Size in pixels to use for thumbnail
|
||||
* @info: Recent item info
|
||||
*
|
||||
* Asynchronously load a thumbnail image of a #GtkRecentInfo into a texture. The
|
||||
* returned texture object will be a new instance; however, its texture data
|
||||
* may be shared with other objects. This implies the texture data is cached.
|
||||
*
|
||||
* The current caching policy is permanent; to uncache, you must explicitly
|
||||
* call shell_texture_cache_unref_recent_thumbnail().
|
||||
*
|
||||
* Returns: (transfer none): A new #ClutterActor
|
||||
*/
|
||||
ClutterActor *
|
||||
shell_texture_cache_load_recent_thumbnail (ShellTextureCache *cache,
|
||||
int size,
|
||||
GtkRecentInfo *info)
|
||||
{
|
||||
ClutterTexture *texture;
|
||||
AsyncTextureLoadData *data;
|
||||
CacheKey key;
|
||||
CoglHandle texdata;
|
||||
const char *uri;
|
||||
|
||||
uri = gtk_recent_info_get_uri (info);
|
||||
|
||||
/* Don't attempt to load thumbnails for non-local URIs */
|
||||
if (!g_str_has_prefix (uri, "file://"))
|
||||
{
|
||||
GIcon *icon = icon_for_recent (info);
|
||||
return shell_texture_cache_load_gicon (cache, icon, size);
|
||||
}
|
||||
|
||||
texture = CLUTTER_TEXTURE (clutter_texture_new ());
|
||||
clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
|
||||
|
||||
memset (&key, 0, sizeof(key));
|
||||
key.size = size;
|
||||
key.thumbnail_uri = (char*)gtk_recent_info_get_uri (info);
|
||||
|
||||
texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
|
||||
if (!texdata)
|
||||
{
|
||||
data = g_new0 (AsyncTextureLoadData, 1);
|
||||
data->policy = SHELL_TEXTURE_CACHE_POLICY_FOREVER;
|
||||
data->thumbnail = TRUE;
|
||||
data->recent_info = gtk_recent_info_ref (info);
|
||||
data->width = size;
|
||||
data->height = size;
|
||||
data->texture = g_object_ref (texture);
|
||||
load_recent_thumbnail_async (cache, info, size, NULL, on_pixbuf_loaded, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_texture_cogl_texture (texture, texdata);
|
||||
}
|
||||
|
||||
return CLUTTER_ACTOR (texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_texture_cache_evict_thumbnail:
|
||||
* @cache:
|
||||
* @size: Size in pixels
|
||||
* @uri: Source URI
|
||||
*
|
||||
* Removes the reference the shell_texture_cache_load_thumbnail() function
|
||||
* created for a thumbnail.
|
||||
*/
|
||||
void
|
||||
shell_texture_cache_evict_thumbnail (ShellTextureCache *cache,
|
||||
int size,
|
||||
const char *uri)
|
||||
{
|
||||
CacheKey key;
|
||||
|
||||
memset (&key, 0, sizeof(key));
|
||||
key.size = size;
|
||||
key.thumbnail_uri = (char*)uri;
|
||||
|
||||
g_hash_table_remove (cache->priv->keyed_cache, &key);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_texture_cache_evict_recent_thumbnail:
|
||||
* @cache:
|
||||
* @size: Size in pixels
|
||||
* @info: A recent info
|
||||
*
|
||||
* Removes the reference the shell_texture_cache_load_recent_thumbnail() function
|
||||
* created for a thumbnail.
|
||||
*/
|
||||
void
|
||||
shell_texture_cache_evict_recent_thumbnail (ShellTextureCache *cache,
|
||||
int size,
|
||||
GtkRecentInfo *info)
|
||||
{
|
||||
CacheKey key;
|
||||
|
||||
memset (&key, 0, sizeof(key));
|
||||
key.size = size;
|
||||
key.thumbnail_uri = (char*)gtk_recent_info_get_uri (info);
|
||||
|
||||
g_hash_table_remove (cache->priv->keyed_cache, &key);
|
||||
}
|
||||
|
||||
static ShellTextureCache *instance = NULL;
|
||||
|
||||
/**
|
||||
@ -638,7 +1144,7 @@ static ShellTextureCache *instance = NULL;
|
||||
* Return value: (transfer none): The global texture cache
|
||||
*/
|
||||
ShellTextureCache*
|
||||
shell_texture_cache_get_default ()
|
||||
shell_texture_cache_get_default (void)
|
||||
{
|
||||
if (instance == NULL)
|
||||
instance = g_object_new (SHELL_TYPE_TEXTURE_CACHE, NULL);
|
||||
|
@ -2,6 +2,8 @@
|
||||
#define __SHELL_TEXTURE_CACHE_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#define SHELL_TYPE_TEXTURE_CACHE (shell_texture_cache_get_type ())
|
||||
@ -29,20 +31,47 @@ struct _ShellTextureCacheClass
|
||||
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SHELL_TEXTURE_CACHE_POLICY_NONE,
|
||||
SHELL_TEXTURE_CACHE_POLICY_FOREVER
|
||||
} ShellTextureCachePolicy;
|
||||
|
||||
GType shell_texture_cache_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ShellTextureCache* shell_texture_cache_get_default();
|
||||
ShellTextureCache* shell_texture_cache_get_default (void);
|
||||
|
||||
ClutterActor *shell_texture_cache_load_icon_name (ShellTextureCache *cache,
|
||||
const char *name,
|
||||
gint size);
|
||||
|
||||
ClutterActor *shell_texture_cache_load_gicon (ShellTextureCache *cache,
|
||||
GIcon *icon,
|
||||
gint size);
|
||||
|
||||
ClutterActor *shell_texture_cache_load_thumbnail (ShellTextureCache *cache,
|
||||
int size,
|
||||
const char *uri,
|
||||
const char *mimetype);
|
||||
|
||||
ClutterActor *shell_texture_cache_load_recent_thumbnail (ShellTextureCache *cache,
|
||||
int size,
|
||||
GtkRecentInfo *info);
|
||||
|
||||
void shell_texture_cache_evict_thumbnail (ShellTextureCache *cache,
|
||||
int size,
|
||||
const char *uri);
|
||||
|
||||
void shell_texture_cache_evict_recent_thumbnail (ShellTextureCache *cache,
|
||||
int size,
|
||||
GtkRecentInfo *info);
|
||||
|
||||
ClutterActor *shell_texture_cache_load_uri_async (ShellTextureCache *cache,
|
||||
const gchar *filename,
|
||||
int available_width,
|
||||
int available_height);
|
||||
|
||||
ClutterActor *shell_texture_cache_load_uri_sync (ShellTextureCache *cache,
|
||||
ShellTextureCachePolicy policy,
|
||||
const gchar *filename,
|
||||
int available_width,
|
||||
int available_height,
|
||||
|
383
src/shell-uri-util.c
Normal file
383
src/shell-uri-util.c
Normal file
@ -0,0 +1,383 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include "shell-uri-util.h"
|
||||
#include <glib/gi18n.h>
|
||||
#include <gconf/gconf-client.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* The code in this file adapted under the GPLv2+ from:
|
||||
*
|
||||
* GNOME panel utils: gnome-panel/gnome-panel/panel-util.c
|
||||
* (C) 1997, 1998, 1999, 2000 The Free Software Foundation
|
||||
* Copyright 2000 Helix Code, Inc.
|
||||
* Copyright 2000,2001 Eazel, Inc.
|
||||
* Copyright 2001 George Lebl
|
||||
* Copyright 2002 Sun Microsystems Inc.
|
||||
*
|
||||
* Authors: George Lebl
|
||||
* Jacob Berkman
|
||||
* Mark McLoughlin
|
||||
*/
|
||||
|
||||
static GFile *
|
||||
shell_util_get_gfile_root (GFile *file)
|
||||
{
|
||||
GFile *parent;
|
||||
GFile *parent_old;
|
||||
|
||||
/* search for the root on the URI */
|
||||
parent_old = g_object_ref (file);
|
||||
parent = g_file_get_parent (file);
|
||||
while (parent != NULL)
|
||||
{
|
||||
g_object_unref (parent_old);
|
||||
parent_old = parent;
|
||||
parent = g_file_get_parent (parent);
|
||||
}
|
||||
|
||||
return parent_old;
|
||||
}
|
||||
|
||||
static char *
|
||||
shell_util_get_file_display_name_if_mount (GFile *file)
|
||||
{
|
||||
GFile *compare;
|
||||
GVolumeMonitor *monitor;
|
||||
GList *mounts, *l;
|
||||
char *ret;
|
||||
|
||||
ret = NULL;
|
||||
|
||||
/* compare with all mounts */
|
||||
monitor = g_volume_monitor_get ();
|
||||
mounts = g_volume_monitor_get_mounts (monitor);
|
||||
for (l = mounts; l != NULL; l = l->next)
|
||||
{
|
||||
GMount *mount;
|
||||
mount = G_MOUNT(l->data);
|
||||
compare = g_mount_get_root (mount);
|
||||
if (!ret && g_file_equal (file, compare))
|
||||
ret = g_mount_get_name (mount);
|
||||
g_object_unref (mount);
|
||||
}
|
||||
g_list_free (mounts);
|
||||
g_object_unref (monitor);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define HOME_NAME_KEY "/apps/nautilus/desktop/home_icon_name"
|
||||
static char *
|
||||
shell_util_get_file_display_for_common_files (GFile *file)
|
||||
{
|
||||
GFile *compare;
|
||||
|
||||
compare = g_file_new_for_path (g_get_home_dir ());
|
||||
if (g_file_equal (file, compare))
|
||||
{
|
||||
char *gconf_name;
|
||||
|
||||
g_object_unref (compare);
|
||||
|
||||
gconf_name = gconf_client_get_string (gconf_client_get_default (),
|
||||
HOME_NAME_KEY, NULL);
|
||||
if (!(gconf_name && gconf_name[0]))
|
||||
{
|
||||
g_free (gconf_name);
|
||||
return g_strdup (_("Home Folder"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return gconf_name;
|
||||
}
|
||||
}
|
||||
g_object_unref (compare);
|
||||
|
||||
compare = g_file_new_for_path ("/");
|
||||
if (g_file_equal (file, compare))
|
||||
{
|
||||
g_object_unref (compare);
|
||||
/* Translators: this is the same string as the one found in
|
||||
* nautilus */
|
||||
return g_strdup (_("File System"));
|
||||
}
|
||||
g_object_unref (compare);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *
|
||||
shell_util_get_file_description (GFile *file)
|
||||
{
|
||||
GFileInfo *info;
|
||||
char *ret;
|
||||
|
||||
ret = NULL;
|
||||
|
||||
info = g_file_query_info (file, "standard::description",
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
|
||||
|
||||
if (info)
|
||||
{
|
||||
ret = g_strdup (g_file_info_get_attribute_string(info,
|
||||
G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION));
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
shell_util_get_file_display_name (GFile *file, gboolean use_fallback)
|
||||
{
|
||||
GFileInfo *info;
|
||||
char *ret;
|
||||
|
||||
ret = NULL;
|
||||
|
||||
info = g_file_query_info (file, "standard::display-name",
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
|
||||
|
||||
if (info)
|
||||
{
|
||||
ret = g_strdup (g_file_info_get_display_name (info));
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
if (!ret && use_fallback)
|
||||
{
|
||||
/* can happen with URI schemes non supported by gvfs */
|
||||
char *basename;
|
||||
|
||||
basename = g_file_get_basename (file);
|
||||
ret = g_filename_display_name (basename);
|
||||
g_free (basename);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GIcon *
|
||||
shell_util_get_file_icon_if_mount (GFile *file)
|
||||
{
|
||||
GFile *compare;
|
||||
GVolumeMonitor *monitor;
|
||||
GList *mounts, *l;
|
||||
GIcon *ret;
|
||||
|
||||
ret = NULL;
|
||||
|
||||
/* compare with all mounts */
|
||||
monitor = g_volume_monitor_get ();
|
||||
mounts = g_volume_monitor_get_mounts (monitor);
|
||||
for (l = mounts; l != NULL; l = l->next)
|
||||
{
|
||||
GMount *mount;
|
||||
mount = G_MOUNT (l->data);
|
||||
compare = g_mount_get_root (mount);
|
||||
if (!ret && g_file_equal (file, compare))
|
||||
{
|
||||
ret = g_mount_get_icon (mount);
|
||||
}
|
||||
g_object_unref (mount);
|
||||
}
|
||||
g_list_free (mounts);
|
||||
g_object_unref (monitor);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *
|
||||
shell_util_get_icon_for_uri_known_folders (const char *uri)
|
||||
{
|
||||
const char *icon;
|
||||
char *path;
|
||||
int len;
|
||||
|
||||
icon = NULL;
|
||||
|
||||
if (!g_str_has_prefix (uri, "file:"))
|
||||
return NULL;
|
||||
|
||||
path = g_filename_from_uri (uri, NULL, NULL);
|
||||
|
||||
len = strlen (path);
|
||||
if (path[len] == '/')
|
||||
path[len] = '\0';
|
||||
|
||||
if (strcmp (path, "/") == 0)
|
||||
icon = "drive-harddisk";
|
||||
else if (strcmp (path, g_get_home_dir ()) == 0)
|
||||
icon = "user-home";
|
||||
else if (strcmp (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP))
|
||||
== 0)
|
||||
icon = "user-desktop";
|
||||
|
||||
g_free (path);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
/* This is based on nautilus_compute_title_for_uri() and
|
||||
* nautilus_file_get_display_name_nocopy() */
|
||||
char *
|
||||
shell_util_get_label_for_uri (const char *text_uri)
|
||||
{
|
||||
GFile *file;
|
||||
char *label;
|
||||
GFile *root;
|
||||
char *root_display;
|
||||
|
||||
/* Here's what we do:
|
||||
* + x-nautilus-search: URI
|
||||
* + check if the URI is a mount
|
||||
* + if file: URI:
|
||||
* - check for known file: URI
|
||||
* - check for description of the GFile
|
||||
* - use display name of the GFile
|
||||
* + else:
|
||||
* - check for description of the GFile
|
||||
* - if the URI is a root: "root displayname"
|
||||
* - else: "root displayname: displayname"
|
||||
*/
|
||||
|
||||
label = NULL;
|
||||
|
||||
//FIXME: see nautilus_query_to_readable_string() to have a nice name
|
||||
if (g_str_has_prefix (text_uri, "x-nautilus-search:"))
|
||||
return g_strdup (_("Search"));
|
||||
|
||||
file = g_file_new_for_uri (text_uri);
|
||||
|
||||
label = shell_util_get_file_display_name_if_mount (file);
|
||||
if (label)
|
||||
{
|
||||
g_object_unref (file);
|
||||
return label;
|
||||
}
|
||||
|
||||
if (g_str_has_prefix (text_uri, "file:"))
|
||||
{
|
||||
label = shell_util_get_file_display_for_common_files (file);
|
||||
if (!label)
|
||||
label = shell_util_get_file_description (file);
|
||||
if (!label)
|
||||
label = shell_util_get_file_display_name (file, TRUE);
|
||||
g_object_unref (file);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
label = shell_util_get_file_description (file);
|
||||
if (label)
|
||||
{
|
||||
g_object_unref (file);
|
||||
return label;
|
||||
}
|
||||
|
||||
root = shell_util_get_gfile_root (file);
|
||||
root_display = shell_util_get_file_description (root);
|
||||
if (!root_display)
|
||||
root_display = shell_util_get_file_display_name (root, FALSE);
|
||||
if (!root_display)
|
||||
/* can happen with URI schemes non supported by gvfs */
|
||||
root_display = g_file_get_uri_scheme (root);
|
||||
|
||||
if (g_file_equal (file, root))
|
||||
label = root_display;
|
||||
else
|
||||
{
|
||||
char *displayname;
|
||||
|
||||
displayname = shell_util_get_file_display_name (file, TRUE);
|
||||
/* Translators: the first string is the name of a gvfs
|
||||
* method, and the second string is a path. For
|
||||
* example, "Trash: some-directory". It means that the
|
||||
* directory called "some-directory" is in the trash.
|
||||
*/
|
||||
label = g_strdup_printf (_("%1$s: %2$s"),
|
||||
root_display, displayname);
|
||||
g_free (root_display);
|
||||
g_free (displayname);
|
||||
}
|
||||
|
||||
g_object_unref (root);
|
||||
g_object_unref (file);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_util_get_icon_for_uri:
|
||||
* @text_uri: A URI
|
||||
*
|
||||
* Look up the icon that should be associated with a given URI. Handles
|
||||
* various special GNOME-internal cases like x-nautilus-search, etc.
|
||||
*
|
||||
* Return Value: (transfer none): A new #GIcon
|
||||
*/
|
||||
GIcon *
|
||||
shell_util_get_icon_for_uri (const char *text_uri)
|
||||
{
|
||||
const char *name;
|
||||
GFile *file;
|
||||
GFileInfo *info;
|
||||
GIcon *retval;
|
||||
|
||||
/* Here's what we do:
|
||||
* + check for known file: URI
|
||||
* + x-nautilus-search: URI
|
||||
* + override burn: URI icon
|
||||
* + check if the URI is a mount
|
||||
* + override trash: URI icon for subfolders
|
||||
* + check for application/x-gnome-saved-search mime type and override
|
||||
* icon of the GFile
|
||||
* + use icon of the GFile
|
||||
*/
|
||||
|
||||
/* this only checks file: URI */
|
||||
name = shell_util_get_icon_for_uri_known_folders (text_uri);
|
||||
if (name)
|
||||
return g_themed_icon_new (name);
|
||||
|
||||
if (g_str_has_prefix (text_uri, "x-nautilus-search:"))
|
||||
return g_themed_icon_new ("folder-saved-search");
|
||||
|
||||
/* gvfs doesn't give us a nice icon, so overriding */
|
||||
if (g_str_has_prefix (text_uri, "burn:"))
|
||||
return g_themed_icon_new ("nautilus-cd-burner");
|
||||
|
||||
file = g_file_new_for_uri (text_uri);
|
||||
|
||||
retval = shell_util_get_file_icon_if_mount (file);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* gvfs doesn't give us a nice icon for subfolders of the trash, so
|
||||
* overriding */
|
||||
if (g_str_has_prefix (text_uri, "trash:"))
|
||||
{
|
||||
GFile *root;
|
||||
|
||||
root = shell_util_get_gfile_root (file);
|
||||
g_object_unref (file);
|
||||
file = root;
|
||||
}
|
||||
|
||||
info = g_file_query_info (file, "standard::icon", G_FILE_QUERY_INFO_NONE,
|
||||
NULL, NULL);
|
||||
g_object_unref (file);
|
||||
|
||||
if (!info)
|
||||
return g_themed_icon_new ("gtk-file");
|
||||
|
||||
retval = g_file_info_get_icon (info);
|
||||
if (retval)
|
||||
g_object_ref (retval);
|
||||
g_object_unref (info);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
return g_themed_icon_new ("gtk-file");
|
||||
}
|
13
src/shell-uri-util.h
Normal file
13
src/shell-uri-util.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef __SHELL_UTIL_H__
|
||||
#define __SHELL_UTIL_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
char *shell_util_get_label_for_uri (const char *text_uri);
|
||||
GIcon *shell_util_get_icon_for_uri (const char *text_uri);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_UTIL_H__ */
|
@ -1,3 +1,5 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/* tidy-grid.h: Reflowing grid layout container for clutter.
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corporation
|
||||
@ -102,7 +104,6 @@ G_DEFINE_TYPE_WITH_CODE (TidyGrid, tidy_grid,
|
||||
struct _TidyGridPrivate
|
||||
{
|
||||
gfloat for_height, for_width;
|
||||
gfloat pref_width, pref_height;
|
||||
gfloat alloc_width, alloc_height;
|
||||
|
||||
GHashTable *hash_table;
|
||||
@ -139,7 +140,6 @@ struct _TidyGridActorData
|
||||
{
|
||||
gboolean xpos_set, ypos_set;
|
||||
gfloat xpos, ypos;
|
||||
gfloat pref_width, pref_height;
|
||||
};
|
||||
|
||||
static void
|
||||
@ -660,21 +660,37 @@ tidy_grid_pick (ClutterActor *actor,
|
||||
|
||||
static void
|
||||
tidy_grid_get_preferred_width (ClutterActor *self,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
TidyGrid *layout = (TidyGrid *) self;
|
||||
TidyGridPrivate *priv = layout->priv;
|
||||
gfloat natural_width;
|
||||
GList *iter;
|
||||
gfloat natural_width = 0;
|
||||
gfloat min_width = 0;
|
||||
|
||||
for (iter = priv->list; iter; iter=iter->next)
|
||||
{
|
||||
ClutterActor *child = iter->data;
|
||||
gfloat child_natural_w, child_natural_h;
|
||||
gfloat child_min_w, child_min_h;
|
||||
|
||||
clutter_actor_get_preferred_size (child, &child_min_w, &child_min_h,
|
||||
&child_natural_w, &child_natural_h);
|
||||
|
||||
if (child_min_w > min_width)
|
||||
min_width = child_min_w;
|
||||
natural_width += child_natural_w;
|
||||
|
||||
if (iter->next)
|
||||
natural_width += priv->column_gap;
|
||||
}
|
||||
|
||||
natural_width = 200.0;
|
||||
if (min_width_p)
|
||||
*min_width_p = natural_width;
|
||||
*min_width_p = min_width;
|
||||
if (natural_width_p)
|
||||
*natural_width_p = natural_width;
|
||||
|
||||
priv->pref_width = natural_width;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -685,12 +701,42 @@ tidy_grid_get_preferred_height (ClutterActor *self,
|
||||
{
|
||||
TidyGrid *layout = (TidyGrid *) self;
|
||||
TidyGridPrivate *priv = layout->priv;
|
||||
gfloat current_natural_width;
|
||||
gfloat row_height;
|
||||
gfloat natural_height;
|
||||
GList *iter;
|
||||
|
||||
natural_height = 200.0;
|
||||
current_natural_width = 0;
|
||||
natural_height = 0;
|
||||
row_height = 0;
|
||||
for (iter = priv->list; iter; iter=iter->next)
|
||||
{
|
||||
ClutterActor *child = iter->data;
|
||||
gfloat child_natural_w, child_natural_h;
|
||||
|
||||
priv->for_width = for_width;
|
||||
priv->pref_height = natural_height;
|
||||
clutter_actor_get_preferred_size (child, NULL, NULL,
|
||||
&child_natural_w, &child_natural_h);
|
||||
|
||||
if (iter == priv->list)
|
||||
{
|
||||
current_natural_width = child_natural_w;
|
||||
}
|
||||
else if ((current_natural_width + child_natural_w) > for_width)
|
||||
{
|
||||
natural_height += row_height + priv->row_gap;
|
||||
current_natural_width = child_natural_w;
|
||||
row_height = child_natural_h;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_natural_width += priv->column_gap + child_natural_w;
|
||||
}
|
||||
|
||||
if (child_natural_h > row_height)
|
||||
row_height = child_natural_h;
|
||||
}
|
||||
|
||||
natural_height += row_height;
|
||||
|
||||
if (min_height_p)
|
||||
*min_height_p = natural_height;
|
||||
@ -758,9 +804,6 @@ compute_row_height (GList *siblings,
|
||||
return best_yet;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static gfloat
|
||||
compute_row_start (GList *siblings,
|
||||
gfloat start_x,
|
||||
|
@ -32,7 +32,7 @@ fi
|
||||
# Devel packages needed by gnome-shell and its deps:
|
||||
# dbus-glib, gconf, GL, gnome-menus, gstreamer, gtk, libffi,
|
||||
# libgnomeui, librsvg, libwnck, python, readline, spidermonkey
|
||||
# ({mozilla,firefox,xulrunner}-js), xdamage, xscrnsaver
|
||||
# ({mozilla,firefox,xulrunner}-js), xdamage
|
||||
#
|
||||
# Non-devel packages needed by gnome-shell and its deps:
|
||||
# gdb, glxinfo, gstreamer-plugins-base, gstreamer-plugins-good,
|
||||
@ -63,8 +63,8 @@ if test x$system = xUbuntu -o x$system = xDebian ; then
|
||||
automake bison flex git-core gnome-common gtk-doc-tools \
|
||||
libdbus-glib-1-dev libgconf2-dev libgtk2.0-dev libffi-dev \
|
||||
libgnome-menu-dev libgnomeui-dev librsvg2-dev libwnck-dev libgl1-mesa-dev \
|
||||
mesa-common-dev python-dev libreadline5-dev xulrunner-dev \
|
||||
xserver-xephyr libxss-dev \
|
||||
mesa-common-dev mesa-utils python-dev libreadline5-dev xulrunner-dev \
|
||||
xserver-xephyr \
|
||||
libgstreamer0.10-dev gstreamer0.10-plugins-base gstreamer0.10-plugins-good \
|
||||
; do
|
||||
if ! dpkg_is_installed $pkg; then
|
||||
@ -86,7 +86,7 @@ if test x$system = xFedora ; then
|
||||
libtool pkgconfig \
|
||||
dbus-glib-devel GConf2-devel gnome-menus-devel gtk2-devel libffi-devel libgnomeui-devel \
|
||||
librsvg2-devel libwnck-devel mesa-libGL-devel python-devel readline-devel \
|
||||
xulrunner-devel libXdamage-devel libXScrnSaver-devel \
|
||||
xulrunner-devel libXdamage-devel \
|
||||
gstreamer-devel gstreamer-plugins-base gstreamer-plugins-good \
|
||||
gdb glx-utils xorg-x11-apps xorg-x11-server-Xephyr xterm zenity \
|
||||
; do
|
||||
@ -105,7 +105,7 @@ if test x$system = xSUSE ; then
|
||||
curl \
|
||||
bison flex gnome-doc-utils-devel \
|
||||
gconf2-devel libffi-devel libgnomeui-devel librsvg-devel libwnck-devel \
|
||||
libXScrnSaver-devel readline-devel mozilla-xulrunner190-devel \
|
||||
xorg-x11-proto-devel readline-devel mozilla-xulrunner190-devel \
|
||||
xorg-x11-devel xterm xorg-x11 xorg-x11-server-extra \
|
||||
; do
|
||||
if ! rpm -q $pkg > /dev/null 2>&1; then
|
||||
@ -126,8 +126,7 @@ if test x$system = xMandrivaLinux ; then
|
||||
bison flex gnome-common gnome-doc-utils gtk-doc intltool \
|
||||
libGConf2-devel ffi5-devel libgnomeui2-devel librsvg2-devel \
|
||||
libwnck-1-devel GL-devel readline-devel libxulrunner-devel \
|
||||
libxdamage-devel libxscrnsaver-devel \
|
||||
mesa-demos x11-server-xephyr x11-apps xterm zenity \
|
||||
libxdamage-devel mesa-demos x11-server-xephyr x11-apps xterm zenity \
|
||||
; do
|
||||
if ! rpm -q --whatprovides $pkg > /dev/null 2>&1; then
|
||||
reqd="$pkg $reqd"
|
||||
|
@ -30,7 +30,7 @@
|
||||
</autotools>
|
||||
|
||||
<autotools id="clutter">
|
||||
<branch repo="git.clutter-project.org" module="clutter"/>
|
||||
<branch repo="git.clutter-project.org" module="clutter" revision="clutter-1.0"/>
|
||||
<dependencies>
|
||||
<dep package="gobject-introspection"/>
|
||||
</dependencies>
|
||||
|
Reference in New Issue
Block a user