Compare commits

..

48 Commits

Author SHA1 Message Date
Colin Walters
e191636f34 Update for review of e57b7ec335 2009-06-30 15:29:10 -04:00
Colin Walters
39e31a3aa9 Update for comments on b5988a57fa 2009-06-30 15:29:10 -04:00
Colin Walters
8293f3423a Update for review of d5a80d3063 2009-06-30 15:29:10 -04:00
Marina Zhurakhinskaya
858a82fcdd Use a separate icon image as a drag actor instead of using the clone of the icon
Clutter no longer allows using a clone of an actor that is not a part of
the scene graph. This is what used to happen when we created a clone for
the icon of the item that was being dragged, and then closed the More panes
with the original item, removing the icon from the scene graph. This was
also when happened when the user hit Esc while dragging, which prompted the
overlay to close, removing the original icon from the scene graph.

Rename getIcon() methods to createIcon() to better reflect on the fact that
a new icon is created each time the method is called (we do use cache in
some cases).

Remove a stray log message in overlay.js

Fixes http://bugzilla.gnome.org/show_bug.cgi?id=585490
and http://bugzilla.gnome.org/show_bug.cgi?id=585489
2009-06-29 15:08:48 -04:00
Marina Zhurakhinskaya
2ae89f3b36 Merge branch 'master' into my-overlay-design 2009-06-29 14:40:04 -04:00
Marina Zhurakhinskaya
cdee026095 Make workspaces accept a drop of AppDisplay.WellDisplayItem
AppDispplay.WellDisplayItem needed to be added along with the
GenericDisplay.GenericDisplayItem as a type of a drop object
that workspaces accept.
2009-06-29 12:28:21 -04:00
Colin Walters
340fbfe943 Avoid duplicating most used applications in the AppDisplay cache
All used applications should be in the database from the menus
anyways.
2009-06-26 18:35:48 -04:00
Colin Walters
c577951ec9 Fix getMostUsedApps to avoid returning duplicate items 2009-06-26 18:30:28 -04:00
Colin Walters
d588b083d9 Fix invalid function signature and a memory leak
Add the missing GParamSpec to on_n_workspaces_changed.

Also, we don't need to re-dup the appid, since it's already dup'd.
2009-06-26 17:12:55 -04:00
Colin Walters
e57b7ec335 Replace main AppDisplay with AppWell
This is a start towards implementing the 02 overlay design.  The
default applications has moved into GConf.  We keep around an AppDisplay
instance for handling the right side behavior.
2009-06-25 20:32:08 -04:00
Colin Walters
b5988a57fa ShellAppSystem: Add API for looking up and manipulating favorites
The ShellAppSystem now proxies the GConf favorites key.
2009-06-25 20:32:08 -04:00
Colin Walters
d5a80d3063 ShellAppMonitor now works harder to assocate windows with desktop files
Instead of just keeping a heuristic wmclass match, call into the app
system to more strongly associate windows with desktop file IDs.
2009-06-25 20:32:08 -04:00
Marina Zhurakhinskaya
02a8cd5ce2 Use a single ItemResults class instead of AppResults and DocResults
AppResults and DocResults classes were identical with an exception of
the display class they used and the text label for the results. Merged
them into a single ItemResults class that takes these two additional
arguments.
2009-06-25 19:14:29 -04:00
Marina Zhurakhinskaya
aa77762d27 Move the activate and select functionality inside the callbacks
Move the activate and select functionality inside the callbacks for
'button-release-event' signals of the display item and the information
button correspondingly. This way it is more obvious that this is an
event handling code that needs to return a boolean value for whether
the signal has been fully handled by the actor.
2009-06-25 18:01:45 -04:00
Marina Zhurakhinskaya
de3f5dec68 Use TextureCache to load the information icon
Use TextureCache to load the information icon, so that we don't create a
new ClutterTexture for information icons corresponding to each display
item.
2009-06-25 17:24:46 -04:00
Marina Zhurakhinskaya
84865f416d Make sure we show the information button when a new item appears under the pointer
Update the code for checking a display item under the pointer to expect
the item itself rather than its child to be returned by stage_get_actor_at_pos().

This code is now used to display an information button when an item is
drawn under the pointer, so update the comment accordingly.
2009-06-25 15:31:09 -04:00
Marina Zhurakhinskaya
ca3e3df199 Show and hide dash panes instead of adding and removing each time
Add results and details panes up-front, and show and hide them instead
of adding and removing them each time
2009-06-25 14:54:28 -04:00
Marina Zhurakhinskaya
643febebf8 Add a comment about the use of the transparent background and set its opacity to 0
Add a comment about the use of the transparent background to catch clicks
in the workspaces area when the dash panes are being displayed and dismiss
the dash panes.

Set opacity for the background to 0 instead of using a transparent background
color so that Clutter optimizes the drawing of the background actor.
2009-06-25 14:33:24 -04:00
Marina Zhurakhinskaya
6d002c893d Fix up horizontal gradient code and its use
Fix up the comments about the horizontal gradient code and use 8x1 texture
instead of 8x8.

Make sure the values we assign to the three-stop horizontal gradient
require the use of the three stop gradient, with the middle value not being
right between the side values.
2009-06-25 14:32:00 -04:00
Marina Zhurakhinskaya
5eaed34047 Display search results automatically
Display a pane with search results for both applications and documents
automatically when a search string is entered.

Allow viewing search results for the individual section when More link
for applications or documents is clicked.

Move text labels for the applications and documents sections into the
respective classes.
2009-06-24 18:24:48 -04:00
Marina Zhurakhinskaya
8f5b55350f Add back search functionality
Enable typing in the search box and display results in the results pane.
This means that the user has to open the details pane for applications
or documents to view the results for now.

Connect Enter to launch the seleted item.

Connect Escape to clear search, remove results and details panes,
or exit overlay.
2009-06-22 19:06:50 -04:00
Marina Zhurakhinskaya
c600ebb687 Merge branch 'master' into my-overlay-design 2009-06-22 16:14:55 -04:00
Siegfried-Angel Gevatter Pujals
9abc062a64 Hide details panel by default
Don't show the details panel when the overlay is activated,
only when explicitly requested by clicking the info icon.
2009-06-22 16:10:39 -04:00
Marina Zhurakhinskaya
81d0474926 Fix the height allocated to the results sections
The results sections no longer include a label on top of them, so the
height of that label needs to be subtracted when specifying the height
for the sections. This ensures that display controls are positioned
correctly on the bottom of the section.
2009-06-18 20:01:49 -04:00
Marina Zhurakhinskaya
c41902c188 Select an item when information button is clicked, launch on single click
Clicking the information button for an item selects it (i.e. highlights it)
and shows details about the item.
Clicking the rest of the item area launches it.
Item does not become draggable if the dragging is started over the information
icon (i.e. if the user presses the information icon, but releases elsewhere).

Make sure we emit "activated" signal and close the overlay when an item from
one of the results displays is launched.
2009-06-18 19:53:21 -04:00
Marina Zhurakhinskaya
97b9ccbff7 Add an icon for the information link
Use an (i) icon supplied by Jeremy for the information link and display
it on hover. Make sure it is positioned nicely and the text doesn't
overlap with it.
2009-06-18 17:50:56 -04:00
Colin Walters
282daf768f Merge branch 'master' into overlay-design02 2009-06-18 17:26:03 -04:00
Marina Zhurakhinskaya
1b057300b0 Remove pop-up previews
Pop-up previews are not part of the new design and interfere with the information link.

Make sure details display for applications has the appropriate width set.
2009-06-18 17:12:58 -04:00
Colin Walters
75da772d05 Merge branch 'master' into overlay-design02
Conflicts:
	js/ui/appDisplay.js
2009-06-18 13:12:21 -04:00
Marina Zhurakhinskaya
df9cf98826 Make sure at most one item is selected in the overlay
Make sure at most one item is selected in the overlay and we always show
a details pane for the selected item.

Improve the positioning of the search box.

Remove a duplicate variable DASH_PAD and use DASH_SECTION_PADDING everywhere instead.
2009-06-17 18:12:02 -04:00
Colin Walters
d3cb8e5b21 Split out separate AppResults, DocResults classes. Readd search as stub.
Avoid bloating the Dash class by separating out a bit more functionality.
2009-06-17 17:38:08 -04:00
Marina Zhurakhinskaya
71998a6b43 Merge branch 'master' into my-overlay-design 2009-06-17 13:54:39 -04:00
Colin Walters
2fd2293e4a Merge branch 'master' into overlay-design02 2009-06-17 10:12:31 -04:00
Marina Zhurakhinskaya
3528eef655 Change selected item color and make sure only one item is selected at a time
A blue selected item color fits better with the new color scheme than a green one.

Only one item should be selected across all the displays we are showing.
2009-06-16 16:30:42 -04:00
Marina Zhurakhinskaya
f209d6792d Display a pane with item details
Display a pane with item details, such as a full image previews, when an item is single clicked.

Add a placeholder information link that shows up when an item is moused over.
2009-06-16 14:50:38 -04:00
Marina Zhurakhinskaya
614e83476e Merge branch 'master' into my-overlay-design 2009-06-16 13:04:56 -04:00
Marina Zhurakhinskaya
4e2a301ef0 Add icons from Jeremy
These icons are for the "more", "close", and "info" images in the overlay.
2009-06-11 19:00:12 -04:00
Marina Zhurakhinskaya
6def8cf7dd Merge branch 'master' into my-overlay-design 2009-06-11 18:24:42 -04:00
Marina Zhurakhinskaya
1204898d1e Display all recent documents in the results pane
Display all recent documents in the results pane, in addition to the first
few displayed in the main dash. All documents can be viewed with the help
of a paging control.
2009-06-09 16:11:51 -04:00
Marina Zhurakhinskaya
98530afd87 Add a results pane in the overlay
Display the results pane above the workspaces. The results pane is somewhat
transparent and has a blue gradient background. The dash pane is slightly
transparent and also has a blue gradient background.

The results pane shows up when a More control is clicked. It disappears when
a Less control is clicked, an area outside of the dash area is clicked,
an item starts being dragged, or the overlay mode is exited.

Add shell_global_create_horizontal_gradient() to shell-global.[ch]
2009-06-08 19:06:23 -04:00
Marina Zhurakhinskaya
a9fedcce76 Merge branch 'master' into my-overlay-design 2009-06-08 18:02:31 -04:00
Colin Walters
dae1d94258 Merge branch 'master' into overlay-design02 2009-06-04 17:43:48 -04:00
Colin Walters
4a7c328c33 Revert inadvertent commits for future rebasing 2009-06-04 17:42:03 -04:00
Colin Walters
2c0d6fdf89 favorites 2009-06-03 11:49:42 -04:00
Colin Walters
12f896eb94 random hack 2009-06-02 18:18:25 -04:00
Colin Walters
230c91b6d4 Functionality in ShellAppMonitor for tracking active applications
Hook into the metacity core for notification of new/removed windows,
and use the WM_CLASS data to map these to applications.  This is
a first pass; we need further redudancy in the system.
2009-06-02 18:18:25 -04:00
Colin Walters
d7dcfe6a06 Make ShellAppMonitor and ShellAppSystem singletons 2009-06-02 17:21:50 -04:00
Colin Walters
275eba17db Remove a lot of obsolete code from old Sideshow (now Dash) 2009-06-02 13:13:51 -04:00
69 changed files with 3990 additions and 7413 deletions

2
.gitignore vendored
View File

@@ -16,8 +16,6 @@ config.log
config.status
config
configure
data/gnome-shell.desktop
data/gnome-shell.desktop.in
libtool
scripts/launcher.pyc
src/*.gir

View File

@@ -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 ; \

View File

@@ -1,12 +1,10 @@
AC_INIT(gnome-shell, 2.27.0)
AC_INIT(gnome-shell, 0.0.1)
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
@@ -22,7 +20,6 @@ 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
@@ -36,18 +33,19 @@ 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-1.0)
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-0.9)
else
AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
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(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(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.
@@ -82,31 +80,17 @@ AC_SUBST(GIRDIR)
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
AC_SUBST(TYPELIBDIR)
# 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)
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
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wall" ;;
esac
case " $OBJCFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) OBJCFLAGS="$OBJCFLAGS -Wall" ;;
esac
fi
changequote([,])dnl

View File

@@ -1,38 +1,14 @@
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 \
close.svg \
info.svg \
remove-workspace.svg \
view-more-activated.svg \
view-more.svg
remove-workspace.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 $(srcdir)/$(schema_DATA)
GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(top_builddir)/data/$(schema_DATA)
EXTRA_DIST = \
gnome-shell.desktop.in.in \
EXTRA_DIST = \
$(schema_DATA)
CLEANFILES = \
gnome-shell.desktop.in \
$(desktop_DATA)

View File

@@ -1,15 +0,0 @@
[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

View File

@@ -20,7 +20,7 @@
<applyto>/desktop/gnome/shell/favorite_apps</applyto>
<owner>gnome-shell</owner>
<type>list</type>
<list_type>string</list_type>
<listtype>string</listtype>
<default>[mozilla-firefox.desktop,evolution.desktop,openoffice.org-writer.desktop]</default>
<locale name="C">
<short>List of desktop file IDs for favorite applications</short>
@@ -30,49 +30,6 @@
</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>

View File

@@ -1,4 +1,5 @@
jsmiscdir = $(pkgdatadir)/js/misc
dist_jsmisc_DATA = \
appInfo.js \
docInfo.js

134
js/misc/appInfo.js Normal file
View File

@@ -0,0 +1,134 @@
/* -*- 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;
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();
},
createIcon : 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;
}
// getTopApps:
// @count: maximum number of apps to retrieve
//
// Gets a list of #AppInfos for the @count most-frequently-used
// applications, with explicitly-chosen favorites first.
//
// Return value: the list of #AppInfo
function getTopApps(count) {
let appMonitor = Shell.AppMonitor.get_default();
let matches = [], alreadyAdded = {};
let favs = getFavorites();
for (let i = 0; i < favs.length && favs.length <= count; i++) {
let appId = favs[i].appId;
if (alreadyAdded[appId])
continue;
alreadyAdded[appId] = true;
matches.push(favs[i]);
}
// 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));
for (let i = 0; i < apps.length && matches.length <= count; i++) {
let appId = apps[i] + ".desktop";
if (alreadyAdded[appId])
continue;
alreadyAdded[appId] = true;
let appInfo = getAppInfo(appId);
if (appInfo) {
matches.push(appInfo);
}
}
return matches;
}
function _idListToInfos(ids) {
let infos = [];
for (let i = 0; i < ids.length; i++) {
let display = getAppInfo(ids[i]);
if (display == null)
continue;
infos.push(display);
}
return infos;
}
function getFavorites() {
let system = Shell.AppSystem.get_default();
return _idListToInfos(system.get_favorites());
}
function getRunning() {
let monitor = Shell.AppMonitor.get_default();
return _idListToInfos(monitor.get_running_app_ids());
}

View File

@@ -5,8 +5,6 @@ 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;
@@ -18,17 +16,40 @@ 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();
},
createIcon : function(size) {
return Shell.TextureCache.get_default().load_recent_thumbnail(size, this._recentInfo);
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;
}
},
launch : function() {
@@ -42,7 +63,7 @@ DocInfo.prototype = {
if (appInfo != null) {
appInfo.launch_uris([this.uri], Main.createAppLaunchContext());
} else {
log("Failed to get default application info for mime type " + this.mimeType +
log("Failed to get default application info for mime type " + 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);
@@ -80,60 +101,14 @@ 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);

View File

@@ -5,16 +5,13 @@ 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 \

View File

@@ -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.NONE);
labelbox.append(this._label, Big.BoxPackFlags.EXPAND);
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 = Math.floor((global.screen_width - this.actor.width) / 2);
this.actor.y = Math.floor((global.screen_height - this.actor.height) / 2);
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.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

View File

@@ -3,7 +3,6 @@
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;
@@ -12,23 +11,14 @@ 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;
@@ -38,23 +28,20 @@ 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) {
this._init(appInfo);
function AppDisplayItem(appInfo, availableWidth) {
this._init(appInfo, availableWidth);
}
AppDisplayItem.prototype = {
__proto__: GenericDisplay.GenericDisplayItem.prototype,
_init : function(appInfo) {
GenericDisplay.GenericDisplayItem.prototype._init.call(this);
_init : function(appInfo, availableWidth) {
GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
this._appInfo = appInfo;
this._setItemInfo(appInfo.get_name(), appInfo.get_description());
},
getId: function() {
return this._appInfo.get_id();
this._setItemInfo(appInfo.name, appInfo.description);
},
//// Public method overrides ////
@@ -68,12 +55,24 @@ AppDisplayItem.prototype = {
// Returns an icon for the item.
_createIcon : function() {
return this._appInfo.create_icon_texture(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
return this._appInfo.createIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
},
// Returns a preview icon for the item.
_createPreviewIcon : function() {
return this._appInfo.create_icon_texture(GenericDisplay.PREVIEW_ICON_SIZE);
// Ensures the preview icon is created.
_ensurePreviewIconCreated : function() {
if (!this._showPreview || this._previewIcon)
return;
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);
}
}
}
};
@@ -114,28 +113,24 @@ MenuItem.prototype = {
}
if (pixbuf != null)
Shell.clutter_texture_set_from_pixbuf(this._icon, pixbuf);
this.actor.append(this._icon, Big.BoxPackFlags.NONE);
this.actor.append(this._icon, 0);
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);
// 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 });
let box = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER
});
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,
this._arrow = new Shell.Arrow({ surface_width: MENU_ICON_SIZE/2,
surface_height: MENU_ICON_SIZE/2,
direction: Gtk.ArrowType.RIGHT,
opacity: 0 });
arrowBox.append(this._arrow, Big.BoxPackFlags.NONE);
this.actor.append(arrowBox, Big.BoxPackFlags.NONE);
opacity: 0
});
box.append(this._arrow, 0);
this.actor.append(box, 0);
},
getState: function() {
@@ -165,16 +160,19 @@ 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() {
this._init();
function AppDisplay(width, height, numberOfColumns, columnGap) {
this._init(width, height, numberOfColumns, columnGap);
}
AppDisplay.prototype = {
__proto__: GenericDisplay.GenericDisplay.prototype,
_init : function() {
GenericDisplay.GenericDisplay.prototype._init.call(this);
_init : function(width, height, numberOfColumns, columnGap) {
GenericDisplay.GenericDisplay.prototype._init.call(this, width, height, numberOfColumns, columnGap);
this._menus = [];
this._menuDisplays = [];
@@ -197,7 +195,7 @@ AppDisplay.prototype = {
this._redisplay(false);
}));
// Load the apps now so it doesn't slow down the first
// Load the GAppInfos now so it doesn't slow down the first
// transition into the overlay
this._refreshCache();
@@ -213,7 +211,6 @@ AppDisplay.prototype = {
this.connect('expanded', Lang.bind(this, function (self) {
this._filterReset();
}));
this._filterReset();
},
moveRight: function() {
@@ -233,7 +230,7 @@ AppDisplay.prototype = {
},
// Override genericDisplay.js
getNavigationArea: function() {
getSideArea: function() {
return this._menuDisplay;
},
@@ -254,13 +251,16 @@ AppDisplay.prototype = {
// Protected overrides
_filterActive: function() {
// We always have a filter now since a menu must be selected
return true;
return !!this._search || this._activeMenuIndex >= 0;
},
_filterReset: function() {
GenericDisplay.GenericDisplay.prototype._filterReset.call(this);
this._selectMenuIndex(0);
if (this._activeMenu != null)
this._activeMenu.setState(MENU_UNSELECTED);
this._activeMenuIndex = -1;
this._activeMenu = null;
this._focusInMenu = true;
},
//// Private ////
@@ -275,53 +275,48 @@ AppDisplay.prototype = {
this._menuDisplays[index].setState(MENU_SELECTED);
},
_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 });
},
_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);
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);
}
},
_addAppForId: function(appId) {
let appInfo = AppInfo.getAppInfo(appId);
if (appInfo != null) {
this._addApp(appInfo);
} else {
log("appInfo for " + appId + " was not found.");
}
},
_addApp: function(appInfo) {
let appId = appInfo.get_id();
let appId = appInfo.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 ////
// Gets information about all applications by calling Gio.app_info_get_all().
_refreshCache : function() {
let me = this;
@@ -331,14 +326,15 @@ 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 app = menuApps[j];
this._addApp(app);
let appId = menuApps[j];
this._addAppForId(appId);
}
}
@@ -346,16 +342,17 @@ 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 app = settings[i];
this._addApp(app);
let appId = settings[i];
this._addAppForId(appId);
}
this._appsStale = false;
},
// Stub this out; the app display always has a category selected
// Sets the list of the displayed items based on the most used apps.
_setDefaultList : function() {
this._matchedItems = [];
let matchedInfos = AppInfo.getTopApps(MAX_ITEMS);
this._matchedItems = matchedInfos.map(function(info) { return info.appId; });
},
// Compares items associated with the item ids based on the alphabetical order
@@ -364,17 +361,14 @@ AppDisplay.prototype = {
_compareItems : function(itemIdA, itemIdB) {
let appA = this._allItems[itemIdA];
let appB = this._allItems[itemIdB];
return appA.get_name().localeCompare(appB.get_name());
return appA.name.localeCompare(appB.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 Shell.AppInfo.
// the name, description, execution command, and categories for the application.
// Item info is expected to be GAppInfo.
// 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 != "")
@@ -384,10 +378,10 @@ AppDisplay.prototype = {
},
_isInfoMatchingMenu : function(itemInfo, search) {
let id = itemInfo.get_id();
let id = itemInfo.id;
for (let i = 0; i < this._activeMenuApps.length; i++) {
let activeApp = this._activeMenuApps[i];
if (activeApp.get_id() == id)
let activeId = this._activeMenuApps[i];
if (activeId == id)
return true;
}
return false;
@@ -397,43 +391,39 @@ AppDisplay.prototype = {
if (search == null || search == '')
return true;
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());
let name = itemInfo.name.toLowerCase();
if (name.indexOf(search) >= 0)
return true;
let description = fold(itemInfo.get_description());
let description = itemInfo.description;
if (description) {
description = description.toLowerCase();
if (description.indexOf(search) >= 0)
return true;
}
let exec = fold(itemInfo.get_executable());
if (exec == null) {
if (itemInfo.executable == null) {
log("Missing an executable for " + itemInfo.name);
} else {
let exec = itemInfo.executable.toLowerCase();
if (exec.indexOf(search) >= 0)
return true;
}
let categories = itemInfo.get_categories();
// we expect this._appCategories.hasOwnProperty(itemInfo.id) to always be true here
let categories = this._appCategories[itemInfo.id];
for (let i = 0; i < categories.length; i++) {
let category = fold(categories[i]);
let category = categories[i].toLowerCase();
if (category.indexOf(search) >= 0)
return true;
}
return false;
},
// Creates an AppDisplayItem based on itemInfo, which is expected be an Shell.AppInfo object.
// Creates an AppDisplayItem based on itemInfo, which is expected be an AppInfo object.
_createDisplayItem: function(itemInfo) {
return new AppDisplayItem(itemInfo);
return new AppDisplayItem(itemInfo, this._columnWidth);
}
};
@@ -450,76 +440,35 @@ WellDisplayItem.prototype = {
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,
width: APP_ICON_SIZE,
reactive: true });
this.actor._delegate = this;
this.actor.connect('button-release-event', Lang.bind(this, function (b, e) {
this._handleActivate();
this.launch();
this.emit('activated');
}));
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._icon = appInfo.createIcon(APP_ICON_SIZE);
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
this.actor.append(this._icon, 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;
let count = Shell.AppMonitor.get_default().get_window_count(appInfo.appId);
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);
text: appInfo.name });
if (count > 0) {
let runningBox = new Big.Box({ /* border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
border: 1,
padding: 1 */ });
runningBox.append(this._name, Big.BoxPackFlags.EXPAND);
this.actor.append(runningBox, Big.BoxPackFlags.NONE);
} else {
this.actor.append(this._name, Big.BoxPackFlags.NONE);
}
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.
@@ -529,7 +478,7 @@ WellDisplayItem.prototype = {
// Draggable interface - FIXME deduplicate with GenericDisplay
getDragActor: function(stageX, stageY) {
this.dragActor = this.appInfo.create_icon_texture(APP_ICON_SIZE);
this.dragActor = this.appInfo.createIcon(APP_ICON_SIZE);
// If the user dragged from the icon itself, then position
// the dragActor over the original icon. Otherwise center it
@@ -548,211 +497,79 @@ WellDisplayItem.prototype = {
// 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();
function WellArea(width, isFavorite) {
this._init(width, isFavorite);
}
WellGrid.prototype = {
_init: function() {
this.actor = new Shell.GenericContainer();
WellArea.prototype = {
_init : function(width, isFavorite) {
this.isFavorite = isFavorite;
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));
this.actor = new Tidy.Grid({ width: width });
this.actor._delegate = this;
},
_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;
redisplay: function (infos) {
let children;
children = this.actor.get_children();
children.forEach(Lang.bind(this, function (v) {
v.destroy();
}));
for (let i = 0; i < infos.length; i++) {
let display = new WellDisplayItem(infos[i], this.isFavorite);
display.connect('activated', Lang.bind(this, function (display) {
this.emit('activated', display);
}));
this.actor.add_actor(display.actor);
};
},
_getPreferredHeight: function (grid, forWidth, alloc) {
let [rows, columns, itemWidth, itemHeight] = this._computeLayout(forWidth);
let totalVerticalSpacing = Math.max(rows - 1, 0) * WELL_ITEM_VSPACING;
// Draggable target interface
acceptDrop : function(source, actor, x, y, time) {
let global = Shell.Global.get();
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;
}
if (!(source instanceof WellDisplayItem)) {
return false;
}
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;
let appSystem = Shell.AppSystem.get_default();
let id = source.appInfo.appId;
if (source.isFavorite && (!this.isFavorite)) {
Mainloop.idle_add(function () {
appSystem.remove_favorite(id);
});
} else if ((!source.isFavorite) && this.isFavorite) {
Mainloop.idle_add(function () {
appSystem.add_favorite(id);
});
} else {
itemWidth = Math.max(forWidth - horizSpacingTotal, 0) / nColumns;
return false;
}
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];
return true;
}
}
function AppWell() {
this._init();
Signals.addSignalMethods(WellArea.prototype);
function AppWell(width) {
this._init(width);
}
AppWell.prototype = {
_init : function() {
_init : function(width) {
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);
width: width });
this._appSystem = Shell.AppSystem.get_default();
this._appMonitor = Shell.AppMonitor.get_default();
@@ -767,101 +584,40 @@ AppWell.prototype = {
this._redisplay();
}));
this._favoritesArea = new WellArea(width, true);
this._favoritesArea.connect('activated', Lang.bind(this, function (a, display) {
this.emit('activated');
}));
this.actor.append(this._favoritesArea.actor, Big.BoxPackFlags.NONE);
this._runningBox = new Big.Box({ border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
border: 1,
corner_radius: 3,
padding: GenericDisplay.PREVIEW_BOX_PADDING });
this._runningArea = new WellArea(width, false);
this._runningArea.connect('activated', Lang.bind(this, function (a, display) {
this.emit('activated');
}));
this._runningBox.append(this._runningArea.actor, Big.BoxPackFlags.EXPAND);
this.actor.append(this._runningBox, Big.BoxPackFlags.NONE);
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);
_redisplay: function() {
let arrayToObject = function(a) {
let o = {};
for (let i = 0; i < a.length; i++)
o[a[i]] = 1;
return o;
};
let favorites = AppInfo.getFavorites();
let favoriteIds = arrayToObject(favorites.map(function (e) { return e.appId; }));
let running = AppInfo.getRunning().filter(function (e) {
return !(e.appId in favoriteIds);
});
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;
this._favoritesArea.redisplay(favorites);
this._runningArea.redisplay(running);
}
};

View File

@@ -2,11 +2,6 @@
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);
@@ -14,20 +9,12 @@ DEFAULT_BUTTON_COLOR.from_pixel(0xeeddcc66);
const DEFAULT_PRESSED_BUTTON_COLOR = new Clutter.Color();
DEFAULT_PRESSED_BUTTON_COLOR.from_pixel(0xccbbaa66);
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);
function Button(widget, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) {
this._init(widget, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight);
}
Button.prototype = {
_init : function(widgetOrText, buttonColor, pressedButtonColor, textColor, staysPressed, minWidth, minHeight, font) {
_init : function(widgetOrText, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) {
let me = this;
this._buttonColor = buttonColor
@@ -38,18 +25,10 @@ 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)
@@ -63,14 +42,13 @@ Button.prototype = {
this.button = new Big.Box({ reactive: true,
corner_radius: 5,
padding_left: SIDE_PADDING,
padding_right: SIDE_PADDING,
padding_left: 4,
padding_right: 4,
orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER
});
if (typeof widgetOrText == 'string') {
this._widget = new Clutter.Text({ font_name: this._font,
color: this._textColor,
this._widget = new Clutter.Text({ font_name: "Sans Bold 16px",
text: widgetOrText });
} else {
this._widget = widgetOrText;
@@ -134,95 +112,3 @@ 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" });
}
};

View File

@@ -126,11 +126,9 @@ Chrome.prototype = {
//
// Removes @actor from the chrome layer
removeActor: function(actor) {
if (actor.get_parent() == this.nonOverlayActor)
this.nonOverlayActor.remove_actor(actor);
else
this.actor.remove_actor(actor);
this._untrackActor(actor, true, true);
this.actor.remove_actor(actor);
// We don't have to do anything else; the parent-set handlers
// will do the rest.
},
_findActor: function(actor) {
@@ -172,7 +170,7 @@ Chrome.prototype = {
this._trackedActors.push(actorData);
actor = actor.get_parent();
if (actor != this.actor && actor != this.nonOverlayActor)
if (actor != this.actor)
this._trackActor(actor, false, false);
if (inputRegion || strut)
@@ -200,7 +198,7 @@ Chrome.prototype = {
actor.disconnect(actorData.parentSetId);
actor = actor.get_parent();
if (actor && actor != this.actor && actor != this.nonOverlayActor)
if (actor && actor != this.actor)
this._untrackActor(actor, false, false);
}
@@ -211,10 +209,10 @@ Chrome.prototype = {
_actorReparented: function(actor, oldParent) {
if (this._verifyAncestry(actor, this.actor)) {
let newParent = actor.get_parent();
if (newParent != this.actor && newParent != this.nonOverlayActor)
if (newParent != this.actor)
this._trackActor(newParent, false, false);
}
if (oldParent != this.actor && oldParent != this.nonOverlayActor)
if (oldParent != this.actor)
this._untrackActor(oldParent, false, false);
},
@@ -233,8 +231,7 @@ Chrome.prototype = {
_queueUpdateRegions: function() {
if (!this._updateRegionIdle)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions));
},
_windowsRestacked: function() {

View File

@@ -1,518 +0,0 @@
/* -*- 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);

View File

@@ -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 - targX) / target.scale_x,
(stageY + this._dragOffsetY - targY) / target.scale_y,
(stageX + this._dragOffsetX + this._xOffset - targX) / target.scale_x,
(stageY + this._dragOffsetY + this._yOffset - targY) / target.scale_y,
event.get_time());
}
target = target.get_parent();
@@ -153,6 +153,8 @@ _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();
@@ -163,15 +165,14 @@ _Draggable.prototype = {
if (target._delegate && target._delegate.acceptDrop) {
let [targX, targY] = target.get_transformed_position();
if (target._delegate.acceptDrop(this.actor._delegate, actor,
(dropX - targX) / target.scale_x,
(dropY - targY) / target.scale_y,
(dropX + this._xOffset - targX) / target.scale_x,
(dropY + this._yOffset - 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;
}
}
@@ -195,20 +196,18 @@ _Draggable.prototype = {
transition: "easeOutQuad",
onComplete: this._onSnapBackComplete,
onCompleteScope: this,
onCompleteParams: [actor, event.get_time()]
onCompleteParams: [actor]
});
return true;
},
_onSnapBackComplete : function (dragActor, eventTime) {
_onSnapBackComplete : function (dragActor) {
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);
}
};

View File

@@ -6,47 +6,32 @@ 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
* currentSeconds - current number of seconds since the epoch
* availableWidth - total width available for the item
*/
function DocDisplayItem(docInfo, currentSecs) {
this._init(docInfo, currentSecs);
function DocDisplayItem(docInfo, availableWidth) {
this._init(docInfo, availableWidth);
}
DocDisplayItem.prototype = {
__proto__: GenericDisplay.GenericDisplayItem.prototype,
_init : function(docInfo, currentSecs) {
GenericDisplay.GenericDisplayItem.prototype._init.call(this);
_init : function(docInfo, availableWidth) {
GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
this._docInfo = docInfo;
this._setItemInfo(docInfo.name, "");
this._timeoutTime = -1;
this._resetTimeDisplay(currentSecs);
},
//// Public methods ////
getUpdateTimeoutTime: function() {
return this._timeoutTime;
},
// Update any relative-time based displays for this item.
redisplay: function(currentSecs) {
this._resetTimeDisplay(currentSecs);
},
//// Public method overrides ////
// Opens a document represented by this display item.
@@ -61,9 +46,10 @@ DocDisplayItem.prototype = {
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);
// Ensures the preview icon is created.
_ensurePreviewIconCreated : function() {
if (!this._previewIcon)
this._previewIcon = this._docInfo.createIcon(GenericDisplay.PREVIEW_ICON_SIZE);
},
// Creates and returns a large preview icon, but only if this._docInfo is an image file
@@ -72,68 +58,54 @@ DocDisplayItem.prototype = {
if (this._docInfo.mimeType == null || this._docInfo.mimeType.indexOf("image/") != 0)
return null;
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);
return Shell.TextureCache.get_default().load_uri_sync(this._docInfo.uri, availableWidth, availableHeight);
}
};
/* 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() {
this._init();
}
function DocDisplay(width, height, numberOfColumns, columnGap) {
this._init(width, height, numberOfColumns, columnGap);
}
DocDisplay.prototype = {
__proto__: GenericDisplay.GenericDisplay.prototype,
_init : function() {
GenericDisplay.GenericDisplay.prototype._init.call(this);
_init : function(width, height, numberOfColumns, columnGap) {
GenericDisplay.GenericDisplay.prototype._init.call(this, width, height, numberOfColumns, columnGap);
let me = this;
// We keep a single timeout callback for updating last visited times
// for all the items in the display. This avoids creating individual
// callbacks for each item in the display. So proper time updates
// for individual items and item details depend on the item being
// associated with one of the displays.
this._updateTimeoutTargetTime = -1;
this._updateTimeoutId = 0;
this._docManager = DocInfo.getDocManager(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
this._recentManager = Gtk.RecentManager.get_default();
this._docsStale = true;
this._docManager.connect('changed', function(mgr, userData) {
this._recentManager.connect('changed', function(recentManager, 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 = this._docManager.getItems();
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._docsStale = false;
},
@@ -177,7 +149,7 @@ DocDisplay.prototype = {
let docA = this._allItems[itemIdA];
let docB = this._allItems[itemIdB];
return docB.timestamp - docA.timestamp;
return docB.lastVisited() - docA.lastVisited();
},
// Checks if the item info can be a match for the search string by checking
@@ -200,39 +172,8 @@ DocDisplay.prototype = {
// Creates a DocDisplayItem based on itemInfo, which is expected to be a DocInfo object.
_createDisplayItem: function(itemInfo) {
let currentSecs = new Date().getTime() / 1000;
let docDisplayItem = new DocDisplayItem(itemInfo, currentSecs);
this._updateTimeoutCallback(docDisplayItem, currentSecs);
return docDisplayItem;
},
//// Private Methods ////
// A callback function that redisplays the items, updating their descriptions,
// and sets up a new timeout callback.
_docTimeout: function () {
let currentSecs = new Date().getTime() / 1000;
this._updateTimeoutId = 0;
this._updateTimeoutTargetTime = -1;
for (let docId in this._displayedItems) {
let docDisplayItem = this._displayedItems[docId];
docDisplayItem.redisplay(currentSecs);
this._updateTimeoutCallback(docDisplayItem, currentSecs);
}
return false;
},
// Updates the timeout callback if the timeout time for the docDisplayItem
// is earlier than the target time for the current timeout callback.
_updateTimeoutCallback: function (docDisplayItem, currentSecs) {
let timeoutTime = docDisplayItem.getUpdateTimeoutTime();
if (this._updateTimeoutTargetTime < 0 || timeoutTime < this._updateTimeoutTargetTime) {
if (this._updateTimeoutId > 0)
Mainloop.source_remove(this._updateTimeoutId);
this._updateTimeoutId = Mainloop.timeout_add_seconds(timeoutTime - currentSecs, Lang.bind(this, this._docTimeout));
this._updateTimeoutTargetTime = timeoutTime;
}
}
return new DocDisplayItem(itemInfo, this._columnWidth);
}
};
Signals.addSignalMethods(DocDisplay.prototype);

View File

@@ -12,10 +12,8 @@ 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);
@@ -30,18 +28,17 @@ 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_TOP = 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 = DEFAULT_PADDING;
const PREVIEW_BOX_CORNER_RADIUS = 10;
const PREVIEW_BOX_SPACING = 4;
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;
@@ -49,25 +46,25 @@ 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() {
this._init();
function GenericDisplayItem(availableWidth) {
this._init(availableWidth);
}
GenericDisplayItem.prototype = {
_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 });
_init: function(availableWidth) {
this._availableWidth = availableWidth;
this.actor = new Clutter.Group({ reactive: true,
width: availableWidth,
height: ITEM_DISPLAY_HEIGHT });
this.actor._delegate = this;
this.actor.connect('button-release-event',
Lang.bind(this,
this.actor.connect('button-release-event',
Lang.bind(this,
function() {
// Activates the item by launching it
this.emit('activate');
@@ -77,57 +74,51 @@ GenericDisplayItem.prototype = {
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
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);
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);
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,
this._informationButton = Shell.TextureCache.get_default().load_uri_sync(infoIconUri,
INFORMATION_BUTTON_SIZE,
INFORMATION_BUTTON_SIZE);
this._informationButton.x = availableWidth - ITEM_DISPLAY_PADDING_RIGHT - INFORMATION_BUTTON_SIZE;
this._informationButton.y = ITEM_DISPLAY_HEIGHT / 2 - INFORMATION_BUTTON_SIZE / 2;
this._informationButton.reactive = true;
// 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._informationButton.connect('button-press-event',
Lang.bind(this,
function() {
return true;
}));
this._informationButton.connect('button-release-event',
Lang.bind(this,
function() {
// Selects the item by highlighting it and displaying its details
this.emit('select');
return true;
}));
this._informationButton.hide();
this.actor.add_actor(this._informationButton);
this._informationButton.lower_bottom();
this._name = null;
this._description = null;
this._icon = 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._previewIcon = null;
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 ////
@@ -162,32 +153,29 @@ GenericDisplayItem.prototype = {
// Shows the information button when the item was drawn under the mouse pointer.
onDrawnUnderPointer: function() {
this._informationButton.show();
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;
this._informationButton.forceShow(true);
}
else {
else
color = ITEM_DISPLAY_BACKGROUND_COLOR;
this._informationButton.forceShow(false);
}
this.actor.background_color = color;
this._bg.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
*/
createDetailsActor: function(availableWidth) {
* availableHeight - height available for displaying details
*/
createDetailsActor: function(availableWidth, availableHeight) {
let details = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PREVIEW_BOX_SPACING,
width: availableWidth });
@@ -202,7 +190,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,
@@ -211,15 +199,14 @@ GenericDisplayItem.prototype = {
text: this._description.text });
textDetails.append(detailsDescription, Big.BoxPackFlags.NONE);
this._detailsDescriptions.push(detailsDescription);
mainDetails.append(textDetails, Big.BoxPackFlags.EXPAND);
let previewIcon = this._createPreviewIcon();
let largePreviewIcon = this._createLargePreviewIcon(availableWidth, -1);
this._ensurePreviewIconCreated();
let largePreviewIcon = this._createLargePreviewIcon(availableWidth, Math.max(0, availableHeight - mainDetails.height - PREVIEW_BOX_SPACING));
if (previewIcon != null && largePreviewIcon == null) {
mainDetails.prepend(previewIcon, Big.BoxPackFlags.NONE);
if (this._previewIcon != null && largePreviewIcon == null) {
let previewIconClone = new Clutter.Clone({ source: this._previewIcon });
mainDetails.prepend(previewIconClone, Big.BoxPackFlags.NONE);
}
details.append(mainDetails, Big.BoxPackFlags.NONE);
@@ -233,13 +220,13 @@ GenericDisplayItem.prototype = {
return details;
},
// Destroys the item.
// Destoys the item.
destroy: function() {
this.actor.destroy();
},
//// Pure virtual public methods ////
// Performes an action associated with launching this item, such as opening a file or an application.
launch: function() {
throw new Error("Not implemented");
@@ -269,35 +256,33 @@ GenericDisplayItem.prototype = {
// and therefore should be responsible for distroying it
this._icon.destroy();
this._icon = null;
}
// This ensures we'll create a new previewIcon next time we need it
if (this._previewIcon != null) {
this._previewIcon.destroy();
this._previewIcon = null;
}
this._icon = this._createIcon();
this._iconBox.append(this._icon, Big.BoxPackFlags.NONE);
this.actor.add_actor(this._icon);
let textWidth = this._availableWidth - (ITEM_DISPLAY_ICON_SIZE + 4) - INFORMATION_BUTTON_SIZE - ITEM_DISPLAY_PADDING_RIGHT;
this._name = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans 14px",
width: textWidth,
ellipsize: Pango.EllipsizeMode.END,
text: nameText });
this._infoText.append(this._name, Big.BoxPackFlags.EXPAND);
text: nameText,
x: ITEM_DISPLAY_ICON_SIZE + 4,
y: ITEM_DISPLAY_PADDING_TOP });
this.actor.add_actor(this._name);
this._description = new Clutter.Text({ color: ITEM_DISPLAY_DESCRIPTION_COLOR,
font_name: "Sans 12px",
width: textWidth,
ellipsize: Pango.EllipsizeMode.END,
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;
}
}
text: descriptionText ? descriptionText : "",
x: this._name.x,
y: this._name.height + 4 });
this.actor.add_actor(this._description);
},
//// Virtual protected methods ////
@@ -314,18 +299,28 @@ GenericDisplayItem.prototype = {
throw new Error("Not implemented");
},
// Returns a preview icon for the item.
_createPreviewIcon: function() {
// Ensures the preview icon is created.
_ensurePreviewIconCreated: function() {
throw new Error("Not implemented");
},
//// Private methods ////
// Performs actions on mouse enter event for the item. Currently, shows the information button for the item.
_onEnter: function(actor, event) {
this._informationButton.show();
},
// Performs actions on mouse leave event for the item. Currently, hides the information button for the item.
_onLeave: function(actor, event) {
this._informationButton.hide();
},
// 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 we should remove the link manually.
this._informationButton.actor.hide();
this._informationButton.hide();
}
};
@@ -333,60 +328,82 @@ 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() {
this._init();
function GenericDisplay(width, height, numberOfColumns, columnGap) {
this._init(width, height, numberOfColumns, columnGap);
}
GenericDisplay.prototype = {
_init : function() {
_init : function(width, height, numberOfColumns, columnGap) {
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._list = new Shell.OverflowList({ spacing: 6.0,
item_height: ITEM_DISPLAY_HEIGHT });
this._grid = new Tidy.Grid({width: this._width, height: this._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._setDimensionsAndMaxItems(width, 0, height);
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._openDetailIndex = -1;
this._displayedItems = {};
this._displayedItemsCount = 0;
this._pageDisplayed = 0;
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 getNavigationArea.
this.actor = this._list;
// See also getSideArea.
this.actor = this._grid;
this.displayControl = new Big.Box({ background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
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, closing the overlay
// Launches the item that is currently selected and emits 'activated' signal.
activateSelected: function() {
if (this._selectedIndex != -1) {
let selected = this._findDisplayedByIndex(this._selectedIndex);
selected.launch();
this.unsetSelected();
Main.overlay.hide();
selected.launch()
this.emit('activated');
}
},
@@ -394,11 +411,10 @@ 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 = count - 1;
prev = this._displayedItemsCount - 1;
selectedUp = false;
}
this._selectIndex(prev);
@@ -409,10 +425,9 @@ 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 == count - 1) {
if (this._selectedIndex == this._displayedItemsCount - 1) {
next = 0;
selectedDown = false;
}
@@ -428,9 +443,8 @@ GenericDisplay.prototype = {
// Selects the last item among the displayed items.
selectLastItem: function() {
let count = this._list.displayedCount;
if (this.hasItems())
this._selectIndex(count - 1);
this._selectIndex(this._displayedItemsCount - 1);
},
// Returns true if the display has some item selected.
@@ -445,31 +459,48 @@ GenericDisplay.prototype = {
// Returns true if the display has any displayed items.
hasItems: function() {
return this._list.displayedCount > 0;
return this._displayedItemsCount > 0;
},
// Load the initial state
load: function() {
// 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();
this._redisplay(true);
},
// Should be called when the display is closed
resetState: function() {
// Hides the display actor.
hide: function() {
this._grid.hide();
this._filterReset();
this._openDetailIndex = -1;
this._removeAllDisplayItems();
},
// Returns an actor which acts as a sidebar; this is used for
// the applications category view
getNavigationArea: function () {
getSideArea: function () {
return null;
},
createDetailsForIndex: function(index, width, height) {
let item = this._findDisplayedByIndex(index);
return item.createDetailsActor(width, height);
},
//// Protected methods ////
/*
@@ -488,7 +519,9 @@ GenericDisplay.prototype = {
let hadSelected = this.hasSelected();
this._removeAllDisplayItems();
for (let i = 0; i < this._matchedItems.length; i++) {
for (let i = this._maxItemsPerPage * this._pageDisplayed; i < this._matchedItems.length && i < this._maxItemsPerPage * (this._pageDisplayed + 1); i++) {
this._addDisplayItem(this._matchedItems[i]);
}
@@ -497,9 +530,11 @@ 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 information button if it is.
@@ -528,46 +563,40 @@ GenericDisplay.prototype = {
let itemInfo = this._allItems[itemId];
let displayItem = this._createDisplayItem(itemInfo);
displayItem.connect('activate',
displayItem.connect('activate',
Lang.bind(this,
function() {
// update the selection
this._selectIndex(this._list.get_actor_index(displayItem.actor));
this._selectIndex(this._getIndexOfDisplayedActor(displayItem.actor));
this.activateSelected();
}));
displayItem.connect('show-details',
displayItem.connect('select',
Lang.bind(this,
function() {
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);
}
// update the selection
this._selectIndex(this._getIndexOfDisplayedActor(displayItem.actor));
}));
this._list.add_actor(displayItem.actor);
this._grid.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._list.get_actor_index(displayItem.actor);
let displayItemIndex = this._getIndexOfDisplayedActor(displayItem.actor);
if (this.hasSelected() && count == 1) {
if (this.hasSelected() && (this._displayedItemsCount == 1 || !this._grid.visible)) {
this.unsetSelected();
} else if (this.hasSelected() && displayItemIndex < this._selectedIndex) {
this.selectUp();
}
}
displayItem.destroy();
delete this._displayedItems[itemId];
this._displayedItemsCount--;
},
// Removes all displayed items.
@@ -598,6 +627,9 @@ 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();
@@ -605,7 +637,7 @@ GenericDisplay.prototype = {
this._doSearchFilter();
if (resetPage)
this._list.page = 0;
this._pageDisplayed = 0;
this._displayMatchedItems(true);
@@ -646,6 +678,27 @@ 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
@@ -695,12 +748,13 @@ GenericDisplay.prototype = {
return 1;
else
return this._compareItems(a, b);
}));
}));
},
// Displays the page specified by the pageNumber argument.
// Displays the page specified by the pageNumber argument. The pageNumber is 0-based.
_displayPage: function(pageNumber) {
this._list.page = pageNumber;
this._pageDisplayed = pageNumber;
this._displayMatchedItems(false);
},
/*
@@ -713,30 +767,30 @@ GenericDisplay.prototype = {
*/
_updateDisplayControl: function(resetDisplayControl) {
if (resetDisplayControl) {
this._selectedIndex = -1;
this.displayControl.remove_all();
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,
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,
font_name: "Sans Bold 16px",
text: (i+1) + "",
reactive: (i == pageNumber) ? false : true});
text: (pageNumber + 1) + "",
height: LABEL_HEIGHT,
reactive: (pageNumber == this._pageDisplayed) ? 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 = i;
let pageNumberLocalScope = pageNumber;
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._list.page) {
if (i == this._pageDisplayed) {
pageControlActor.color = DISPLAY_CONTROL_SELECTED_COLOR;
pageControlActor.reactive = false;
} else {
@@ -747,10 +801,11 @@ 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 actor = this._list.get_displayed_actor(index);
let displayedActors = this._grid.get_children();
let actor = displayedActors[index];
return this._findDisplayedByActor(actor);
},
@@ -766,31 +821,39 @@ 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 (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;
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();
}
// Cleanup from the previous item
if (this._selectedIndex >= 0) {
this._findDisplayedByIndex(this._selectedIndex).markSelected(false);
}
this._selectedIndex = index;
if (index < 0)
return
// Mark the new item as selected and create its details pane
let item = this._findDisplayedByIndex(index);
item.markSelected(true);
this.emit('selected');
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');
}
}
};

View File

@@ -1,589 +0,0 @@
/* -*- 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);

View File

@@ -13,7 +13,6 @@ 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;
@@ -26,7 +25,6 @@ let panel = null;
let sidebar = null;
let overlay = null;
let runDialog = null;
let lookingGlass = null;
let wm = null;
let recorder = null;
let inModal = false;
@@ -37,6 +35,7 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env("GNOME");
global.grab_dbus_service();
global.start_task_panel();
Tweener.init();
@@ -53,10 +52,17 @@ function start() {
global.connect('panel-run-dialog', function(panel) {
// Make sure not more than one run dialog is shown.
if (runDialog == null) {
if (!runDialog) {
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();
@@ -67,6 +73,11 @@ 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 });
}
@@ -77,8 +88,6 @@ 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));
@@ -139,14 +148,6 @@ 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;

View File

@@ -15,12 +15,28 @@ const GenericDisplay = imports.ui.genericDisplay;
const Link = imports.ui.link;
const Main = imports.ui.main;
const Panel = imports.ui.panel;
const Dash = imports.ui.dash;
const Tweener = imports.ui.tweener;
const Workspaces = imports.ui.workspaces;
const ROOT_OVERLAY_COLOR = new Clutter.Color();
ROOT_OVERLAY_COLOR.from_pixel(0x000000ff);
ROOT_OVERLAY_COLOR.from_pixel(0x000000bb);
// The factor to scale the overlay wallpaper with. This should not be less
// than 3/2, because the rule of thirds is used for positioning (see below).
const BACKGROUND_SCALE = 2;
const LABEL_HEIGHT = 16;
const DASH_MIN_WIDTH = 250;
const DASH_SECTION_PADDING = 6;
const DASH_SECTION_SPACING = 6;
const DASH_COLUMNS = 1;
const DASH_CORNER_RADIUS = 5;
// This is the height of section components other than the item display.
const DASH_SECTION_MISC_HEIGHT = (LABEL_HEIGHT + DASH_SECTION_SPACING) * 2 + DASH_SECTION_PADDING;
const DASH_SEARCH_BG_COLOR = new Clutter.Color();
DASH_SEARCH_BG_COLOR.from_pixel(0xffffffff);
const DASH_TEXT_COLOR = new Clutter.Color();
DASH_TEXT_COLOR.from_pixel(0xffffffff);
// Time for initial animation going into overlay mode
const ANIMATION_TIME = 0.25;
@@ -45,8 +61,6 @@ const ROWS_REGULAR_SCREEN = 8;
const COLUMNS_WIDE_SCREEN = 5;
const ROWS_WIDE_SCREEN = 10;
const DEFAULT_PADDING = 4;
// Padding around workspace grid / Spacing between Dash and Workspaces
const WORKSPACE_GRID_PADDING = 12;
@@ -61,6 +75,27 @@ const STATE_ACTIVE = true;
const STATE_PENDING_INACTIVE = false;
const STATE_INACTIVE = false;
// The dash has a slightly transparent blue background with a gradient.
const DASH_LEFT_COLOR = new Clutter.Color();
DASH_LEFT_COLOR.from_pixel(0x324c6fbb);
const DASH_MIDDLE_COLOR = new Clutter.Color();
DASH_MIDDLE_COLOR.from_pixel(0x324c6faa);
const DASH_RIGHT_COLOR = new Clutter.Color();
DASH_RIGHT_COLOR.from_pixel(0x324c6fcc);
const DASH_BORDER_COLOR = new Clutter.Color();
DASH_BORDER_COLOR.from_pixel(0x213b5dff);
const DASH_BORDER_WIDTH = 2;
// The results and details panes have a somewhat transparent blue background with a gradient.
const PANE_LEFT_COLOR = new Clutter.Color();
PANE_LEFT_COLOR.from_pixel(0x324c6ff4);
const PANE_MIDDLE_COLOR = new Clutter.Color();
PANE_MIDDLE_COLOR.from_pixel(0x324c6ffa);
const PANE_RIGHT_COLOR = new Clutter.Color();
PANE_RIGHT_COLOR.from_pixel(0x324c6ff4);
const SHADOW_COLOR = new Clutter.Color();
SHADOW_COLOR.from_pixel(0x00000033);
const TRANSPARENT_COLOR = new Clutter.Color();
@@ -74,6 +109,561 @@ let wideScreen = false;
let displayGridColumnWidth = null;
let displayGridRowHeight = null;
function SearchEntry(width) {
this._init(width);
}
SearchEntry.prototype = {
_init : function(width) {
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER,
background_color: DASH_SEARCH_BG_COLOR,
corner_radius: 4,
spacing: 4,
padding_left: 4,
padding_right: 4,
width: width,
height: 24
});
let icontheme = Gtk.IconTheme.get_default();
let searchIconTexture = new Clutter.Texture({});
let searchIconPath = icontheme.lookup_icon('gtk-find', 16, 0).get_filename();
searchIconTexture.set_from_file(searchIconPath);
this.actor.append(searchIconTexture, 0);
// 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.entry.connect('text-changed', Lang.bind(this, function (e) {
let text = this.entry.text;
}));
this.actor.append(this.entry, Big.BoxPackFlags.EXPAND);
}
};
function ItemResults(resultsWidth, resultsHeight, displayClass, labelText) {
this._init(resultsWidth, resultsHeight, displayClass, labelText);
}
ItemResults.prototype = {
_init: function(resultsWidth, resultsHeight, displayClass, labelText) {
this._resultsWidth = resultsWidth;
this._resultsHeight = resultsHeight;
this.actor = new Big.Box({ height: resultsHeight,
padding: DASH_SECTION_PADDING + DASH_BORDER_WIDTH,
spacing: DASH_SECTION_SPACING });
this._resultsText = new Clutter.Text({ color: DASH_TEXT_COLOR,
font_name: "Sans Bold 14px",
text: labelText });
this.actor.append(this._resultsText, Big.BoxPackFlags.NONE);
// LABEL_HEIGHT is the height of this._resultsText and GenericDisplay.LABEL_HEIGHT is the height
// of the display controls.
this._displayHeight = resultsHeight - LABEL_HEIGHT - GenericDisplay.LABEL_HEIGHT - DASH_SECTION_SPACING * 2;
this.display = new displayClass(resultsWidth, this._displayHeight, DASH_COLUMNS, DASH_SECTION_SPACING);
this.actor.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.END);
},
_setSearchMode: function() {
this.actor.height = this._resultsHeight / NUMBER_OF_SECTIONS_IN_SEARCH;
let displayHeight = this._displayHeight - this._resultsHeight * (NUMBER_OF_SECTIONS_IN_SEARCH - 1) / NUMBER_OF_SECTIONS_IN_SEARCH;
this.display.setExpanded(false, this._resultsWidth, 0, displayHeight, DASH_COLUMNS);
this.actor.remove_all();
this.actor.append(this._resultsText, Big.BoxPackFlags.NONE);
this.actor.append(this.display.actor, Big.BoxPackFlags.EXPAND);
this.actor.append(this.controlBox, Big.BoxPackFlags.END);
},
_unsetSearchMode: function() {
this.actor.height = this._resultsHeight;
this.display.setExpanded(false, this._resultsWidth, 0, this._displayHeight, DASH_COLUMNS);
this.actor.remove_all();
this.actor.append(this._resultsText, Big.BoxPackFlags.NONE);
this.actor.append(this.display.actor, Big.BoxPackFlags.EXPAND);
this.actor.append(this.controlBox, Big.BoxPackFlags.END);
}
}
function Dash() {
this._init();
}
Dash.prototype = {
_init : function() {
let me = this;
this._moreAppsMode = false;
this._moreDocsMode = false;
this._width = displayGridColumnWidth;
this._displayWidth = displayGridColumnWidth - DASH_SECTION_PADDING * 2;
this._resultsWidth = displayGridColumnWidth;
this._detailsWidth = displayGridColumnWidth * 2;
let bottomHeight = DASH_SECTION_PADDING;
let global = Shell.Global.get();
let resultsHeight = global.screen_height - Panel.PANEL_HEIGHT - DASH_SECTION_PADDING - bottomHeight;
let detailsHeight = global.screen_height - Panel.PANEL_HEIGHT - DASH_SECTION_PADDING - bottomHeight;
// The whole dash group needs to be reactive so that the clicks are not passed to the transparent background underneath it.
// This background is used in the workspaces area when the additional dash panes are being shown. It handles clicks in the
// workspaces area by closing these additional dash panes and revealing all workspaces.
this.actor = new Clutter.Group({reactive: true});
this.actor.height = global.screen_height;
let dashPane = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x: 0,
y: Panel.PANEL_HEIGHT + DASH_SECTION_PADDING,
width: this._width + SHADOW_WIDTH,
height: global.screen_height - Panel.PANEL_HEIGHT - DASH_SECTION_PADDING - bottomHeight});
let dashBackground = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
width: this._width,
height: global.screen_height - Panel.PANEL_HEIGHT - DASH_SECTION_PADDING - bottomHeight,
corner_radius: DASH_CORNER_RADIUS,
border: DASH_BORDER_WIDTH,
border_color: DASH_BORDER_COLOR });
dashPane.append(dashBackground, Big.BoxPackFlags.EXPAND);
let dashLeft = global.create_horizontal_gradient(DASH_LEFT_COLOR,
DASH_MIDDLE_COLOR);
let dashRight = global.create_horizontal_gradient(DASH_MIDDLE_COLOR,
DASH_RIGHT_COLOR);
let dashShadow = global.create_horizontal_gradient(SHADOW_COLOR,
TRANSPARENT_COLOR);
dashShadow.set_width(SHADOW_WIDTH);
dashBackground.append(dashLeft, Big.BoxPackFlags.EXPAND);
dashBackground.append(dashRight, Big.BoxPackFlags.EXPAND);
dashPane.append(dashShadow, Big.BoxPackFlags.NONE);
this.actor.add_actor(dashPane);
this._searchEntry = new SearchEntry(this._width - DASH_SECTION_PADDING * 2 - DASH_BORDER_WIDTH * 2);
this.actor.add_actor(this._searchEntry.actor);
this._searchEntry.actor.set_position(DASH_SECTION_PADDING + DASH_BORDER_WIDTH, dashPane.y + DASH_SECTION_PADDING + DASH_BORDER_WIDTH);
this._searchQueued = false;
this._searchEntry.entry.connect('text-changed', function (se, prop) {
if (me._searchQueued)
return;
me._searchQueued = true;
Mainloop.timeout_add(250, function() {
// Strip leading and trailing whitespace
let text = me._searchEntry.entry.text.replace(/^\s+/g, "").replace(/\s+$/g, "");
me._searchQueued = false;
me._resultsAppsSection.display.setSearch(text);
me._resultsDocsSection.display.setSearch(text);
if (text == '')
me._unsetSearchMode();
else
me._setSearchMode();
return false;
});
});
this._searchEntry.entry.connect('activate', function (se) {
// only one of the displays will have an item selected, so it's ok to
// call activateSelected() on all of them
me._docDisplay.activateSelected();
me._resultsAppsSection.display.activateSelected();
me._resultsDocsSection.display.activateSelected();
return true;
});
this._searchEntry.entry.connect('key-press-event', 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 (me._searchEntry.entry.text != '')
me._searchEntry.entry.text = '';
// Next, if we're in one of the "more" modes or showing the details pane, close them
else if (me._moreAppsMode || me._moreDocsMode || me._detailsShowing())
me.unsetMoreMode();
// Finally, just close the overlay entirely
else
me.emit('activated');
return true;
} else if (symbol == Clutter.Up) {
// 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.
// TODO: add the right logic
} else if (symbol == Clutter.Down) {
// TODO: add the right logic
}
return false;
});
this._appsText = new Clutter.Text({ color: DASH_TEXT_COLOR,
font_name: "Sans Bold 14px",
text: "Applications",
height: LABEL_HEIGHT});
this._appsSection = new Big.Box({ x: DASH_SECTION_PADDING,
y: this._searchEntry.actor.y + this._searchEntry.actor.height + DASH_SECTION_PADDING,
padding_top: DASH_SECTION_PADDING,
spacing: DASH_SECTION_SPACING});
this._appsSection.append(this._appsText, Big.BoxPackFlags.EXPAND);
this._itemDisplayHeight = global.screen_height - this._appsSection.y - DASH_SECTION_MISC_HEIGHT * 2 - bottomHeight;
this._appsContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
this._appsSection.append(this._appsContent, Big.BoxPackFlags.EXPAND);
this._appWell = new AppDisplay.AppWell(this._displayWidth);
this._appWell.actor.show();
this._appsContent.append(this._appWell.actor, Big.BoxPackFlags.EXPAND);
let moreAppsBox = new Big.Box({x_align: Big.BoxAlignment.END});
this._moreAppsLink = new Link.Link({ color: DASH_TEXT_COLOR,
font_name: "Sans Bold 14px",
text: "More...",
height: LABEL_HEIGHT });
moreAppsBox.append(this._moreAppsLink.actor, Big.BoxPackFlags.EXPAND);
this._appsSection.append(moreAppsBox, Big.BoxPackFlags.EXPAND);
this.actor.add_actor(this._appsSection);
this._appsSectionDefaultHeight = this._appsSection.height;
this._docsSection = new Big.Box({ x: DASH_SECTION_PADDING,
y: this._appsSection.y + this._appsSection.height,
padding_top: DASH_SECTION_PADDING,
spacing: DASH_SECTION_SPACING});
this._docsText = new Clutter.Text({ color: DASH_TEXT_COLOR,
font_name: "Sans Bold 14px",
text: "Recent Documents",
height: LABEL_HEIGHT});
this._docsSection.append(this._docsText, Big.BoxPackFlags.EXPAND);
this._docDisplay = new DocDisplay.DocDisplay(this._displayWidth, this._itemDisplayHeight - this._appsContent.height, DASH_COLUMNS, DASH_SECTION_PADDING);
this._docsSection.append(this._docDisplay.actor, Big.BoxPackFlags.EXPAND);
let moreDocsBox = new Big.Box({x_align: Big.BoxAlignment.END});
this._moreDocsLink = new Link.Link({ color: DASH_TEXT_COLOR,
font_name: "Sans Bold 14px",
text: "More...",
height: LABEL_HEIGHT });
moreDocsBox.append(this._moreDocsLink.actor, Big.BoxPackFlags.EXPAND);
this._docsSection.append(moreDocsBox, Big.BoxPackFlags.EXPAND);
this.actor.add_actor(this._docsSection);
this._docsSectionDefaultHeight = this._docsSection.height;
// The "More" or search results area
this._resultsAppsSection = new ItemResults(this._displayWidth, resultsHeight, AppDisplay.AppDisplay, "Applications");
this._resultsDocsSection = new ItemResults(this._displayWidth, resultsHeight, DocDisplay.DocDisplay, "Documents");
this._resultsPane = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x: this._width,
y: Panel.PANEL_HEIGHT + DASH_SECTION_PADDING,
width: this._resultsWidth + SHADOW_WIDTH,
height: resultsHeight });
let resultsBackground = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
width: this._resultsWidth,
height: resultsHeight,
corner_radius: DASH_CORNER_RADIUS,
border: DASH_BORDER_WIDTH,
border_color: DASH_BORDER_COLOR });
this._resultsPane.append(resultsBackground, Big.BoxPackFlags.EXPAND);
let resultsLeft = global.create_horizontal_gradient(PANE_LEFT_COLOR,
PANE_MIDDLE_COLOR);
let resultsRight = global.create_horizontal_gradient(PANE_MIDDLE_COLOR,
PANE_RIGHT_COLOR);
let resultsShadow = global.create_horizontal_gradient(SHADOW_COLOR,
TRANSPARENT_COLOR);
resultsShadow.set_width(SHADOW_WIDTH);
resultsBackground.append(resultsLeft, Big.BoxPackFlags.EXPAND);
resultsBackground.append(resultsRight, Big.BoxPackFlags.EXPAND);
this._resultsPane.append(resultsShadow, Big.BoxPackFlags.NONE);
this.actor.add_actor(this._resultsPane);
this._resultsPane.hide();
this._detailsPane = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x: this._width,
y: Panel.PANEL_HEIGHT + DASH_SECTION_PADDING,
width: this._detailsWidth + SHADOW_WIDTH,
height: detailsHeight });
this._firstSelectAfterOverlayShow = true;
let detailsBackground = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
width: this._detailsWidth,
height: detailsHeight,
corner_radius: DASH_CORNER_RADIUS,
border: DASH_BORDER_WIDTH,
border_color: DASH_BORDER_COLOR });
this._detailsPane.append(detailsBackground, Big.BoxPackFlags.EXPAND);
let detailsLeft = global.create_horizontal_gradient(PANE_LEFT_COLOR,
PANE_MIDDLE_COLOR);
let detailsRight = global.create_horizontal_gradient(PANE_MIDDLE_COLOR,
PANE_RIGHT_COLOR);
let detailsShadow = global.create_horizontal_gradient(SHADOW_COLOR,
TRANSPARENT_COLOR);
detailsShadow.set_width(SHADOW_WIDTH);
detailsBackground.append(detailsLeft, Big.BoxPackFlags.EXPAND);
detailsBackground.append(detailsRight, Big.BoxPackFlags.EXPAND);
this._detailsPane.append(detailsShadow, Big.BoxPackFlags.NONE);
this._detailsContent = new Big.Box({ padding: DASH_SECTION_PADDING + DASH_BORDER_WIDTH });
this._detailsPane.add_actor(this._detailsContent);
this.actor.add_actor(this._detailsPane);
this._detailsPane.hide();
let itemDetailsAvailableWidth = this._detailsWidth - DASH_SECTION_PADDING * 2 - DASH_BORDER_WIDTH * 2;
let itemDetailsAvailableHeight = detailsHeight - DASH_SECTION_PADDING * 2 - DASH_BORDER_WIDTH * 2;
this._docDisplay.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
this._resultsAppsSection.display.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
this._resultsDocsSection.display.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
/* Proxy the activated signals */
this._appWell.connect('activated', function(well) {
me.emit('activated');
});
this._docDisplay.connect('activated', function(docDisplay) {
me.emit('activated');
});
this._resultsAppsSection.display.connect('activated', function(resultsAppsDisplay) {
me.emit('activated');
});
this._resultsDocsSection.display.connect('activated', function(resultsDocsDisplay) {
me.emit('activated');
});
this._docDisplay.connect('selected', function(docDisplay) {
me._resultsDocsSection.display.unsetSelected();
me._resultsAppsSection.display.unsetSelected();
if (!me._detailsShowing()) {
me._detailsPane.show();
me.emit('panes-displayed');
}
me._detailsContent.remove_all();
me._detailsContent.append(me._docDisplay.selectedItemDetails, Big.BoxPackFlags.NONE);
});
this._resultsDocsSection.display.connect('selected', function(resultsDocDisplay) {
me._docDisplay.unsetSelected();
me._resultsAppsSection.display.unsetSelected();
if (!me._detailsShowing()) {
me._detailsPane.show();
me.emit('panes-displayed');
}
me._detailsContent.remove_all();
me._detailsContent.append(me._resultsDocsSection.display.selectedItemDetails, Big.BoxPackFlags.NONE);
});
this._resultsAppsSection.display.connect('selected', function(resultsAppDisplay) {
me._docDisplay.unsetSelected();
me._resultsDocsSection.display.unsetSelected();
if (!me._detailsShowing()) {
me._detailsPane.show();
me.emit('panes-displayed');
}
me._detailsContent.remove_all();
me._detailsContent.append(me._resultsAppsSection.display.selectedItemDetails, Big.BoxPackFlags.NONE);
});
this._moreAppsLink.connect('clicked',
function(o, event) {
if (me._moreAppsMode) {
me._unsetMoreAppsMode();
} else {
me._setMoreAppsMode();
}
});
this._moreDocsLink.connect('clicked',
function(o, event) {
if (me._moreDocsMode) {
me._unsetMoreDocsMode();
} else {
me._setMoreDocsMode();
}
});
},
show: function() {
let global = Shell.Global.get();
this._appsContent.show();
this._docDisplay.show();
global.stage.set_key_focus(this._searchEntry.entry);
},
hide: function() {
this._firstSelectAfterOverlayShow = true;
this._appsContent.hide();
this._docDisplay.hide();
this._searchEntry.entry.text = '';
this.unsetMoreMode();
},
unsetMoreMode: function() {
this._unsetMoreAppsMode();
this._unsetMoreDocsMode();
if (this._detailsShowing()) {
this._detailsPane.hide();
this.emit('panes-removed');
}
this._unsetSearchMode();
},
// Sets the 'More' mode for browsing applications.
_setMoreAppsMode: function() {
if (this._moreAppsMode)
return;
this._unsetMoreDocsMode();
this._unsetSearchMode();
this._moreAppsMode = true;
this._resultsAppsSection.display.show();
this._resultsPane.add_actor(this._resultsAppsSection.actor);
this._resultsPane.show();
this._moreAppsLink.setText("Less...");
this._detailsPane.x = this._width + this._resultsWidth;
this.emit('panes-displayed');
},
// Unsets the 'More' mode for browsing applications.
_unsetMoreAppsMode: function() {
if (!this._moreAppsMode)
return;
this._moreAppsMode = false;
this._resultsPane.remove_actor(this._resultsAppsSection.actor);
this._resultsAppsSection.display.hide();
this._resultsPane.hide();
this._moreAppsLink.setText("More...");
this._detailsPane.x = this._width;
if (!this._detailsShowing()) {
this.emit('panes-removed');
}
},
// Sets the 'More' mode for browsing documents.
_setMoreDocsMode: function() {
if (this._moreDocsMode)
return;
this._unsetMoreAppsMode();
this._unsetSearchMode();
this._moreDocsMode = true;
this._resultsDocsSection.display.show();
this._resultsPane.add_actor(this._resultsDocsSection.actor);
this._resultsPane.show();
this._moreDocsLink.setText("Less...");
this._detailsPane.x = this._width + this._resultsWidth;
this.emit('panes-displayed');
},
// Unsets the 'More' mode for browsing documents.
_unsetMoreDocsMode: function() {
if (!this._moreDocsMode)
return;
this._moreDocsMode = false;
this._resultsPane.hide();
this._resultsPane.remove_actor(this._resultsDocsSection.actor);
this._resultsDocsSection.display.hide();
this._moreDocsLink.setText("More...");
this._detailsPane.x = this._width;
if (!this._detailsShowing()) {
this.emit('panes-removed');
}
},
_setSearchMode: function() {
if (this._resultsShowing())
return;
this._resultsAppsSection._setSearchMode();
this._resultsAppsSection.display.show();
this._resultsPane.add_actor(this._resultsAppsSection.actor);
this._resultsDocsSection._setSearchMode();
this._resultsDocsSection.display.show();
this._resultsPane.add_actor(this._resultsDocsSection.actor);
this._resultsDocsSection.actor.set_y(this._resultsAppsSection.actor.height);
this._resultsPane.show();
this._detailsPane.x = this._width + this._resultsWidth;
this.emit('panes-displayed');
},
_unsetSearchMode: function() {
if (this._moreDocsMode || this._moreAppsMode || !this._resultsShowing())
return;
this._resultsPane.hide();
this._resultsPane.remove_actor(this._resultsAppsSection.actor);
this._resultsAppsSection.display.hide();
this._resultsAppsSection._unsetSearchMode();
this._resultsPane.remove_actor(this._resultsDocsSection.actor);
this._resultsDocsSection.display.hide();
this._resultsDocsSection._unsetSearchMode();
this._resultsDocsSection.actor.set_y(0);
this._detailsPane.x = this._width;
if (!this._detailsShowing()) {
this.emit('panes-removed');
}
},
_detailsShowing: function() {
return this._detailsPane.visible;
},
_resultsShowing: function() {
return this._resultsPane.visible;
}
};
Signals.addSignalMethods(Dash.prototype);
function Overlay() {
this._init();
}
@@ -84,61 +674,10 @@ Overlay.prototype = {
let global = Shell.Global.get();
this._group = new Clutter.Group();
this._group._delegate = this;
this.visible = false;
this.animationInProgress = false;
this._hideInProgress = false;
this._recalculateGridSizes();
this._activeDisplayPane = null;
// Used to catch any clicks when we have an active pane; see the comments
// in addPane below.
this._transparentBackground = new Clutter.Rectangle({ opacity: 0,
reactive: true });
this._group.add_actor(this._transparentBackground);
// Background color for the overlay
this._backOver = new Clutter.Rectangle({ color: ROOT_OVERLAY_COLOR });
this._group.add_actor(this._backOver);
this._group.hide();
global.overlay_group.add_actor(this._group);
// TODO - recalculate everything when desktop size changes
this._dash = new Dash.Dash(displayGridColumnWidth);
this._group.add_actor(this._dash.actor);
// Container to hold popup pane chrome.
this._paneContainer = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 6
});
// Note here we explicitly don't set the paneContainer to be reactive yet; that's done
// inside the notify::visible handler on panes.
this._paneContainer.connect('button-release-event', Lang.bind(this, function(background) {
this._activeDisplayPane.close();
return true;
}));
this._group.add_actor(this._paneContainer);
this._transparentBackground.lower_bottom();
this._paneContainer.lower_bottom();
this._repositionChildren();
this._workspaces = null;
},
_recalculateGridSizes: function () {
let global = Shell.Global.get();
wideScreen = (global.screen_width/global.screen_height > WIDE_SCREEN_CUT_OFF_RATIO);
// We divide the screen into an imaginary grid which helps us determine the layout of
// different visual components.
// different visual components.
if (wideScreen) {
displayGridColumnWidth = global.screen_width / COLUMNS_WIDE_SCREEN;
displayGridRowHeight = global.screen_height / ROWS_WIDE_SCREEN;
@@ -146,73 +685,87 @@ Overlay.prototype = {
displayGridColumnWidth = global.screen_width / COLUMNS_REGULAR_SCREEN;
displayGridRowHeight = global.screen_height / ROWS_REGULAR_SCREEN;
}
},
_repositionChildren: function () {
let global = Shell.Global.get();
this._group = new Clutter.Group();
this._group._delegate = this;
let contentHeight = global.screen_height - Panel.PANEL_HEIGHT;
this.visible = false;
this._hideInProgress = false;
this._dash.actor.set_position(0, Panel.PANEL_HEIGHT);
this._dash.actor.set_size(displayGridColumnWidth, contentHeight);
// A scaled root pixmap actor is used as a background. It is zoomed in
// to the lower right intersection of the lines that divide the image
// evenly in a 3x3 grid. This is based on the rule of thirds, a
// compositional rule of thumb in visual arts. The choice for the
// lower right point is based on a quick survey of GNOME wallpapers.
let background = global.create_root_pixmap_actor();
background.width = global.screen_width * BACKGROUND_SCALE;
background.height = global.screen_height * BACKGROUND_SCALE;
background.x = -global.screen_width * (4 * BACKGROUND_SCALE - 3) / 6;
background.y = -global.screen_height * (4 * BACKGROUND_SCALE - 3) / 6;
this._group.add_actor(background);
this._backOver.set_position(0, Panel.PANEL_HEIGHT);
this._backOver.set_size(global.screen_width, contentHeight);
// Transparent background is used to catch clicks outside of the dash panes when the panes
// are being displayed and the workspaces area should not be reactive. Catching such a
// click results in the panes being closed and the workspaces area becoming reactive again.
this._transparentBackground = new Clutter.Rectangle({ opacity: 0,
width: global.screen_width,
height: global.screen_height - Panel.PANEL_HEIGHT,
y: Panel.PANEL_HEIGHT,
reactive: true });
this._group.add_actor(this._transparentBackground);
this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING,
Panel.PANEL_HEIGHT);
// Dynamic width
this._paneContainer.height = contentHeight;
// Draw a semitransparent rectangle over the background for readability.
let backOver = new Clutter.Rectangle({ color: ROOT_OVERLAY_COLOR,
width: global.screen_width,
height: global.screen_height - Panel.PANEL_HEIGHT,
y: Panel.PANEL_HEIGHT });
this._group.add_actor(backOver);
this._transparentBackground.set_position(this._paneContainer.x, this._paneContainer.y);
this._transparentBackground.set_size(global.screen_width - this._paneContainer.x,
this._paneContainer.height);
},
this._group.hide();
global.overlay_group.add_actor(this._group);
addPane: function (pane) {
pane.actor.width = displayGridColumnWidth * 2;
this._paneContainer.append(pane.actor, Big.BoxPackFlags.NONE);
// When a pane is displayed, we raise the transparent background to the top
// and connect to button-release-event on it, then raise the pane above that.
// The idea here is that clicking anywhere outside the pane should close it.
// When the active pane is closed, undo the effect.
let backgroundEventId = null;
pane.connect('open-state-changed', Lang.bind(this, function (pane, isOpen) {
if (isOpen) {
this._activeDisplayPane = pane;
this._transparentBackground.raise_top();
this._paneContainer.raise_top();
if (backgroundEventId != null)
this._transparentBackground.disconnect(backgroundEventId);
backgroundEventId = this._transparentBackground.connect('button-release-event', Lang.bind(this, function () {
this._activeDisplayPane.close();
// TODO - recalculate everything when desktop size changes
this._dash = new Dash();
this._group.add_actor(this._dash.actor);
this._workspaces = null;
this._buttonEventHandlerId = null;
this._dash.connect('activated', function(dash) {
// TODO - have some sort of animation/effect while
// transitioning to the new app. We definitely need
// startup-notification integration at least.
me.hide();
});
this._dash.connect('panes-displayed', function(dash) {
if (me._buttonEventHandlerId == null) {
me._transparentBackground.raise_top();
me._dash.actor.raise_top();
me._buttonEventHandlerId = me._transparentBackground.connect('button-release-event', function(background) {
me._dash.unsetMoreMode();
return true;
}));
} else if (pane == this._activeDisplayPane) {
this._activeDisplayPane = null;
if (backgroundEventId != null) {
this._transparentBackground.disconnect(backgroundEventId);
backgroundEventId = null;
}
this._transparentBackground.lower_bottom();
this._paneContainer.lower_bottom();
});
}
});
this._dash.connect('panes-removed', function(dash) {
if (me._buttonEventHandlerId != null) {
me._transparentBackground.lower_bottom();
me._transparentBackground.disconnect(me._buttonEventHandlerId);
me._buttonEventHandlerId = null;
}
}));
});
},
//// Draggable target interface ////
// Closes any active panes if a GenericDisplayItem is being
// Unsets the expanded display mode if a GenericDisplayItem is being
// dragged over the overlay, i.e. as soon as it starts being dragged.
// This allows the user to place the item on any workspace.
// This closes the additional panes and allows the user to place
// the item on any workspace.
handleDragOver : function(source, actor, x, y, time) {
if (source instanceof GenericDisplay.GenericDisplayItem
|| source instanceof AppDisplay.WellDisplayItem) {
if (this._activeDisplayPane != null)
this._activeDisplayPane.close();
if (source instanceof GenericDisplay.GenericDisplayItem) {
this._dash.unsetMoreMode();
return true;
}
return false;
},
@@ -225,7 +778,6 @@ Overlay.prototype = {
return;
this.visible = true;
this.animationInProgress = true;
let global = Shell.Global.get();
let screenWidth = global.screen_width;
@@ -293,11 +845,8 @@ Overlay.prototype = {
let global = Shell.Global.get();
this.animationInProgress = true;
this._hideInProgress = true;
if (this._activeDisplayPane != null)
this._activeDisplayPane.close();
// lower the panes, so that workspaces display is on top while sliding out
// lower the Dash, so that workspaces display is on top and covers the Dash while it is sliding out
this._dash.actor.lower(this._workspaces.actor);
this._workspaces.hide();
@@ -330,21 +879,6 @@ Overlay.prototype = {
this.show();
},
/**
* activateWindow:
* @metaWindow: A #MetaWindow
* @time: Event timestamp integer
*
* Make the given MetaWindow be the focus window, switching
* to the workspace it's on if necessary. This function
* should only be used when the overlay is currently active;
* outside of that, use the relevant methods on MetaDisplay.
*/
activateWindow: function (metaWindow, time) {
this._workspaces.activateWindowFromOverlay(metaWindow, time);
this.hide();
},
//// Private methods ////
// Raises the Dash to the top, so that we can tell if the pointer is above one of its items.
@@ -362,8 +896,6 @@ Overlay.prototype = {
this._dash.actor.raise_top();
this._dash.actor.remove_clip();
this.animationInProgress = false;
this.emit('shown');
},
@@ -380,7 +912,6 @@ Overlay.prototype = {
this._group.hide();
this.visible = false;
this.animationInProgress = false;
this._hideInProgress = false;
Main.endModal();

View File

@@ -7,36 +7,35 @@ 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 = 26;
const PANEL_HEIGHT = 32;
const TRAY_HEIGHT = PANEL_HEIGHT - 1;
const SHADOW_HEIGHT = 6;
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);
// 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 SHADOW_COLOR = new Clutter.Color();
SHADOW_COLOR.from_pixel(0x00000033);
const TRANSPARENT_COLOR = new Clutter.Color();
TRANSPARENT_COLOR.from_pixel(0x00000000);
// Don't make the mouse hover effect visible to the user for a menu feel.
// Darken (pressed) buttons; lightening has no effect on white backgrounds.
const PANEL_BUTTON_COLOR = new Clutter.Color();
PANEL_BUTTON_COLOR.from_pixel(0x00000000);
// Lighten pressed buttons; darkening has no effect on a black background.
PANEL_BUTTON_COLOR.from_pixel(0x00000015);
const PRESSED_BUTTON_BACKGROUND_COLOR = new Clutter.Color();
PRESSED_BUTTON_BACKGROUND_COLOR.from_pixel(0x324c6ffa);
const DEFAULT_FONT = 'Sans 16px';
PRESSED_BUTTON_BACKGROUND_COLOR.from_pixel(0x00000030);
const TRAY_PADDING = 0;
// See comments around _recomputeTraySize
const TRAY_SPACING = 14;
const TRAY_SPACING_MIN = 8;
const TRAY_SPACING = 2;
// Used for the tray icon container with gtk pre-2.16, which doesn't
// fully support tray icon transparency
@@ -58,11 +57,26 @@ Panel.prototype = {
// Put the background under the panel within a group.
this.actor = new Clutter.Group();
// 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 });
// 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);
this.actor.add_actor(backBox);
let box = new Big.Box({ x: 0,
@@ -72,35 +86,14 @@ Panel.prototype = {
orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 4 });
this.button = new Button.Button("Activities", PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR, PANEL_FOREGROUND_COLOR, true, null, PANEL_HEIGHT, DEFAULT_FONT);
this.button = new Button.Button("Activities", PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR, true, null, PANEL_HEIGHT);
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,
PANEL_FOREGROUND_COLOR,
let statusbutton = new Button.Button(statusbox, PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR,
true, null, PANEL_HEIGHT);
statusbutton.button.connect('button-press-event', function (b, e) {
statusmenu.toggle(e);
@@ -112,14 +105,15 @@ Panel.prototype = {
statusbutton.release();
});
this._clock = new Clutter.Text({ font_name: DEFAULT_FONT,
color: PANEL_FOREGROUND_COLOR,
this._clock = new Clutter.Text({ font_name: "Sans Bold 16px",
text: "" });
let clockbox = new Big.Box({ y_align: Big.BoxAlignment.CENTER,
let pad = (PANEL_HEIGHT - this._clock.height) / 2;
let clockbox = new Big.Box({ padding_top: pad,
padding_bottom: pad,
padding_left: 4,
padding_right: 4 });
clockbox.append(this._clock, Big.BoxPackFlags.NONE);
backBox.append(clockbox, Big.BoxPackFlags.EXPAND);
box.append(clockbox, Big.BoxPackFlags.END);
// The tray icons live in trayBox within trayContainer.
// The trayBox is hidden when there are no tray icons.
@@ -130,7 +124,6 @@ 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
@@ -147,21 +140,19 @@ Panel.prototype = {
this._traymanager = new Shell.TrayManager({ bg_color: TRAY_BACKGROUND_COLOR });
this._traymanager.connect('tray-icon-added',
Lang.bind(this, function(o, icon) {
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',
Lang.bind(this, function(o, icon) {
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.)
@@ -185,26 +176,6 @@ 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() +
@@ -213,15 +184,8 @@ Panel.prototype = {
displayDate.setMinutes(displayDate.getMinutes() + 1);
msecRemaining += 60000;
}
this._clock.set_text(displayDate.toLocaleFormat("%a %l:%M %p"));
this._clock.set_text(displayDate.toLocaleFormat("%a %b %e, %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;
}
}
};

View File

@@ -1,159 +0,0 @@
/* -*- 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);

View File

@@ -1,9 +1,6 @@
/* -*- 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;
@@ -29,19 +26,6 @@ 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 });
@@ -59,12 +43,11 @@ RunDialog.prototype = {
(global.screen_height - BOX_HEIGHT) / 2);
this._group.add_actor(boxGroup);
let box = new Big.Box({ background_color: BOX_BACKGROUND_COLOR,
corner_radius: 4,
reactive: false,
width: BOX_WIDTH,
height: BOX_HEIGHT
});
let box = new Clutter.Rectangle({ color: BOX_BACKGROUND_COLOR,
reactive: false,
width: BOX_WIDTH,
height: BOX_HEIGHT,
border_width: 0 });
boxGroup.add_actor(box);
let label = new Clutter.Text({ color: BOX_TEXT_COLOR,
@@ -85,26 +68,20 @@ RunDialog.prototype = {
this._entry.set_position(6, 30);
boxGroup.add_actor(this._entry);
this._entry.connect('activate', Lang.bind(this, function (o, e) {
this._run(o.get_text());
this.close();
let me = this;
this._entry.connect('activate', function (o, e) {
me.hide();
me._run(o.get_text());
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) {
let f = this._internalCommands[command];
if (f) {
f();
if (command == 'restart') {
let global = Shell.Global.get();
global.reexec_self();
} else if (command) {
var p = new Shell.Process({'args' : [command]});
try {
@@ -114,32 +91,47 @@ RunDialog.prototype = {
log('Could not run command ' + command + '.');
}
}
this.emit('run');
},
open : function() {
if (this._isOpen) // Already shown
return;
show : function() {
let me = this;
if (this._group.visible) // Already shown
return false;
if (!Main.startModal())
return;
this._isOpen = true;
this._group.show();
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;
});
let global = Shell.Global.get();
global.stage.set_key_focus(this._entry);
return true;
},
close : function() {
if (!this._isOpen)
hide : function() {
if (!this._group.visible)
return;
this._isOpen = false;
this._group.hide();
this._entry.text = '';
Main.endModal();
},
destroy : function(){
this.hide();
this._group.destroy();
}
};
Signals.addSignalMethods(RunDialog.prototype);

View File

@@ -14,12 +14,10 @@ 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
// (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 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 maximum height of the sidebar would be extending from just
// below the panel to just above the taskbar. Since the taskbar is
@@ -28,6 +26,13 @@ const SIDEBAR_EXPANDED_WIDTH = Widget.EXPANDED_WIDTH + 3 * 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();
}
@@ -43,6 +48,7 @@ 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,
@@ -52,38 +58,23 @@ Sidebar.prototype = {
// during the animation.
this.box = new Big.Box ({ padding_top: SIDEBAR_PADDING,
padding_bottom: SIDEBAR_PADDING,
padding_right: 0,
padding_right: SIDEBAR_PADDING,
padding_left: 0,
spacing: SIDEBAR_SPACING });
this.actor.add_actor(this.box);
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._visible = this.expanded = true;
this._widgets = [];
this.addWidget(new ToggleWidget());
let default_widgets = this._gconf.get_string_list("sidebar/widgets");
this.addWidget(new ToggleWidget(this));
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, this._expanded);
widgetBox = new WidgetBox.WidgetBox(widget);
} catch(e) {
logError(e, "Failed to add widget '" + widget + "'");
return;
@@ -93,32 +84,18 @@ Sidebar.prototype = {
this._widgets.push(widgetBox);
},
_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);
show: function() {
this._visible = true;
this.actor.show();
},
_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();
hide: function() {
this._visible = false;
this.actor.hide();
},
_expand: function() {
this._expanded = true;
expand: function() {
this.expanded = true;
for (let i = 0; i < this._widgets.length; i++)
this._widgets[i].expand();
@@ -129,8 +106,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();
@@ -153,33 +130,24 @@ Sidebar.prototype = {
const LEFT_DOUBLE_ARROW = "\u00AB";
const RIGHT_DOUBLE_ARROW = "\u00BB";
function ToggleWidget() {
this._init();
function ToggleWidget(sidebar) {
this._init(sidebar);
}
ToggleWidget.prototype = {
__proto__ : Widget.Widget.prototype,
_init : function() {
this._gconf = Shell.GConf.get_default();
_init : function(sidebar) {
this._sidebar = sidebar;
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, this._collapse));
Lang.bind(this._sidebar, this._sidebar.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, this._expand));
},
_collapse : function () {
this._gconf.set_boolean ("sidebar/expanded", false);
},
_expand : function () {
this._gconf.set_boolean ("sidebar/expanded", true);
Lang.bind(this._sidebar, this._sidebar.expand));
}
};

View File

@@ -10,6 +10,8 @@ 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;
@@ -29,22 +31,12 @@ function Widget() {
Widget.prototype = {
// _init():
//
// 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.
// 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.
//
// If you want to have a separate collapsed view, you can define a
// field "collapsedActor" containing the Clutter.Actor to show in
@@ -59,9 +51,6 @@ 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():
//
@@ -92,22 +81,23 @@ Widget.prototype = {
// state:
//
// A field set on your widget by the sidebar. Will contain one of
// the Widget.STATE_* values. (Eg, Widget.STATE_EXPANDED).
// 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.
};
Signals.addSignalMethods(Widget.prototype);
function ClockWidget() {
this._init.apply(this, arguments);
this._init();
}
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
@@ -161,9 +151,9 @@ ClockWidget.prototype = {
_updateCairo: function(time) {
let global = Shell.Global.get();
Shell.draw_clock(this.collapsedActor,
time.getHours() % 12,
time.getMinutes());
global.clutter_cairo_texture_draw_clock(this.collapsedActor,
time.getHours() % 12,
time.getMinutes());
}
};
@@ -178,7 +168,7 @@ const ITEM_NAME_COLOR = new Clutter.Color();
ITEM_NAME_COLOR.from_pixel(0x000000ff);
function LauncherWidget() {
this._init.apply(this, arguments);
this._init();
}
LauncherWidget.prototype = {
@@ -282,61 +272,33 @@ 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.apply(this, arguments);
this._init();
}
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 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));
}
let apps = AppInfo.getTopApps(5);
for (let i = 0; i < apps.length; i++)
this.addItem(apps[i]);
}
};
function RecentDocsWidget() {
this._init.apply(this, arguments);
function DocsWidget() {
this._init();
}
RecentDocsWidget.prototype = {
DocsWidget.prototype = {
__proto__ : LauncherWidget.prototype,
_init : function() {
Widget.prototype._init.apply(this, arguments);
this.title = "Recent Documents";
this.title = "Recent Docs";
this.actor = new Big.Box({ spacing: 2 });
this._recentManager = Gtk.RecentManager.get_default();
@@ -358,7 +320,7 @@ RecentDocsWidget.prototype = {
items.push(docInfo);
}
items.sort(function (a,b) { return b.timestamp - a.timestamp; });
items.sort(function (a,b) { return b.lastVisited() - a.lastVisited(); });
for (i = 0; i < Math.min(items.length, 5); i++)
this.addItem(items[i]);
}

View File

@@ -15,24 +15,21 @@ WIDGETBOX_BG_COLOR.from_pixel(0xf0f0f0ff);
const BLACK = new Clutter.Color();
BLACK.from_pixel(0x000000ff);
const WIDGETBOX_PADDING = 2;
const WIDGETBOX_PADDING = 4;
const ANIMATION_TIME = 0.5;
const POP_IN_LAG = 250; /* milliseconds */
function WidgetBox(widget, expanded) {
this._init(widget, expanded);
function WidgetBox(widget) {
this._init(widget);
}
WidgetBox.prototype = {
_init: function(widget, expanded) {
this.state = expanded ? Widget.STATE_EXPANDED : Widget.STATE_COLLAPSED;
if (widget instanceof Widget.Widget) {
_init: function(widget) {
if (widget instanceof Widget.Widget)
this._widget = widget;
this._widget.state = this.state;
} else {
else {
let ctor = this._ctorFromName(widget);
this._widget = new ctor(this.state);
this._widget = new ctor();
}
if (!this._widget.actor)
@@ -40,7 +37,7 @@ WidgetBox.prototype = {
else if (!this._widget.title && !this._widget.collapsedActor)
throw new Error("widget has neither title nor collapsedActor");
this.state = expanded ? Widget.STATE_EXPANDED : Widget.STATE_COLLAPSED;
this.state = this._widget.state = Widget.STATE_EXPANDED;
// The structure of a WidgetBox:
//
@@ -80,15 +77,9 @@ WidgetBox.prototype = {
this.actor = new Clutter.Group();
this._hbox = new Big.Box({ background_color: WIDGETBOX_BG_COLOR,
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,
padding: WIDGETBOX_PADDING,
spacing: WIDGETBOX_PADDING,
corner_radius: WIDGETBOX_PADDING,
corner_radius: WIDGETBOX_PADDING / 2,
orientation: Big.BoxOrientation.HORIZONTAL,
reactive: true });
this.actor.add_actor(this._hbox);
@@ -131,6 +122,7 @@ 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);
@@ -150,11 +142,6 @@ 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
@@ -191,25 +178,16 @@ WidgetBox.prototype = {
this.state = this._widget.state = Widget.STATE_EXPANDING;
},
_setWidgetExpanded: function() {
_expandPart1Complete: function() {
this._cgroup.hide();
this._egroup.show();
this._cbox.x = 0;
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();
@@ -218,6 +196,11 @@ 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,
@@ -239,27 +222,19 @@ WidgetBox.prototype = {
this.state = this._widget.state = Widget.STATE_COLLAPSING;
},
_setWidgetCollapsed: function() {
_collapsePart1Complete: function() {
this._egroup.hide();
this._cgroup.show();
if (this._singleActor) {
this._widget.actor.unparent();
this._cbox.append(this._widget.actor, Big.BoxPackFlags.NONE);
}
this._ebox.x = 0;
if (this._htitle) {
this._htitle.hide();
this._hline.hide();
}
if (this._vtitle)
this._cbox.height = this._ebox.height;
},
_collapsePart1Complete: function() {
this._ebox.x = 0;
this._setWidgetCollapsed();
if (this._singleActor) {
log(this._widget.actor);
this._widget.actor.unparent();
this._cbox.append(this._widget.actor, Big.BoxPackFlags.NONE);
}
if (this._widget.collapse) {
try {
@@ -269,7 +244,10 @@ 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",

View File

@@ -35,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.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]]
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]]
};
// Spacing between workspaces. At the moment, the same spacing is used
@@ -121,10 +121,9 @@ WindowClone.prototype = {
_onDragBegin : function (draggable, time) {
this._inDrag = true;
this._updateTitle();
this.emit('drag-begin');
},
_onDragEnd : function (draggable, time, snapback) {
_onDragEnd : function (draggable, time) {
this._inDrag = false;
// Most likely, the clone is going to move away from the
@@ -134,8 +133,6 @@ WindowClone.prototype = {
// better to have the label mysteriously missing than
// mysteriously present
this._havePointer = false;
this.emit('drag-end');
},
// Called by Tweener
@@ -158,7 +155,16 @@ 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,
@@ -252,26 +258,18 @@ DesktopClone.prototype = {
Signals.addSignalMethods(DesktopClone.prototype);
/**
* @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);
function Workspace(workspaceNum) {
this._init(workspaceNum);
}
Workspace.prototype = {
_init : function(workspaceNum, parentActor) {
_init : function(workspaceNum) {
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;
@@ -300,7 +298,6 @@ 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]);
@@ -324,7 +321,6 @@ 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) {
@@ -367,21 +363,6 @@ 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);
@@ -411,11 +392,13 @@ Workspace.prototype = {
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
this._frame.lower_bottom();
this._framePosHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFramePosition));
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));
} else {
if (!this._frame)
return;
this.actor.disconnect(this._framePosHandler);
this.actor.disconnect(this._frameSizeHandler);
this._frame.destroy();
this._frame = null;
}
@@ -424,6 +407,9 @@ 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);
},
@@ -431,103 +417,51 @@ 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;
// 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 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);
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 * cloneRect.width,
y: yCenter - 0.5 * scale * cloneRect.height,
{ x: xCenter - 0.5 * scale * clone.actor.width,
y: yCenter - 0.5 * scale * clone.actor.height,
scale_x: scale,
scale_y: scale,
workspace_relative: workspaceZooming ? this : null,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: Lang.bind(this, function() {
this._fadeInWindowIcon(clone, icon);
})
transition: "easeOutQuad"
});
}
},
_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 = this._lookupIndex (metaWin);
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;
}
}
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
@@ -545,9 +479,8 @@ Workspace.prototype = {
};
}
clone.destroy();
icon.destroy();
this.positionWindows(false);
this._positionWindows(false);
this.updateRemovable();
},
@@ -584,7 +517,7 @@ Workspace.prototype = {
clone.actor.set_scale (scale, scale);
}
this.positionWindows(false);
this._positionWindows(false);
this.updateRemovable();
},
@@ -610,15 +543,13 @@ 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._hideAllIcons();
this.leavingOverlay = true;
Tweener.addTween(this.actor,
{ x: this.fullSizeX,
y: this.fullSizeY,
@@ -650,18 +581,16 @@ 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",
onComplete: Lang.bind(this, this._fadeInAllIcons)
transition: "easeOutQuad"
});
},
// Animates the addition of a new (empty) workspace
slideIn : function(oldScale) {
let global = Shell.Global.get();
@@ -690,8 +619,6 @@ 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
@@ -744,47 +671,16 @@ 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('drag-begin',
Lang.bind(this, function() {
icon.hide();
}));
clone.connect('drag-end',
Lang.bind(this, function() {
icon.show();
}));
clone.connect('dragged',
Lang.bind(this, this._onCloneDragged));
this.actor.add_actor(clone.actor);
this._windows.push(clone);
this._windowIcons.push(icon);
return clone;
},
@@ -802,7 +698,7 @@ Workspace.prototype = {
let gridWidth = Math.ceil(Math.sqrt(numberOfWindows));
let gridHeight = Math.ceil(numberOfWindows / gridWidth);
let fraction = 0.95 * (1. / gridWidth);
let fraction = Math.sqrt(.5/(gridWidth * gridHeight));
let xCenter = (.5 / gridWidth) + ((windowIndex) % gridWidth) / gridWidth;
let yCenter = (.5 / gridHeight) + Math.floor((windowIndex / gridWidth)) / gridHeight;
@@ -810,8 +706,20 @@ Workspace.prototype = {
return [xCenter, yCenter, fraction];
},
_onCloneDragged : function (clone, stageX, stageY, time) {
this.emit('window-dragged', clone, stageX, stageY, time);
},
_onCloneSelected : function (clone, time) {
Main.overlay.activateWindow(clone.metaWindow, 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();
},
_removeSelf : function(actor, event) {
@@ -919,32 +827,6 @@ 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) {
@@ -1120,14 +1002,6 @@ 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++)
@@ -1149,7 +1023,7 @@ Workspaces.prototype = {
},
_addWorkspaceActor : function(workspaceNum) {
let workspace = new Workspace(workspaceNum, this.actor);
let workspace = new Workspace(workspaceNum);
this._workspaces[workspaceNum] = workspace;
this.actor.add_actor(workspace.actor);
},

View File

@@ -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
$(AM_V_GEN) ( cd $(srcdir) && \
( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template $(srcdir)/big/big-enum-types.h.in \
$(big_source_h) ) >> xgen-teth && \
(cmp -s xgen-teth big-enum-types.h || cp xgen-teth big-enum-types.h) && \
(cmp 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
$(AM_V_GEN) ( cd $(srcdir) && \
( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template $(srcdir)/big/big-enum-types.c.in \
$(big_source_h) ) >> xgen-tetc && \

17
src/Makefile-taskpanel.am Normal file
View File

@@ -0,0 +1,17 @@
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

View File

@@ -33,7 +33,7 @@ tidy_source_c = \
tidy-marshal.h: stamp-tidy-marshal.h
@true
stamp-tidy-marshal.h: Makefile tidy/tidy-marshal.list
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
$(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
$(AM_V_GEN) (echo "#include \"tidy-marshal.h\"" ; \
(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
$(AM_V_GEN) ( cd $(srcdir) && \
( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template $(srcdir)/tidy/tidy-enum-types.h.in \
$(tidy_source_h) ) >> xgen-teth && \
(cmp -s xgen-teth tidy-enum-types.h || cp xgen-teth tidy-enum-types.h) && \
(cmp 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
$(AM_V_GEN) ( cd $(srcdir) && \
( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template $(srcdir)/tidy/tidy-enum-types.c.in \
$(tidy_source_h) ) >> xgen-tetc && \

View File

@@ -24,7 +24,7 @@ tray_source = \
na-marshal.h: stamp-na-marshal.h
@true
stamp-na-marshal.h: Makefile tray/na-marshal.list
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
$(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
$(AM_V_GEN) (echo "#include \"na-marshal.h\"" ; \
(echo "#include \"na-marshal.h\"" ; \
$(GLIB_GENMARSHAL) \
--prefix=_na_marshal \
--body \

View File

@@ -8,13 +8,12 @@ noinst_LTLIBRARIES =
bin_SCRIPTS = gnome-shell
gnome-shell: gnome-shell.in
$(AM_V_GEN) sed -e "s|@MUTTER_BIN_DIR[@]|$(MUTTER_BIN_DIR)|" \
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
@@ -23,6 +22,7 @@ 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,35 +57,21 @@ 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
@@ -123,7 +109,7 @@ libgnome_shell_la_gir_sources = \
shell-marshal.h: stamp-shell-marshal.h
@true
stamp-shell-marshal.h: Makefile shell-marshal.list
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
$(GLIB_GENMARSHAL) \
--prefix=_shell_marshal \
--header \
$(srcdir)/shell-marshal.list > xgen-tmh && \
@@ -132,7 +118,7 @@ stamp-shell-marshal.h: Makefile shell-marshal.list
echo timestamp > $(@F)
shell-marshal.c: Makefile shell-marshal.list
$(AM_V_GEN) (echo "#include \"shell-marshal.h\"" ; \
(echo "#include \"shell-marshal.h\"" ; \
$(GLIB_GENMARSHAL) \
--prefix=_shell_marshal \
--body \
@@ -154,11 +140,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
$(AM_V_GEN) $(G_IR_SCANNER) \
$(G_IR_SCANNER) \
--namespace=Shell \
--nsversion=0.1 \
--add-include-path=$(MUTTER_LIB_DIR)/mutter/ \
--include=Clutter-1.0 \
--include=Clutter-0.9 \
--include=Meta-2.27 \
--add-include-path=$(builddir) \
--include=Big-1.0 \
@@ -176,10 +162,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
$(AM_V_GEN) $(G_IR_SCANNER) \
$(G_IR_SCANNER) \
--namespace=Tidy \
--nsversion=1.0 \
--include=Clutter-1.0 \
--include=Clutter-0.9 \
--program=mutter \
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
$(addprefix $(srcdir)/,$(tidy_source_h)) \
@@ -194,10 +180,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
$(AM_V_GEN) $(G_IR_SCANNER) \
--namespace=Big \
$(G_IR_SCANNER) \
--namespace=Big \
--nsversion=1.0 \
--include=Clutter-1.0 \
--include=Clutter-0.9 \
--include=GdkPixbuf-2.0 \
--program=mutter \
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \

View File

@@ -0,0 +1,208 @@
/* -*- 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);
}

View File

@@ -0,0 +1,62 @@
/* -*- 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 */

View File

@@ -0,0 +1,723 @@
/* -*- 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);
}

View File

@@ -0,0 +1,70 @@
/* -*- 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 */

View File

@@ -20,7 +20,6 @@
#include <config.h>
#include <float.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -412,16 +411,13 @@ _gdm_user_update (GdmUser *user,
/* Display Name */
if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
gchar *first_comma;
gchar *real_name_utf8;
real_name_utf8 = g_locale_to_utf8 (pwent->pw_gecos, -1, NULL, NULL, NULL);
first_comma = strchr (real_name_utf8, ',');
first_comma = strchr (pwent->pw_gecos, ',');
if (first_comma) {
real_name = g_strndup (real_name_utf8, first_comma - real_name_utf8);
g_free (real_name_utf8);
real_name = g_strndup (pwent->pw_gecos,
(first_comma - pwent->pw_gecos));
} else {
real_name = real_name_utf8;
real_name = g_strdup (pwent->pw_gecos);
}
if (real_name[0] == '\0') {
@@ -885,7 +881,7 @@ curved_rectangle (cairo_t *cr,
x1 = x0 + width;
y1 = y0 + height;
if (width < FLT_EPSILON || height < FLT_EPSILON) {
if (!width || !height) {
return;
}
@@ -1160,8 +1156,8 @@ gdm_user_render_icon (GdmUser *user,
} else {
pixbuf = NULL;
}
g_free (path);
out:
g_free (path);
if (pixbuf != NULL) {
framed = frame_pixbuf (pixbuf);

29
src/gnome-shell.in Normal file → Executable file
View File

@@ -13,7 +13,6 @@ import tempfile
import termios
import time
import errno
import dbus
def find_cmd (cmd_list):
"""
@@ -123,27 +122,25 @@ 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', ''),
'PATH' : '@MUTTER_BIN_DIR@:' + os.environ.get('PATH', '') + ':' + taskpanel_dir,
'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'],
@@ -229,16 +226,7 @@ if options.wide:
metacity_pid = pidof("metacity")
compiz_pid = pidof("compiz.real") or pidof("compiz")
# 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")
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
@@ -271,8 +259,6 @@ 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:
@@ -301,11 +287,6 @@ 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"
@@ -314,7 +295,7 @@ finally:
if options.verbose:
print "Restarting Compiz"
subprocess.Popen(["/usr/bin/compiz"])
if gnome_panel_dbus or gnome_panel_pid:
if gnome_panel_pid:
if options.verbose:
print "Restarting gnome-panel"
subprocess.Popen(["/usr/bin/gnome-panel"])

View File

@@ -0,0 +1,90 @@
/* -*- 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

View File

@@ -5,9 +5,6 @@
#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
@@ -38,16 +35,15 @@ GType shell_app_monitor_get_type (void) G_GNUC_CONST;
ShellAppMonitor* shell_app_monitor_get_default(void);
ShellAppInfo *shell_app_monitor_get_window_app (ShellAppMonitor *monitor, MetaWindow *metawin);
/* Get the most popular applications for a given activity */
GSList *shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
int activity,
gint number);
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);
guint shell_app_monitor_get_window_count (ShellAppMonitor *monitor, const char *appid);
/* Get whatever's running right now */
GSList *shell_app_monitor_get_running_app_ids (ShellAppMonitor *monitor, const char *context);
GList *shell_app_monitor_get_running_app_ids (ShellAppMonitor *monitor);
G_END_DECLS

View File

@@ -4,15 +4,9 @@
#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>
@@ -35,13 +29,11 @@ struct _ShellAppSystemPrivate {
GMenuTree *apps_tree;
GMenuTree *settings_tree;
GHashTable *app_id_to_app;
GSList *cached_app_menus; /* ShellAppMenuEntry */
GSList *cached_settings; /* ShellAppInfo */
GSList *cached_setting_ids; /* utf8 */
GList *cached_favorites; /* utf8 */
GHashTable *cached_favorites; /* <utf8,integer> */
gint app_monitor_id;
};
@@ -54,82 +46,6 @@ 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)
{
@@ -189,15 +105,11 @@ shell_app_system_init (ShellAppSystem *self)
SHELL_TYPE_APP_SYSTEM,
ShellAppSystemPrivate);
/* 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);
priv->cached_favorites = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify)g_free,
NULL);
/* 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->apps_tree = gmenu_tree_lookup ("applications.menu", GMENU_TREE_FLAGS_NONE);
priv->settings_tree = gmenu_tree_lookup ("settings.menu", GMENU_TREE_FLAGS_NONE);
gmenu_tree_add_monitor (priv->apps_tree, on_tree_changed, self);
@@ -224,17 +136,15 @@ 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_settings, (GFunc)shell_app_info_unref, NULL);
g_slist_free (priv->cached_settings);
priv->cached_settings = 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_list_free (priv->cached_favorites);
g_hash_table_destroy (priv->cached_favorites);
gconf_client_notify_remove (gconf_client_get_default (), priv->app_monitor_id);
@@ -286,7 +196,7 @@ reread_directories (ShellAppSystem *self, GSList **cache, GMenuTree *tree)
static GSList *
gather_entries_recurse (ShellAppSystem *monitor,
GSList *apps,
GSList *ids,
GMenuTreeDirectory *root)
{
GSList *contents;
@@ -301,14 +211,15 @@ gather_entries_recurse (ShellAppSystem *monitor,
{
case GMENU_TREE_ITEM_ENTRY:
{
ShellAppInfo *app = shell_app_info_new_from_tree_item (item);
apps = g_slist_prepend (apps, app);
GMenuTreeEntry *entry = (GMenuTreeEntry *)item;
const char *id = gmenu_tree_entry_get_desktop_file_id (entry);
ids = g_slist_prepend (ids, g_strdup (id));
}
break;
case GMENU_TREE_ITEM_DIRECTORY:
{
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
apps = gather_entries_recurse (monitor, apps, dir);
ids = gather_entries_recurse (monitor, ids, dir);
}
break;
default:
@@ -319,7 +230,7 @@ gather_entries_recurse (ShellAppSystem *monitor,
g_slist_free (contents);
return apps;
return ids;
}
static void
@@ -331,7 +242,7 @@ reread_entries (ShellAppSystem *self,
trunk = gmenu_tree_get_root_directory (tree);
g_slist_foreach (*cache, (GFunc)shell_app_info_unref, NULL);
g_slist_foreach (*cache, (GFunc)g_free, NULL);
g_slist_free (*cache);
*cache = NULL;
@@ -340,41 +251,11 @@ 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_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);
reread_entries (self, &(self->priv->cached_setting_ids), self->priv->settings_tree);
}
static void
@@ -387,13 +268,12 @@ on_tree_changed (GMenuTree *monitor, gpointer user_data)
reread_menus (self);
}
static GList *
convert_gconf_value_string_list_to_list_uniquify (GConfValue *value )
static void
copy_gconf_value_string_list_to_hashset (GConfValue *value,
GHashTable *dest)
{
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);
@@ -403,16 +283,8 @@ convert_gconf_value_string_list_to_list_uniquify (GConfValue *value )
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_insert (dest, str, GUINT_TO_POINTER(1));
}
g_hash_table_destroy (tmp_table);
return g_list_reverse (result);
}
static void
@@ -426,9 +298,8 @@ reread_favorite_apps (ShellAppSystem *system)
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);
g_hash_table_remove_all (system->priv->cached_favorites);
copy_gconf_value_string_list_to_hashset (val, system->priv->cached_favorites);
gconf_value_free (val);
}
@@ -444,19 +315,6 @@ on_favorite_apps_changed (GConfClient *client,
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)
{
@@ -476,7 +334,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 container) (element-type ShellAppInfo): List of applications
* Return value: (transfer full) (element-type utf8): List of desktop file ids
*/
GSList *
shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
@@ -501,7 +359,7 @@ shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
/**
* shell_app_system_get_menus:
*
* Returns a list of toplevel #ShellAppMenuEntry items
* Returns a list of toplevel menu names, like "Accessories", "Programming", etc.
*
* Return value: (transfer none) (element-type AppMenuEntry): List of toplevel menus
*/
@@ -514,14 +372,14 @@ shell_app_system_get_menus (ShellAppSystem *monitor)
/**
* shell_app_system_get_all_settings:
*
* Returns a list of application items under "settings.menu".
* Returns a list of all desktop file ids under "settings.menu".
*
* Return value: (transfer none) (element-type ShellAppInfo): List of applications
* Return value: (transfer none) (element-type utf8): List of desktop file ids
*/
GSList *
shell_app_system_get_all_settings (ShellAppSystem *monitor)
{
return monitor->priv->cached_settings;
return monitor->priv->cached_setting_ids;
}
/**
@@ -546,12 +404,12 @@ shell_app_system_get_default ()
* 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
* Return value: (transfer container) (element-type utf8): List of favorite application ids
*/
GList *
shell_app_system_get_favorites (ShellAppSystem *system)
{
return system->priv->cached_favorites;
return g_hash_table_get_keys (system->priv->cached_favorites);
}
static void
@@ -573,25 +431,22 @@ set_gconf_value_string_list (GConfValue *val, GList *items)
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;
GList *favorites;
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));
g_hash_table_insert (system->priv->cached_favorites, g_strdup (id), GUINT_TO_POINTER (1));
favorites = g_hash_table_get_keys (system->priv->cached_favorites);
set_gconf_value_string_list (val, favorites);
g_list_free (favorites);
set_gconf_value_string_list (val, system->priv->cached_favorites);
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
}
@@ -600,346 +455,102 @@ shell_app_system_remove_favorite (ShellAppSystem *system, const char *id)
{
GConfClient *client = gconf_client_get_default ();
GConfValue *val;
GList *iter;
GList *favorites;
iter = g_list_find_custom (system->priv->cached_favorites, id, (GCompareFunc)strcmp);
if (!iter)
if (!g_hash_table_remove (system->priv->cached_favorites, id))
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);
favorites = g_hash_table_get_keys (system->priv->cached_favorites);
set_gconf_value_string_list (val, favorites);
g_list_free (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)
static gboolean
desktop_id_exists (ShellAppSystem *system,
const char *target_id,
GMenuTreeDirectory *root)
{
ShellAppInfo *info;
gboolean found = FALSE;
GSList *contents, *iter;
info = g_hash_table_lookup (self->priv->app_id_to_app, id);
if (info)
shell_app_info_ref (info);
return info;
}
contents = gmenu_tree_directory_get_contents (root);
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)
for (iter = contents; iter; iter = iter->next)
{
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);
GMenuTreeItem *item = iter->data;
if (found)
break;
switch (gmenu_tree_item_get_type (item))
{
case GMENU_TREE_ITEM_ENTRY:
{
GMenuTreeEntry *entry = (GMenuTreeEntry *)item;
const char *id = gmenu_tree_entry_get_desktop_file_id (entry);
if (strcmp (id, target_id) == 0)
found = TRUE;
}
break;
case GMENU_TREE_ITEM_DIRECTORY:
{
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
found = desktop_id_exists (system, target_id, dir);
}
break;
default:
break;
}
gmenu_tree_item_unref (item);
}
if (!success)
{
g_key_file_free (keyfile);
g_free (full_path);
return NULL;
}
g_slist_free (contents);
appinfo = shell_app_info_new_from_keyfile_take_ownership (keyfile, full_path);
g_free (full_path);
return appinfo;
return found;
}
/**
* shell_app_system_lookup_heuristic_basename:
* shell_app_system_lookup_basename:
* @name: Probable application identifier
*
* Find a valid application corresponding to a given
* Determine whether a valid .desktop file ID corresponding to a given
* heuristically determined application identifier
* string, or %NULL if none.
*
* Returns: (transfer full): A #ShellAppInfo for name
* string.
*/
ShellAppInfo *
shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
const char *name)
char *
shell_app_system_lookup_basename (ShellAppSystem *system,
const char *name)
{
char *tmpid;
ShellAppInfo *result;
GMenuTreeDirectory *root;
char *result;
result = shell_app_system_lookup_cached_app (system, name);
if (result != NULL)
return result;
root = gmenu_tree_get_directory_from_path (system->priv->apps_tree, "/");
g_assert (root != NULL);
if (desktop_id_exists (system, name, root))
{
result = g_strdup (name);
goto out;
}
/* 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;
result = g_strjoin ("", "gnome-", name, NULL);
if (desktop_id_exists (system, result, root))
goto out;
tmpid = g_strjoin ("", "fedora-", name, NULL);
result = shell_app_system_lookup_cached_app (system, tmpid);
g_free (tmpid);
if (result != NULL)
return result;
result = g_strjoin ("", "fedora-", name, NULL);
if (desktop_id_exists (system, result, root))
goto out;
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);
out:
gmenu_tree_item_unref (root);
return result;
}

View File

@@ -1,8 +1,7 @@
#ifndef __SHELL_APP_SYSTEM_H__
#define __SHELL_APP_SYSTEM_H__
#include <gio/gio.h>
#include <clutter/clutter.h>
#include <glib-object.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))
@@ -45,37 +44,7 @@ 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);
char * shell_app_system_lookup_basename (ShellAppSystem *system, const char *id);
GSList *shell_app_system_get_menus (ShellAppSystem *system);

View File

@@ -1,92 +0,0 @@
/* -*- 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;
}

View File

@@ -1,37 +0,0 @@
#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__ */

View File

@@ -1,215 +0,0 @@
/* -*- 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);
}

View File

@@ -1,30 +0,0 @@
/* -*- 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__ */

View File

@@ -1,396 +0,0 @@
/* -*- 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)

View File

@@ -1,95 +0,0 @@
/* -*- 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

View File

@@ -1,240 +0,0 @@
/* -*- 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;
}

View File

@@ -1,44 +0,0 @@
#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__ */

View File

@@ -14,8 +14,7 @@
#include <string.h>
#include <unistd.h>
#include <dbus/dbus-glib.h>
#include <gio/gio.h>
#include <glib/gi18n.h>
#include <libgnomeui/gnome-thumbnail.h>
#include <math.h>
#include <X11/extensions/Xfixes.h>
@@ -271,6 +270,49 @@ 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
@@ -296,6 +338,135 @@ 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:
*
@@ -697,26 +868,24 @@ shell_global_grab_dbus_service (ShellGlobal *global)
*/
exit (0);
}
/* 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"))
{
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);
}
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))
{
g_critical ("failed to execute %s: %s", panel_args[0], error->message);
g_clear_error (&error);
}
}
static void
grab_notify (GtkWidget *widget, gboolean was_grabbed, gpointer user_data)
{
@@ -728,6 +897,102 @@ 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;
}
/**
* shell_global_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_global_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;
}
/*
* Updates the global->root_pixmap actor with the root window's pixmap or fails
* with a warning.
@@ -815,46 +1080,6 @@ 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
@@ -911,3 +1136,50 @@ 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);
}

View File

@@ -36,6 +36,10 @@ 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,6 +52,8 @@ MetaScreen *shell_global_get_screen (ShellGlobal *global);
void shell_global_grab_dbus_service (ShellGlobal *global);
void shell_global_start_task_panel (ShellGlobal *global);
typedef enum {
SHELL_STAGE_INPUT_MODE_NONREACTIVE,
SHELL_STAGE_INPUT_MODE_NORMAL,
@@ -69,10 +75,18 @@ void shell_global_ungrab_keyboard (ShellGlobal *global);
void shell_global_reexec_self (ShellGlobal *global);
void shell_global_format_time_relative_pretty (ShellGlobal *global, guint delta, char **text, guint *update_time);
ClutterCairoTexture *shell_global_create_vertical_gradient (ClutterColor *top,
ClutterColor *bottom);
ClutterCairoTexture *shell_global_create_horizontal_gradient (ClutterColor *left,
ClutterColor *right);
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__ */

View File

@@ -1,426 +0,0 @@
/* -*- 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;
}

View File

@@ -1,44 +0,0 @@
/* -*- 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__ */

View File

@@ -52,6 +52,8 @@ 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);
}

View File

@@ -221,8 +221,7 @@ get_memory_target (void)
return mem_total / 2;
}
/* Skip to the next line and discard what we read */
if (fgets(line_buffer, sizeof(line_buffer), f) == NULL)
break;
fgets(line_buffer, sizeof(line_buffer), f);
}
fclose(f);
@@ -233,9 +232,6 @@ 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 ();

View File

@@ -1,150 +0,0 @@
/* -*- 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)
{
}

View File

@@ -1,33 +0,0 @@
#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__ */

View File

@@ -39,13 +39,10 @@
#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;
@@ -57,12 +54,10 @@ 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;
@@ -118,9 +113,12 @@ static void
update_name_text (ShellStatusMenu *status)
{
ShellStatusMenuPrivate *priv = status->priv;
char *markup;
clutter_text_set_text (priv->name,
gdm_user_get_real_name (GDM_USER (priv->user)));
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);
}
static void
@@ -316,18 +314,10 @@ on_account_activate (GtkMenuItem *item,
spawn_external (status, "gnome-about-me");
}
static void
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)
on_quit_session_activate (GtkMenuItem *item,
ShellStatusMenu *status)
{
char *args[3];
GError *error;
@@ -338,7 +328,7 @@ gnome_session_save_command (const char *arg)
if (args[0] == NULL)
return;
args[1] = (char *)arg;
args[1] = "--logout-dialog";
args[2] = NULL;
screen = gdk_screen_get_default ();
@@ -355,21 +345,6 @@ gnome_session_save_command (const char *arg)
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)
{
@@ -455,8 +430,6 @@ 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;
@@ -499,14 +472,6 @@ 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 ());
@@ -542,8 +507,7 @@ create_sub_menu (ShellStatusMenu *status)
G_CALLBACK (on_login_screen_activate), status);
/* Only show switch user if there are other users */
/* Log Out */
priv->quit_session_item = gtk_image_menu_item_new_with_label (_("Log Out..."));
priv->quit_session_item = gtk_image_menu_item_new_with_label (_("Quit..."));
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);
@@ -553,17 +517,6 @@ 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);
}
@@ -635,29 +588,22 @@ 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;
gtk_widget_get_size_request (GTK_WIDGET (menu), &menu_width, NULL);
clutter_actor_get_transformed_position (CLUTTER_ACTOR (status), &src_x, &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);
*x = (gint)(0.5 + src_x);
*y = (gint)(0.5 + src_y);
}
void
@@ -676,27 +622,3 @@ 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;
}

View File

@@ -37,9 +37,6 @@ 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__ */

View File

@@ -1,26 +1,16 @@
#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 *keyed_cache; /* CacheKey -> CoglTexture* */
GnomeThumbnailFactory *thumbnails;
GHashTable *gicon_cache; /* CacheKey -> CoglTexture* */
};
static void shell_texture_cache_dispose (GObject *object);
@@ -32,17 +22,10 @@ static guint
cache_key_hash (gconstpointer a)
{
CacheKey *akey = (CacheKey *)a;
guint base_hash;
if (akey->icon)
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;
return g_icon_hash (akey->icon) + 31*akey->size;
g_assert_not_reached ();
}
static gboolean
@@ -52,34 +35,11 @@ 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);
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;
g_assert_not_reached ();
}
static void
@@ -88,32 +48,9 @@ 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)
{
@@ -127,9 +64,8 @@ static void
shell_texture_cache_init (ShellTextureCache *self)
{
self->priv = g_new0 (ShellTextureCachePrivate, 1);
self->priv->keyed_cache = g_hash_table_new_full (cache_key_hash, cache_key_equal,
self->priv->gicon_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
@@ -137,13 +73,9 @@ shell_texture_cache_dispose (GObject *object)
{
ShellTextureCache *self = (ShellTextureCache*)object;
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;
if (self->priv->gicon_cache)
g_hash_table_destroy (self->priv->gicon_cache);
self->priv->gicon_cache = NULL;
G_OBJECT_CLASS (shell_texture_cache_parent_class)->dispose (object);
}
@@ -154,19 +86,23 @@ 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,
@@ -264,10 +200,6 @@ 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);
}
@@ -396,71 +328,6 @@ 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,
@@ -470,27 +337,9 @@ load_pixbuf_thread (GSimpleAsyncResult *result,
AsyncIconLookupData *data;
GError *error = NULL;
data = g_object_get_data (G_OBJECT (result), "load_pixbuf_async");
g_assert (data != NULL);
data = g_object_get_data (G_OBJECT (result), "load_icon_pixbuf_async");
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)
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);
@@ -503,9 +352,8 @@ load_pixbuf_thread (GSimpleAsyncResult *result,
return;
}
if (pixbuf)
g_simple_async_result_set_op_res_gpointer (result, g_object_ref (pixbuf),
g_object_unref);
g_simple_async_result_set_op_res_gpointer (result, g_object_ref (pixbuf),
g_object_unref);
}
/**
@@ -527,7 +375,6 @@ 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;
@@ -535,7 +382,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_pixbuf_async", data, icon_lookup_data_destroy);
g_object_set_data_full (G_OBJECT (result), "load_icon_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);
@@ -554,7 +401,6 @@ 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;
@@ -562,63 +408,7 @@ 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_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_object_set_data_full (G_OBJECT (result), "load_uri_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);
@@ -634,11 +424,7 @@ 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;
@@ -658,45 +444,6 @@ 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,
@@ -713,39 +460,32 @@ on_pixbuf_loaded (GObject *source,
cache = SHELL_TEXTURE_CACHE (source);
pixbuf = load_pixbuf_async_finish (cache, result, &error);
if (pixbuf == NULL)
pixbuf = load_pixbuf_fallback(data);
if (pixbuf == NULL)
goto out;
{
/* TODO - we need a "broken image" display of some sort */
goto out;
}
texdata = pixbuf_to_cogl_handle (pixbuf);
g_object_unref (pixbuf);
if (data->policy != SHELL_TEXTURE_CACHE_POLICY_NONE)
if (data->icon)
{
gpointer orig_key, value;
key = g_new0 (CacheKey, 1);
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->icon = g_object_ref (data->icon);
key->size = data->width;
if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, key,
if (!g_hash_table_lookup_extended (cache->priv->gicon_cache, key,
&orig_key, &value))
g_hash_table_insert (cache->priv->keyed_cache, key,
g_hash_table_insert (cache->priv->gicon_cache, key,
texdata);
else
cache_key_destroy (key);
}
set_texture_cogl_texture (data->texture, texdata);
clutter_texture_set_cogl_texture (data->texture, texdata);
out:
if (data->icon)
@@ -755,17 +495,10 @@ 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);
}
@@ -786,13 +519,12 @@ shell_texture_cache_load_gicon (ShellTextureCache *cache,
CoglHandle texdata;
CacheKey key;
texture = create_default_texture (cache);
texture = CLUTTER_TEXTURE (clutter_texture_new ());
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->keyed_cache, &key);
texdata = g_hash_table_lookup (cache->priv->gicon_cache, &key);
if (texdata == NULL)
{
@@ -807,9 +539,7 @@ 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);
@@ -819,39 +549,14 @@ shell_texture_cache_load_gicon (ShellTextureCache *cache,
}
else
{
set_texture_cogl_texture (texture, texdata);
clutter_texture_set_cogl_texture (texture, texdata);
}
return CLUTTER_ACTOR (texture);
}
/**
* 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:
* shell_texture_cache_load_uri:
*
* @cache: The texture cache instance
* @uri: uri of the image file from which to create a pixbuf
@@ -873,10 +578,9 @@ shell_texture_cache_load_uri_async (ShellTextureCache *cache,
ClutterTexture *texture;
AsyncTextureLoadData *data;
texture = create_default_texture (cache);
texture = CLUTTER_TEXTURE (clutter_texture_new ());
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;
@@ -890,7 +594,6 @@ 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
@@ -906,236 +609,27 @@ 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;
CoglHandle texdata;
GdkPixbuf *pixbuf;
CacheKey key;
texture = create_default_texture (cache);
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);
}
pixbuf = impl_load_pixbuf_file (uri, available_width, available_height, error);
if (!pixbuf)
return NULL;
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);
}
texdata = pixbuf_to_cogl_handle (pixbuf);
g_object_unref (pixbuf);
clutter_texture_set_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;
/**
@@ -1144,7 +638,7 @@ static ShellTextureCache *instance = NULL;
* Return value: (transfer none): The global texture cache
*/
ShellTextureCache*
shell_texture_cache_get_default (void)
shell_texture_cache_get_default ()
{
if (instance == NULL)
instance = g_object_new (SHELL_TYPE_TEXTURE_CACHE, NULL);

View File

@@ -2,8 +2,6 @@
#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 ())
@@ -31,47 +29,20 @@ 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 (void);
ClutterActor *shell_texture_cache_load_icon_name (ShellTextureCache *cache,
const char *name,
gint size);
ShellTextureCache* shell_texture_cache_get_default();
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,

View File

@@ -1,383 +0,0 @@
/* -*- 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");
}

View File

@@ -1,13 +0,0 @@
#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__ */

View File

@@ -1,5 +1,3 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* tidy-grid.h: Reflowing grid layout container for clutter.
*
* Copyright (C) 2008 Intel Corporation
@@ -104,6 +102,7 @@ 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;
@@ -140,6 +139,7 @@ struct _TidyGridActorData
{
gboolean xpos_set, ypos_set;
gfloat xpos, ypos;
gfloat pref_width, pref_height;
};
static void
@@ -660,37 +660,21 @@ 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;
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;
}
gfloat natural_width;
natural_width = 200.0;
if (min_width_p)
*min_width_p = min_width;
*min_width_p = natural_width;
if (natural_width_p)
*natural_width_p = natural_width;
priv->pref_width = natural_width;
}
static void
@@ -701,42 +685,12 @@ 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;
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;
natural_height = 200.0;
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;
priv->for_width = for_width;
priv->pref_height = natural_height;
if (min_height_p)
*min_height_p = natural_height;
@@ -804,6 +758,9 @@ compute_row_height (GList *siblings,
return best_yet;
}
static gfloat
compute_row_start (GList *siblings,
gfloat start_x,

View File

@@ -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
# ({mozilla,firefox,xulrunner}-js), xdamage, xscrnsaver
#
# 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 mesa-utils python-dev libreadline5-dev xulrunner-dev \
xserver-xephyr \
mesa-common-dev python-dev libreadline5-dev xulrunner-dev \
xserver-xephyr libxss-dev \
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 \
xulrunner-devel libXdamage-devel libXScrnSaver-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 \
xorg-x11-proto-devel readline-devel mozilla-xulrunner190-devel \
libXScrnSaver-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,7 +126,8 @@ 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 mesa-demos x11-server-xephyr x11-apps xterm zenity \
libxdamage-devel libxscrnsaver-devel \
mesa-demos x11-server-xephyr x11-apps xterm zenity \
; do
if ! rpm -q --whatprovides $pkg > /dev/null 2>&1; then
reqd="$pkg $reqd"

View File

@@ -30,7 +30,7 @@
</autotools>
<autotools id="clutter">
<branch repo="git.clutter-project.org" module="clutter" revision="clutter-1.0"/>
<branch repo="git.clutter-project.org" module="clutter"/>
<dependencies>
<dep package="gobject-introspection"/>
</dependencies>