Merge branch 'master' into message-tray
Conflicts: js/ui/main.js
This commit is contained in:
commit
09653fbaf6
@ -88,6 +88,21 @@
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
<schema>
|
||||
<key>/schemas/desktop/gnome/shell/disabled_extensions</key>
|
||||
<applyto>/desktop/gnome/shell/disabled_extensions</applyto>
|
||||
<owner>gnome-shell</owner>
|
||||
<type>list</type>
|
||||
<list_type>string</list_type>
|
||||
<default>[]</default>
|
||||
<locale name="C">
|
||||
<short>Uuids of extensions to disable</short>
|
||||
<long>
|
||||
GNOME Shell extensions have a uuid property; this key lists extensions which should not be loaded.
|
||||
</long>
|
||||
</locale>
|
||||
</schema>
|
||||
|
||||
</schemalist>
|
||||
|
||||
</gconfschemafile>
|
||||
|
@ -17,6 +17,15 @@
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
.shell-link {
|
||||
color: #0000ff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.shell-link:hover {
|
||||
color: #0000e0;
|
||||
}
|
||||
|
||||
StScrollBar
|
||||
{
|
||||
padding: 0px;
|
||||
@ -152,17 +161,6 @@ StTooltip {
|
||||
spacing: 12px;
|
||||
}
|
||||
|
||||
.dash-search-section-header {
|
||||
padding: 6px 0px;
|
||||
spacing: 4px;
|
||||
font-size: 12px;
|
||||
color: #bbbbbb;
|
||||
}
|
||||
|
||||
.dash-search-section-title, dash-search-section-count {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#searchEntry {
|
||||
padding: 4px;
|
||||
border-bottom: 1px solid #262626;
|
||||
@ -237,6 +235,29 @@ StTooltip {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.dash-search-section-header {
|
||||
padding: 6px 0px;
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.dash-search-section-results {
|
||||
color: #ffffff;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.dash-search-section-list-results {
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.dash-search-result-content {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.dash-search-result-content:selected {
|
||||
padding: 1px;
|
||||
border: 1px solid #262626;
|
||||
}
|
||||
|
||||
/* GenericDisplay */
|
||||
|
||||
.generic-display-container {
|
||||
@ -352,6 +373,19 @@ StTooltip {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#LookingGlassDialog .labels {
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
#LookingGlassDialog .notebook-tab {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
#LookingGlassDialog .notebook-tab:selected {
|
||||
border: 1px solid #88ff66;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
#LookingGlassDialog StLabel
|
||||
{
|
||||
color: #88ff66;
|
||||
@ -368,6 +402,29 @@ StTooltip {
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
#lookingGlassExtensions {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.lg-extension-list {
|
||||
padding: 4px;
|
||||
spacing: 6px;
|
||||
}
|
||||
|
||||
.lg-extension {
|
||||
border: 1px solid #6f6f6f;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.lg-extension-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.lg-extension-actions {
|
||||
spacing: 6px;
|
||||
}
|
||||
|
||||
/* Calendar popup */
|
||||
|
||||
#calendarPopup {
|
||||
@ -458,6 +515,11 @@ StTooltip {
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.switcher-list .thumbnail {
|
||||
width: 256px;
|
||||
height: 256px;
|
||||
}
|
||||
|
||||
.switcher-list .outlined-item-box {
|
||||
padding: 6px;
|
||||
border: 2px solid rgba(85,85,85,1.0);
|
||||
|
@ -7,6 +7,7 @@ const Shell = imports.gi.Shell;
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Search = imports.ui.search;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const THUMBNAIL_ICON_MARGIN = 2;
|
||||
@ -23,6 +24,7 @@ DocInfo.prototype = {
|
||||
// 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._lowerName = this.name.toLowerCase();
|
||||
this.uri = recentInfo.get_uri();
|
||||
this.mimeType = recentInfo.get_mime_type();
|
||||
},
|
||||
@ -32,54 +34,27 @@ DocInfo.prototype = {
|
||||
},
|
||||
|
||||
launch : function() {
|
||||
// While using Gio.app_info_launch_default_for_uri() would be
|
||||
// shorter in terms of lines of code, we are not doing so
|
||||
// because that would duplicate the work of retrieving the
|
||||
// mime type.
|
||||
let needsUri = Gio.file_new_for_uri(this.uri).get_path() == null;
|
||||
let appInfo = Gio.app_info_get_default_for_type(this.mimeType, needsUri);
|
||||
|
||||
if (appInfo != null) {
|
||||
appInfo.launch_uris([this.uri], Main.createAppLaunchContext());
|
||||
} else {
|
||||
log("Failed to get default application info for mime type " + this.mimeType +
|
||||
". Will try to use the last application that registered the document.");
|
||||
let appName = this.recentInfo.last_application();
|
||||
let [success, appExec, count, time] = this.recentInfo.get_application_info(appName);
|
||||
if (success) {
|
||||
log("Will open a document with the following command: " + appExec);
|
||||
// TODO: Change this once better support for creating
|
||||
// GAppInfo is added to GtkRecentInfo, as right now
|
||||
// this relies on the fact that the file uri is
|
||||
// already a part of appExec, so we don't supply any
|
||||
// files to appInfo.launch().
|
||||
|
||||
// The 'command line' passed to
|
||||
// create_from_command_line is allowed to contain
|
||||
// '%<something>' macros that are expanded to file
|
||||
// name / icon name, etc, so we need to escape % as %%
|
||||
appExec = appExec.replace(/%/g, "%%");
|
||||
|
||||
let appInfo = Gio.app_info_create_from_commandline(appExec, null, 0, null);
|
||||
|
||||
// The point of passing an app launch context to
|
||||
// launch() is mostly to get startup notification and
|
||||
// associated benefits like the app appearing on the
|
||||
// right desktop; but it doesn't really work for now
|
||||
// because with the way we create the appInfo we
|
||||
// aren't reading the application's desktop file, and
|
||||
// thus don't find the StartupNotify=true in it. So,
|
||||
// despite passing the app launch context, no startup
|
||||
// notification occurs.
|
||||
appInfo.launch([], Main.createAppLaunchContext());
|
||||
} else {
|
||||
log("Failed to get application info for " + this.uri);
|
||||
}
|
||||
}
|
||||
Shell.DocSystem.get_default().open(this.recentInfo);
|
||||
},
|
||||
|
||||
exists : function() {
|
||||
return this.recentInfo.exists();
|
||||
matchTerms: function(terms) {
|
||||
let mtype = Search.MatchType.NONE;
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
let term = terms[i];
|
||||
let idx = this._lowerName.indexOf(term);
|
||||
if (idx == 0) {
|
||||
if (mtype != Search.MatchType.NONE)
|
||||
return Search.MatchType.MULTIPLE;
|
||||
mtype = Search.MatchType.PREFIX;
|
||||
} else if (idx > 0) {
|
||||
if (mtype != Search.MatchType.NONE)
|
||||
return Search.MatchType.MULTIPLE;
|
||||
mtype = Search.MatchType.SUBSTRING;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return mtype;
|
||||
}
|
||||
};
|
||||
|
||||
@ -91,50 +66,86 @@ function getDocManager() {
|
||||
return docManagerInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* DocManager wraps the DocSystem, primarily to expose DocInfo objects
|
||||
* which conform to the GenericDisplay item API.
|
||||
*/
|
||||
function DocManager() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DocManager.prototype = {
|
||||
_init: function() {
|
||||
this._recentManager = Gtk.RecentManager.get_default();
|
||||
this._items = {};
|
||||
this._recentManager.connect('changed', Lang.bind(this, function(recentManager) {
|
||||
this._reload();
|
||||
this.emit('changed');
|
||||
}));
|
||||
this._docSystem = Shell.DocSystem.get_default();
|
||||
this._infosByTimestamp = [];
|
||||
this._infosByUri = {};
|
||||
this._docSystem.connect('changed', Lang.bind(this, this._reload));
|
||||
this._reload();
|
||||
},
|
||||
|
||||
_reload: function() {
|
||||
let docs = this._recentManager.get_items();
|
||||
let newItems = {};
|
||||
let docs = this._docSystem.get_all();
|
||||
this._infosByTimestamp = [];
|
||||
this._infosByUri = {};
|
||||
for (let i = 0; i < docs.length; i++) {
|
||||
let recentInfo = docs[i];
|
||||
if (!recentInfo.exists())
|
||||
continue;
|
||||
|
||||
let docInfo = new DocInfo(recentInfo);
|
||||
|
||||
// we use GtkRecentInfo URI as an item Id
|
||||
newItems[docInfo.uri] = docInfo;
|
||||
this._infosByTimestamp.push(docInfo);
|
||||
this._infosByUri[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._items[uri].recentInfo);
|
||||
}
|
||||
this._items = newItems;
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
getItems: function() {
|
||||
return this._items;
|
||||
getTimestampOrderedInfos: function() {
|
||||
return this._infosByTimestamp;
|
||||
},
|
||||
|
||||
getInfosByUri: function() {
|
||||
return this._infosByUri;
|
||||
},
|
||||
|
||||
lookupByUri: function(uri) {
|
||||
return this._infosByUri[uri];
|
||||
},
|
||||
|
||||
queueExistenceCheck: function(count) {
|
||||
return this._docSystem.queue_existence_check(count);
|
||||
},
|
||||
|
||||
initialSearch: function(terms) {
|
||||
let multipleMatches = [];
|
||||
let prefixMatches = [];
|
||||
let substringMatches = [];
|
||||
for (let i = 0; i < this._infosByTimestamp.length; i++) {
|
||||
let item = this._infosByTimestamp[i];
|
||||
let mtype = item.matchTerms(terms);
|
||||
if (mtype == Search.MatchType.MULTIPLE)
|
||||
multipleMatches.push(item.uri);
|
||||
else if (mtype == Search.MatchType.PREFIX)
|
||||
prefixMatches.push(item.uri);
|
||||
else if (mtype == Search.MatchType.SUBSTRING)
|
||||
substringMatches.push(item.uri);
|
||||
}
|
||||
return multipleMatches.concat(prefixMatches.concat(substringMatches));
|
||||
},
|
||||
|
||||
subsearch: function(previousResults, terms) {
|
||||
let multipleMatches = [];
|
||||
let prefixMatches = [];
|
||||
let substringMatches = [];
|
||||
for (let i = 0; i < previousResults.length; i++) {
|
||||
let uri = previousResults[i];
|
||||
let item = this._infosByUri[uri];
|
||||
let mtype = item.matchTerms(terms);
|
||||
if (mtype == Search.MatchType.MULTIPLE)
|
||||
multipleMatches.push(uri);
|
||||
else if (mtype == Search.MatchType.PREFIX)
|
||||
prefixMatches.push(uri);
|
||||
else if (mtype == Search.MatchType.SUBSTRING)
|
||||
substringMatches.push(uri);
|
||||
}
|
||||
return multipleMatches.concat(prefixMatches.concat(substringMatches));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ dist_jsui_DATA = \
|
||||
dnd.js \
|
||||
docDisplay.js \
|
||||
environment.js \
|
||||
extensionSystem.js \
|
||||
genericDisplay.js \
|
||||
lightbox.js \
|
||||
link.js \
|
||||
@ -20,6 +21,7 @@ dist_jsui_DATA = \
|
||||
panel.js \
|
||||
placeDisplay.js \
|
||||
runDialog.js \
|
||||
search.js \
|
||||
shellDBus.js \
|
||||
sidebar.js \
|
||||
statusMenu.js \
|
||||
|
@ -459,7 +459,7 @@ SwitcherList.prototype = {
|
||||
this._separator = box;
|
||||
this._list.add_actor(box);
|
||||
},
|
||||
|
||||
|
||||
highlight: function(index, justOutline) {
|
||||
if (this._highlighted != -1)
|
||||
this._items[this._highlighted].style_class = 'item-box';
|
||||
@ -477,7 +477,7 @@ SwitcherList.prototype = {
|
||||
_itemActivated: function(n) {
|
||||
this.emit('item-activated', n);
|
||||
},
|
||||
|
||||
|
||||
_itemEntered: function(n) {
|
||||
this.emit('item-entered', n);
|
||||
},
|
||||
@ -592,7 +592,7 @@ AppIcon.prototype = {
|
||||
this.actor = new St.BoxLayout({ style_class: "alt-tab-app",
|
||||
vertical: true });
|
||||
this._icon = this.app.create_icon_texture(POPUP_APPICON_SIZE);
|
||||
this.actor.add(this._icon, { x_fill: false, y_fill: false });
|
||||
this.actor.add(this._icon, { x_fill: false, y_fill: false } );
|
||||
this._label = new St.Label({ text: this.app.get_name() });
|
||||
this.actor.add(this._label, { x_fill: false });
|
||||
}
|
||||
@ -750,11 +750,14 @@ ThumbnailList.prototype = {
|
||||
let box = new St.BoxLayout({ style_class: "thumbnail-box",
|
||||
vertical: true });
|
||||
|
||||
let bin = new St.Bin({ style_class: "thumbnail" });
|
||||
let clone = new Clutter.Clone ({ source: windowTexture,
|
||||
reactive: true,
|
||||
width: width * scale,
|
||||
height: height * scale });
|
||||
box.add_actor(clone);
|
||||
|
||||
bin.add_actor(clone);
|
||||
box.add_actor(bin);
|
||||
|
||||
let title = windows[i].get_title();
|
||||
if (title) {
|
||||
|
@ -18,6 +18,7 @@ const AppFavorites = imports.ui.appFavorites;
|
||||
const DND = imports.ui.dnd;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
const Main = imports.ui.main;
|
||||
const Search = imports.ui.search;
|
||||
const Workspaces = imports.ui.workspaces;
|
||||
|
||||
const APPICON_SIZE = 48;
|
||||
@ -54,7 +55,7 @@ AppDisplayItem.prototype = {
|
||||
let windows = app.get_windows();
|
||||
if (windows.length > 0) {
|
||||
let mostRecentWindow = windows[0];
|
||||
Main.overview.activateWindow(mostRecentWindow, Main.currentTime());
|
||||
Main.overview.activateWindow(mostRecentWindow, global.get_current_time());
|
||||
} else {
|
||||
this._appInfo.launch();
|
||||
}
|
||||
@ -134,17 +135,10 @@ AppDisplay.prototype = {
|
||||
this._addApp(app);
|
||||
}
|
||||
} else {
|
||||
// Loop over the toplevel menu items, load the set of desktop file ids
|
||||
// associated with each one.
|
||||
let allMenus = this._appSystem.get_menus();
|
||||
for (let i = 0; i < allMenus.length; i++) {
|
||||
let menu = allMenus[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 apps = this._appSystem.get_flattened_apps();
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let app = apps[i];
|
||||
this._addApp(app);
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,31 +214,104 @@ AppDisplay.prototype = {
|
||||
|
||||
Signals.addSignalMethods(AppDisplay.prototype);
|
||||
|
||||
|
||||
function BaseWellItem(app, isFavorite) {
|
||||
this._init(app, isFavorite);
|
||||
function BaseAppSearchProvider() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
BaseWellItem.prototype = {
|
||||
_init : function(app, isFavorite) {
|
||||
BaseAppSearchProvider.prototype = {
|
||||
__proto__: Search.SearchProvider.prototype,
|
||||
|
||||
_init: function(name) {
|
||||
Search.SearchProvider.prototype._init.call(this, name);
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
},
|
||||
|
||||
getResultMeta: function(resultId) {
|
||||
let app = this._appSys.get_app(resultId);
|
||||
if (!app)
|
||||
return null;
|
||||
return { 'id': resultId,
|
||||
'name': app.get_name(),
|
||||
'icon': app.create_icon_texture(Search.RESULT_ICON_SIZE)};
|
||||
},
|
||||
|
||||
activateResult: function(id) {
|
||||
let app = this._appSys.get_app(id);
|
||||
app.launch();
|
||||
}
|
||||
};
|
||||
|
||||
function AppSearchProvider() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
AppSearchProvider.prototype = {
|
||||
__proto__: BaseAppSearchProvider.prototype,
|
||||
|
||||
_init: function() {
|
||||
BaseAppSearchProvider.prototype._init.call(this, _("APPLICATIONS"));
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._appSys.initial_search(false, terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._appSys.subsearch(false, previousResults, terms);
|
||||
},
|
||||
|
||||
expandSearch: function(terms) {
|
||||
log("TODO expand search");
|
||||
}
|
||||
}
|
||||
|
||||
function PrefsSearchProvider() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
PrefsSearchProvider.prototype = {
|
||||
__proto__: BaseAppSearchProvider.prototype,
|
||||
|
||||
_init: function() {
|
||||
BaseAppSearchProvider.prototype._init.call(this, _("PREFERENCES"));
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._appSys.initial_search(true, terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._appSys.subsearch(true, previousResults, terms);
|
||||
},
|
||||
|
||||
expandSearch: function(terms) {
|
||||
let controlCenter = this._appSys.load_from_desktop_file('gnomecc.desktop');
|
||||
controlCenter.launch();
|
||||
Main.overview.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function AppIcon(app) {
|
||||
this._init(app);
|
||||
}
|
||||
|
||||
AppIcon.prototype = {
|
||||
_init : function(app) {
|
||||
this.app = app;
|
||||
|
||||
this._glowExtendVertical = 0;
|
||||
this._glowShrinkHorizontal = 0;
|
||||
|
||||
this.actor = new St.Clickable({ style_class: 'app-well-app',
|
||||
reactive: true });
|
||||
this.actor = new St.Bin({ style_class: 'app-icon',
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMapped));
|
||||
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._rerenderGlow));
|
||||
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
this.actor.set_child(box);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
|
||||
this._menu = null;
|
||||
|
||||
this.icon = this.app.create_icon_texture(APPICON_SIZE);
|
||||
|
||||
box.add(this.icon, { expand: true, x_fill: false, y_fill: false });
|
||||
@ -262,17 +329,9 @@ BaseWellItem.prototype = {
|
||||
this._glowBox.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||
this._nameBox.add_actor(this._glowBox);
|
||||
this._glowBox.lower(this._name);
|
||||
this._appWindowChangedId = this.app.connect('windows-changed', Lang.bind(this, this._rerenderGlow));
|
||||
this._rerenderGlow();
|
||||
this._appWindowChangedId = this.app.connect('windows-changed', Lang.bind(this, this._queueRerenderGlow));
|
||||
|
||||
box.add(nameBox);
|
||||
|
||||
this._draggable = DND.makeDraggable(this.actor, true);
|
||||
this._dragStartX = null;
|
||||
this._dragStartY = null;
|
||||
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChange));
|
||||
},
|
||||
|
||||
_nameBoxGetPreferredWidth: function (nameBox, forHeight, alloc) {
|
||||
@ -319,18 +378,24 @@ BaseWellItem.prototype = {
|
||||
this.app.disconnect(this._appWindowChangedId);
|
||||
},
|
||||
|
||||
_onMapped: function() {
|
||||
if (!this._queuedGlowRerender)
|
||||
return;
|
||||
this._queuedGlowRerender = false;
|
||||
this._rerenderGlow();
|
||||
_queueRerenderGlow: function() {
|
||||
Main.queueDeferredWork(this._workId);
|
||||
},
|
||||
|
||||
_onStyleChanged: function() {
|
||||
let themeNode = this._glowBox.get_theme_node();
|
||||
|
||||
let success, len;
|
||||
[success, len] = themeNode.get_length('-shell-glow-extend-vertical', false);
|
||||
if (success)
|
||||
this._glowExtendVertical = len;
|
||||
[success, len] = themeNode.get_length('-shell-glow-shrink-horizontal', false);
|
||||
if (success)
|
||||
this._glowShrinkHorizontal = len;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
_rerenderGlow: function() {
|
||||
if (!this.actor.mapped) {
|
||||
this._queuedGlowRerender = true;
|
||||
return;
|
||||
}
|
||||
this._glowBox.destroy_children();
|
||||
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
|
||||
let windows = this.app.get_windows();
|
||||
@ -340,6 +405,34 @@ BaseWellItem.prototype = {
|
||||
glow.keep_aspect_ratio = false;
|
||||
this._glowBox.add(glow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function AppWellIcon(app) {
|
||||
this._init(app);
|
||||
}
|
||||
|
||||
AppWellIcon.prototype = {
|
||||
_init : function(app) {
|
||||
this.app = app;
|
||||
this.actor = new St.Clickable({ style_class: 'app-well-app',
|
||||
reactive: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._icon = new AppIcon(app);
|
||||
this.actor.set_child(this._icon.actor);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
this._menu = null;
|
||||
|
||||
this._draggable = DND.makeDraggable(this.actor, true);
|
||||
this._dragStartX = null;
|
||||
this._dragStartY = null;
|
||||
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChange));
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
@ -354,7 +447,7 @@ BaseWellItem.prototype = {
|
||||
if (this.actor.pressed && this._dragStartX != null) {
|
||||
this.actor.fake_release();
|
||||
this._draggable.startDrag(this._dragStartX, this._dragStartY,
|
||||
Main.currentTime());
|
||||
global.get_current_time());
|
||||
} else {
|
||||
this._dragStartX = null;
|
||||
this._dragStartY = null;
|
||||
@ -374,19 +467,6 @@ BaseWellItem.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
_onStyleChanged: function() {
|
||||
let themeNode = this._glowBox.get_theme_node();
|
||||
|
||||
let success, len;
|
||||
[success, len] = themeNode.get_length('-shell-glow-extend-vertical', false);
|
||||
if (success)
|
||||
this._glowExtendVertical = len;
|
||||
[success, len] = themeNode.get_length('-shell-glow-shrink-horizontal', false);
|
||||
if (success)
|
||||
this._glowShrinkHorizontal = len;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
popupMenu: function(activatingButton) {
|
||||
if (!this._menu) {
|
||||
this._menu = new AppIconMenu(this);
|
||||
@ -410,13 +490,60 @@ BaseWellItem.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
// Default implementations; AppDisplay.RunningWellItem overrides these
|
||||
highlightWindow: function(window) {
|
||||
this.emit('highlight-window', window);
|
||||
activateMostRecentWindow: function () {
|
||||
let mostRecentWindow = this.app.get_windows()[0];
|
||||
Main.overview.activateWindow(mostRecentWindow, global.get_current_time());
|
||||
},
|
||||
|
||||
activateWindow: function(window) {
|
||||
this.emit('activate-window', window);
|
||||
highlightWindow: function(metaWindow) {
|
||||
if (!this._getRunning())
|
||||
return;
|
||||
Main.overview.getWorkspacesForWindow(metaWindow).setHighlightWindow(metaWindow);
|
||||
},
|
||||
|
||||
activateWindow: function(metaWindow) {
|
||||
if (metaWindow) {
|
||||
this._didActivateWindow = true;
|
||||
Main.overview.activateWindow(metaWindow, global.get_current_time());
|
||||
} else
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
_onMenuPoppedUp: function() {
|
||||
if (this._getRunning()) {
|
||||
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id());
|
||||
this._setWindowSelection = true;
|
||||
}
|
||||
},
|
||||
|
||||
_onMenuPoppedDown: function() {
|
||||
if (this._didActivateWindow)
|
||||
return;
|
||||
if (!this._setWindowSelection)
|
||||
return;
|
||||
|
||||
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(null);
|
||||
this._setWindowSelection = false;
|
||||
},
|
||||
|
||||
_getRunning: function() {
|
||||
return this.app.get_windows().length > 0;
|
||||
},
|
||||
|
||||
_onActivate: function (event) {
|
||||
let running = this._getRunning();
|
||||
|
||||
if (!running) {
|
||||
this.app.launch();
|
||||
} else {
|
||||
let modifiers = Shell.get_event_state(event);
|
||||
|
||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
|
||||
this.app.launch();
|
||||
} else {
|
||||
this.activateMostRecentWindow();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
shellWorkspaceLaunch : function() {
|
||||
@ -441,7 +568,7 @@ BaseWellItem.prototype = {
|
||||
return this.actor;
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(BaseWellItem.prototype);
|
||||
Signals.addSignalMethods(AppWellIcon.prototype);
|
||||
|
||||
function AppIconMenu(source) {
|
||||
this._init(source);
|
||||
@ -607,7 +734,7 @@ AppIconMenu.prototype = {
|
||||
|
||||
this._redisplay();
|
||||
|
||||
this._windowContainer.popup(activatingButton, Main.currentTime());
|
||||
this._windowContainer.popup(activatingButton, global.get_current_time());
|
||||
|
||||
this.emit('popup', true);
|
||||
|
||||
@ -741,80 +868,6 @@ AppIconMenu.prototype = {
|
||||
};
|
||||
Signals.addSignalMethods(AppIconMenu.prototype);
|
||||
|
||||
function RunningWellItem(app, isFavorite) {
|
||||
this._init(app, isFavorite);
|
||||
}
|
||||
|
||||
RunningWellItem.prototype = {
|
||||
__proto__: BaseWellItem.prototype,
|
||||
|
||||
_init: function(app, isFavorite) {
|
||||
BaseWellItem.prototype._init.call(this, app, isFavorite);
|
||||
},
|
||||
|
||||
_onActivate: function (event) {
|
||||
let modifiers = Shell.get_event_state(event);
|
||||
|
||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
|
||||
this.app.launch();
|
||||
} else {
|
||||
this.activateMostRecentWindow();
|
||||
}
|
||||
},
|
||||
|
||||
activateMostRecentWindow: function () {
|
||||
let mostRecentWindow = this.app.get_windows()[0];
|
||||
Main.overview.activateWindow(mostRecentWindow, Main.currentTime());
|
||||
},
|
||||
|
||||
highlightWindow: function(metaWindow) {
|
||||
Main.overview.getWorkspacesForWindow(metaWindow).setHighlightWindow(metaWindow);
|
||||
},
|
||||
|
||||
activateWindow: function(metaWindow) {
|
||||
if (metaWindow) {
|
||||
this._didActivateWindow = true;
|
||||
Main.overview.activateWindow(metaWindow, Main.currentTime());
|
||||
} else
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
_onMenuPoppedUp: function() {
|
||||
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id());
|
||||
},
|
||||
|
||||
_onMenuPoppedDown: function() {
|
||||
if (this._didActivateWindow)
|
||||
return;
|
||||
|
||||
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(null);
|
||||
}
|
||||
};
|
||||
|
||||
function InactiveWellItem(app, isFavorite) {
|
||||
this._init(app, isFavorite);
|
||||
}
|
||||
|
||||
InactiveWellItem.prototype = {
|
||||
__proto__: BaseWellItem.prototype,
|
||||
|
||||
_init : function(app, isFavorite) {
|
||||
BaseWellItem.prototype._init.call(this, app, isFavorite);
|
||||
},
|
||||
|
||||
_onActivate: function(event) {
|
||||
this.app.launch();
|
||||
Main.overview.hide();
|
||||
return true;
|
||||
},
|
||||
|
||||
_onMenuPoppedUp: function() {
|
||||
},
|
||||
|
||||
_onMenuPoppedDown: function() {
|
||||
}
|
||||
};
|
||||
|
||||
function WellGrid() {
|
||||
this._init();
|
||||
}
|
||||
@ -956,8 +1009,7 @@ AppWell.prototype = {
|
||||
x_align: Big.BoxAlignment.CENTER });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._pendingRedisplay = false;
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify));
|
||||
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
||||
|
||||
this._grid = new WellGrid();
|
||||
this.actor.append(this._grid.actor, Big.BoxPackFlags.EXPAND);
|
||||
@ -965,12 +1017,9 @@ AppWell.prototype = {
|
||||
this._tracker = Shell.WindowTracker.get_default();
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, this._redisplay));
|
||||
|
||||
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._redisplay));
|
||||
this._tracker.connect('app-running-changed', Lang.bind(this, this._redisplay));
|
||||
|
||||
this._redisplay();
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
||||
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
|
||||
this._tracker.connect('app-running-changed', Lang.bind(this, this._queueRedisplay));
|
||||
},
|
||||
|
||||
_appIdListToHash: function(apps) {
|
||||
@ -980,20 +1029,11 @@ AppWell.prototype = {
|
||||
return ids;
|
||||
},
|
||||
|
||||
_onMappedNotify: function() {
|
||||
let mapped = this.actor.mapped;
|
||||
if (mapped && this._pendingRedisplay)
|
||||
this._redisplay();
|
||||
_queueRedisplay: function () {
|
||||
Main.queueDeferredWork(this._workId);
|
||||
},
|
||||
|
||||
_redisplay: function () {
|
||||
let mapped = this.actor.mapped;
|
||||
if (!mapped) {
|
||||
this._pendingRedisplay = true;
|
||||
return;
|
||||
}
|
||||
this._pendingRedisplay = false;
|
||||
|
||||
this._grid.removeAll();
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
||||
@ -1007,12 +1047,7 @@ AppWell.prototype = {
|
||||
let nFavorites = 0;
|
||||
for (let id in favorites) {
|
||||
let app = favorites[id];
|
||||
let display;
|
||||
if (app.get_windows().length > 0) {
|
||||
display = new RunningWellItem(app, true);
|
||||
} else {
|
||||
display = new InactiveWellItem(app, true);
|
||||
}
|
||||
let display = new AppWellIcon(app);
|
||||
this._grid.addItem(display.actor);
|
||||
nFavorites++;
|
||||
}
|
||||
@ -1021,7 +1056,7 @@ AppWell.prototype = {
|
||||
let app = running[i];
|
||||
if (app.get_id() in favorites)
|
||||
continue;
|
||||
let display = new RunningWellItem(app, false);
|
||||
let display = new AppWellIcon(app);
|
||||
this._grid.addItem(display.actor);
|
||||
}
|
||||
|
||||
|
@ -472,7 +472,7 @@ AppIconMenu.prototype = {
|
||||
|
||||
this._redisplay();
|
||||
|
||||
this._windowContainer.popup(activatingButton, Main.currentTime());
|
||||
this._windowContainer.popup(activatingButton, global.get_current_time());
|
||||
|
||||
this.emit('popup', true);
|
||||
|
||||
|
@ -191,6 +191,7 @@ Chrome.prototype = {
|
||||
|
||||
_windowsRestacked: function() {
|
||||
let windows = global.get_windows();
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
// The chrome layer should be visible unless there is a window
|
||||
// with layer FULLSCREEN, or a window with layer
|
||||
@ -208,17 +209,15 @@ Chrome.prototype = {
|
||||
for (let i = windows.length - 1; i > -1; i--) {
|
||||
let layer = windows[i].get_meta_window().get_layer();
|
||||
|
||||
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
|
||||
if (windows[i].x <= 0 &&
|
||||
windows[i].x + windows[i].width >= global.screen_width &&
|
||||
windows[i].y <= 0 &&
|
||||
windows[i].y + windows[i].height >= global.screen_height) {
|
||||
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT ||
|
||||
layer == Meta.StackLayer.FULLSCREEN) {
|
||||
if (windows[i].x <= primary.x &&
|
||||
windows[i].x + windows[i].width >= primary.x + primary.width &&
|
||||
windows[i].y <= primary.y &&
|
||||
windows[i].y + windows[i].height >= primary.y + primary.height) {
|
||||
this._obscuredByFullscreen = true;
|
||||
break;
|
||||
}
|
||||
} else if (layer == Meta.StackLayer.FULLSCREEN) {
|
||||
this._obscuredByFullscreen = true;
|
||||
break;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
545
js/ui/dash.js
545
js/ui/dash.js
@ -17,6 +17,10 @@ const DocDisplay = imports.ui.docDisplay;
|
||||
const PlaceDisplay = imports.ui.placeDisplay;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
const Main = imports.ui.main;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
// 25 search results (per result type) should be enough for everyone
|
||||
const MAX_RENDERED_SEARCH_RESULTS = 25;
|
||||
|
||||
const DEFAULT_PADDING = 4;
|
||||
const DEFAULT_SPACING = 4;
|
||||
@ -332,6 +336,254 @@ SearchEntry.prototype = {
|
||||
};
|
||||
Signals.addSignalMethods(SearchEntry.prototype);
|
||||
|
||||
function SearchResult(provider, metaInfo, terms) {
|
||||
this._init(provider, metaInfo, terms);
|
||||
}
|
||||
|
||||
SearchResult.prototype = {
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.provider = provider;
|
||||
this.metaInfo = metaInfo;
|
||||
this.actor = new St.Clickable({ style_class: 'dash-search-result',
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
let content = provider.createResultActor(metaInfo, terms);
|
||||
if (content == null) {
|
||||
content = new St.BoxLayout({ style_class: 'dash-search-result-content' });
|
||||
let title = new St.Label({ text: this.metaInfo['name'] });
|
||||
let icon = this.metaInfo['icon'];
|
||||
content.add(icon, { y_fill: false });
|
||||
content.add(title, { expand: true, y_fill: false });
|
||||
}
|
||||
this._content = content;
|
||||
this.actor.set_child(content);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
|
||||
},
|
||||
|
||||
setSelected: function(selected) {
|
||||
this._content.set_style_pseudo_class(selected ? 'selected' : null);
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.provider.activateResult(this.metaInfo.id);
|
||||
Main.overview.toggle();
|
||||
},
|
||||
|
||||
_onResultClicked: function(actor, event) {
|
||||
this.activate();
|
||||
}
|
||||
}
|
||||
|
||||
function OverflowSearchResults(provider) {
|
||||
this._init(provider);
|
||||
}
|
||||
|
||||
OverflowSearchResults.prototype = {
|
||||
__proto__: Search.SearchResultDisplay.prototype,
|
||||
|
||||
_init: function(provider) {
|
||||
Search.SearchResultDisplay.prototype._init.call(this, provider);
|
||||
this.actor = new St.OverflowBox({ style_class: 'dash-search-section-list-results' });
|
||||
},
|
||||
|
||||
renderResults: function(results, terms) {
|
||||
for (let i = 0; i < results.length && i < MAX_RENDERED_SEARCH_RESULTS; i++) {
|
||||
let result = results[i];
|
||||
let meta = this.provider.getResultMeta(result);
|
||||
let display = new SearchResult(this.provider, meta, terms);
|
||||
this.actor.add_actor(display.actor);
|
||||
}
|
||||
},
|
||||
|
||||
getVisibleCount: function() {
|
||||
return this.actor.get_n_visible();
|
||||
},
|
||||
|
||||
selectIndex: function(index) {
|
||||
let nVisible = this.actor.get_n_visible();
|
||||
let children = this.actor.get_children();
|
||||
if (this.selectionIndex >= 0) {
|
||||
let prevActor = children[this.selectionIndex];
|
||||
prevActor._delegate.setSelected(false);
|
||||
}
|
||||
this.selectionIndex = -1;
|
||||
if (index >= nVisible)
|
||||
return false;
|
||||
else if (index < 0)
|
||||
return false;
|
||||
let targetActor = children[index];
|
||||
targetActor._delegate.setSelected(true);
|
||||
this.selectionIndex = index;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function SearchResults(searchSystem) {
|
||||
this._init(searchSystem);
|
||||
}
|
||||
|
||||
SearchResults.prototype = {
|
||||
_init: function(searchSystem) {
|
||||
this._searchSystem = searchSystem;
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'dashSearchResults',
|
||||
vertical: true });
|
||||
this._searchingNotice = new St.Label({ style_class: 'dash-search-starting',
|
||||
text: _("Searching...") });
|
||||
this.actor.add(this._searchingNotice);
|
||||
this._selectedProvider = -1;
|
||||
this._providers = this._searchSystem.getProviders();
|
||||
this._providerMeta = [];
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
let providerBox = new St.BoxLayout({ style_class: 'dash-search-section',
|
||||
vertical: true });
|
||||
let titleButton = new St.Button({ style_class: 'dash-search-section-header',
|
||||
reactive: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
titleButton.connect('clicked', Lang.bind(this, function () { this._onHeaderClicked(provider); }));
|
||||
providerBox.add(titleButton);
|
||||
let titleBox = new St.BoxLayout();
|
||||
titleButton.set_child(titleBox);
|
||||
let title = new St.Label({ text: provider.title });
|
||||
let count = new St.Label();
|
||||
titleBox.add(title, { expand: true });
|
||||
titleBox.add(count);
|
||||
|
||||
let resultDisplayBin = new St.Bin({ style_class: 'dash-search-section-results',
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
providerBox.add(resultDisplayBin, { expand: true });
|
||||
let resultDisplay = provider.createResultContainerActor();
|
||||
if (resultDisplay == null) {
|
||||
resultDisplay = new OverflowSearchResults(provider);
|
||||
}
|
||||
resultDisplayBin.set_child(resultDisplay.actor);
|
||||
|
||||
this._providerMeta.push({ actor: providerBox,
|
||||
resultDisplay: resultDisplay,
|
||||
count: count });
|
||||
this.actor.add(providerBox);
|
||||
}
|
||||
},
|
||||
|
||||
_clearDisplay: function() {
|
||||
this._selectedProvider = -1;
|
||||
this._visibleResultsCount = 0;
|
||||
for (let i = 0; i < this._providerMeta.length; i++) {
|
||||
let meta = this._providerMeta[i];
|
||||
meta.resultDisplay.clear();
|
||||
meta.actor.hide();
|
||||
}
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._searchSystem.reset();
|
||||
this._searchingNotice.hide();
|
||||
this._clearDisplay();
|
||||
},
|
||||
|
||||
startingSearch: function() {
|
||||
this.reset();
|
||||
this._searchingNotice.show();
|
||||
},
|
||||
|
||||
_metaForProvider: function(provider) {
|
||||
return this._providerMeta[this._providers.indexOf(provider)];
|
||||
},
|
||||
|
||||
updateSearch: function (searchString) {
|
||||
let results = this._searchSystem.updateSearch(searchString);
|
||||
|
||||
this._searchingNotice.hide();
|
||||
this._clearDisplay();
|
||||
|
||||
let terms = this._searchSystem.getTerms();
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
let [provider, providerResults] = results[i];
|
||||
let meta = this._metaForProvider(provider);
|
||||
meta.actor.show();
|
||||
meta.resultDisplay.renderResults(providerResults, terms);
|
||||
meta.count.set_text(""+providerResults.length);
|
||||
}
|
||||
|
||||
this.selectDown(false);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_onHeaderClicked: function(provider) {
|
||||
provider.expandSearch(this._searchSystem.getTerms());
|
||||
},
|
||||
|
||||
_modifyActorSelection: function(resultDisplay, up) {
|
||||
let success;
|
||||
let index = resultDisplay.getSelectionIndex();
|
||||
if (up && index == -1)
|
||||
index = resultDisplay.getVisibleCount() - 1;
|
||||
else if (up)
|
||||
index = index - 1;
|
||||
else
|
||||
index = index + 1;
|
||||
return resultDisplay.selectIndex(index);
|
||||
},
|
||||
|
||||
selectUp: function(recursing) {
|
||||
for (let i = this._selectedProvider; i >= 0; i--) {
|
||||
let meta = this._providerMeta[i];
|
||||
if (!meta.actor.visible)
|
||||
continue;
|
||||
let success = this._modifyActorSelection(meta.resultDisplay, true);
|
||||
if (success) {
|
||||
this._selectedProvider = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this._providerMeta.length > 0 && !recursing) {
|
||||
this._selectedProvider = this._providerMeta.length - 1;
|
||||
this.selectUp(true);
|
||||
}
|
||||
},
|
||||
|
||||
selectDown: function(recursing) {
|
||||
let current = this._selectedProvider;
|
||||
if (current == -1)
|
||||
current = 0;
|
||||
for (let i = current; i < this._providerMeta.length; i++) {
|
||||
let meta = this._providerMeta[i];
|
||||
if (!meta.actor.visible)
|
||||
continue;
|
||||
let success = this._modifyActorSelection(meta.resultDisplay, false);
|
||||
if (success) {
|
||||
this._selectedProvider = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this._providerMeta.length > 0 && !recursing) {
|
||||
this._selectedProvider = 0;
|
||||
this.selectDown(true);
|
||||
}
|
||||
},
|
||||
|
||||
activateSelected: function() {
|
||||
let current = this._selectedProvider;
|
||||
if (current < 0)
|
||||
return;
|
||||
let meta = this._providerMeta[current];
|
||||
let resultDisplay = meta.resultDisplay;
|
||||
let children = resultDisplay.actor.get_children();
|
||||
let targetActor = children[resultDisplay.getSelectionIndex()];
|
||||
targetActor._delegate.activate();
|
||||
}
|
||||
}
|
||||
|
||||
function MoreLink() {
|
||||
this._init();
|
||||
}
|
||||
@ -344,15 +596,15 @@ MoreLink.prototype = {
|
||||
|
||||
let expander = new St.Bin({ style_class: "more-link-expander" });
|
||||
this.actor.add(expander, { expand: true, y_fill: false });
|
||||
},
|
||||
|
||||
this.actor.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;
|
||||
}));
|
||||
activate: function() {
|
||||
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) {
|
||||
@ -385,7 +637,8 @@ SectionHeader.prototype = {
|
||||
this.actor = new St.Bin({ style_class: "section-header",
|
||||
x_align: St.Align.START,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
y_fill: true,
|
||||
reactive: !suppressBrowse });
|
||||
this._innerBox = new St.BoxLayout({ style_class: "section-header-inner" });
|
||||
this.actor.set_child(this._innerBox);
|
||||
|
||||
@ -410,9 +663,14 @@ SectionHeader.prototype = {
|
||||
if (!suppressBrowse) {
|
||||
this.moreLink = new MoreLink();
|
||||
this._innerBox.add(this.moreLink.actor, { x_align: St.Align.END });
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
}
|
||||
},
|
||||
|
||||
_onButtonPress: function() {
|
||||
this.moreLink.activate();
|
||||
},
|
||||
|
||||
setTitle : function(title) {
|
||||
this.text.text = title;
|
||||
},
|
||||
@ -500,9 +758,9 @@ Dash.prototype = {
|
||||
vertical: true,
|
||||
reactive: true });
|
||||
|
||||
// Size for this one explicitly set from overlay.js
|
||||
this.searchArea = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
|
||||
|
||||
// The searchArea just holds the entry
|
||||
this.searchArea = new St.BoxLayout({ name: "dashSearchArea",
|
||||
vertical: true });
|
||||
this.sectionArea = new St.BoxLayout({ name: "dashSections",
|
||||
vertical: true });
|
||||
|
||||
@ -517,16 +775,35 @@ Dash.prototype = {
|
||||
this._searchActive = false;
|
||||
this._searchPending = false;
|
||||
this._searchEntry = new SearchEntry();
|
||||
this.searchArea.append(this._searchEntry.actor, Big.BoxPackFlags.EXPAND);
|
||||
this.searchArea.add(this._searchEntry.actor, { y_fill: false, expand: true });
|
||||
|
||||
this._searchSystem = new Search.SearchSystem();
|
||||
this._searchSystem.registerProvider(new AppDisplay.AppSearchProvider());
|
||||
this._searchSystem.registerProvider(new AppDisplay.PrefsSearchProvider());
|
||||
this._searchSystem.registerProvider(new PlaceDisplay.PlaceSearchProvider());
|
||||
this._searchSystem.registerProvider(new DocDisplay.DocSearchProvider());
|
||||
|
||||
this.searchResults = new SearchResults(this._searchSystem);
|
||||
this.actor.add(this.searchResults.actor);
|
||||
this.searchResults.actor.hide();
|
||||
|
||||
this._searchTimeoutId = 0;
|
||||
this._searchEntry.entry.connect('text-changed', Lang.bind(this, function (se, prop) {
|
||||
let text = this._searchEntry.getText();
|
||||
text = text.replace(/^\s+/g, "").replace(/\s+$/g, "")
|
||||
text = text.replace(/^\s+/g, "").replace(/\s+$/g, "");
|
||||
let searchPreviouslyActive = this._searchActive;
|
||||
this._searchActive = text != '';
|
||||
this._searchPending = this._searchActive && !searchPreviouslyActive;
|
||||
this._updateDashActors();
|
||||
if (this._searchPending) {
|
||||
this.searchResults.startingSearch();
|
||||
}
|
||||
if (this._searchActive) {
|
||||
this.searchResults.actor.show();
|
||||
this.sectionArea.hide();
|
||||
} else {
|
||||
this.searchResults.actor.hide();
|
||||
this.sectionArea.show();
|
||||
}
|
||||
if (!this._searchActive) {
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
@ -543,24 +820,15 @@ Dash.prototype = {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._doSearch();
|
||||
}
|
||||
// Only one of the displays will have an item selected, so it's ok to
|
||||
// call activateSelected() on all of them.
|
||||
for (var i = 0; i < this._searchSections.length; i++) {
|
||||
let section = this._searchSections[i];
|
||||
section.resultArea.display.activateSelected();
|
||||
}
|
||||
this.searchResults.activateSelected();
|
||||
return true;
|
||||
}));
|
||||
this._searchEntry.entry.connect('key-press-event', Lang.bind(this, function (se, e) {
|
||||
let text = this._searchEntry.getText();
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Escape) {
|
||||
// Escape will keep clearing things back to the desktop.
|
||||
// If we are showing a particular section of search, go back to all sections.
|
||||
if (this._searchResultsSingleShownSection != null)
|
||||
this._showAllSearchSections();
|
||||
// If we have an active search, we remove it.
|
||||
else if (this._searchActive)
|
||||
if (this._searchActive)
|
||||
this._searchEntry.reset();
|
||||
// Next, if we're in one of the "more" modes or showing the details pane, close them
|
||||
else if (this._activePane != null)
|
||||
@ -572,44 +840,14 @@ Dash.prototype = {
|
||||
} 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.
|
||||
for (var i = 0; i < this._searchSections.length; i++) {
|
||||
let section = this._searchSections[i];
|
||||
if (section.resultArea.display.hasSelected() && !section.resultArea.display.selectUp()) {
|
||||
if (this._searchResultsSingleShownSection != section.type) {
|
||||
// We need to move the selection to the next section above this section that has items,
|
||||
// wrapping around at the bottom, if necessary.
|
||||
let newSectionIndex = this._findAnotherSectionWithItems(i, -1);
|
||||
if (newSectionIndex >= 0) {
|
||||
this._searchSections[newSectionIndex].resultArea.display.selectLastItem();
|
||||
section.resultArea.display.unsetSelected();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.searchResults.selectUp(false);
|
||||
|
||||
return true;
|
||||
} else if (symbol == Clutter.Down) {
|
||||
if (!this._searchActive)
|
||||
return true;
|
||||
for (var i = 0; i < this._searchSections.length; i++) {
|
||||
let section = this._searchSections[i];
|
||||
if (section.resultArea.display.hasSelected() && !section.resultArea.display.selectDown()) {
|
||||
if (this._searchResultsSingleShownSection != section.type) {
|
||||
// We need to move the selection to the next section below this section that has items,
|
||||
// wrapping around at the top, if necessary.
|
||||
let newSectionIndex = this._findAnotherSectionWithItems(i, 1);
|
||||
if (newSectionIndex >= 0) {
|
||||
this._searchSections[newSectionIndex].resultArea.display.selectFirstItem();
|
||||
section.resultArea.display.unsetSelected();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.searchResults.selectDown(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -666,102 +904,12 @@ Dash.prototype = {
|
||||
this._docDisplay.emit('changed');
|
||||
|
||||
this.sectionArea.add(this._docsSection.actor, { expand: true });
|
||||
|
||||
/***** Search Results *****/
|
||||
|
||||
this._searchResultsSection = new Section(_("SEARCH RESULTS"), true);
|
||||
|
||||
this._searchResultsSingleShownSection = null;
|
||||
|
||||
this._searchResultsSection.header.connect('back-link-activated', Lang.bind(this, function () {
|
||||
this._showAllSearchSections();
|
||||
}));
|
||||
|
||||
this._searchSections = [
|
||||
{ type: APPS,
|
||||
title: _("APPLICATIONS"),
|
||||
header: null,
|
||||
resultArea: null
|
||||
},
|
||||
{ type: PREFS,
|
||||
title: _("PREFERENCES"),
|
||||
header: null,
|
||||
resultArea: null
|
||||
},
|
||||
{ type: DOCS,
|
||||
title: _("RECENT DOCUMENTS"),
|
||||
header: null,
|
||||
resultArea: null
|
||||
},
|
||||
{ type: PLACES,
|
||||
title: _("PLACES"),
|
||||
header: null,
|
||||
resultArea: null
|
||||
}
|
||||
];
|
||||
|
||||
for (var i = 0; i < this._searchSections.length; i++) {
|
||||
let section = this._searchSections[i];
|
||||
section.header = new SearchSectionHeader(section.title,
|
||||
Lang.bind(this,
|
||||
function () {
|
||||
this._showSingleSearchSection(section.type);
|
||||
}));
|
||||
this._searchResultsSection.content.add(section.header.actor);
|
||||
section.resultArea = new ResultArea(section.type, GenericDisplay.GenericDisplayFlags.DISABLE_VSCROLLING);
|
||||
this._searchResultsSection.content.add(section.resultArea.actor, { expand: true });
|
||||
createPaneForDetails(this, section.resultArea.display);
|
||||
}
|
||||
|
||||
this.sectionArea.add(this._searchResultsSection.actor, { expand: true });
|
||||
this._searchResultsSection.actor.hide();
|
||||
},
|
||||
|
||||
_doSearch: function () {
|
||||
this._searchTimeoutId = 0;
|
||||
let text = this._searchEntry.getText();
|
||||
text = text.replace(/^\s+/g, "").replace(/\s+$/g, "");
|
||||
|
||||
let selectionSet = false;
|
||||
|
||||
for (var i = 0; i < this._searchSections.length; i++) {
|
||||
let section = this._searchSections[i];
|
||||
section.resultArea.display.setSearch(text);
|
||||
let itemCount = section.resultArea.display.getMatchedItemsCount();
|
||||
let itemCountText = itemCount + "";
|
||||
section.header.countText.text = itemCountText;
|
||||
|
||||
if (this._searchResultsSingleShownSection == section.type) {
|
||||
this._searchResultsSection.header.setCountText(itemCountText);
|
||||
if (itemCount == 0) {
|
||||
section.resultArea.actor.hide();
|
||||
} else {
|
||||
section.resultArea.actor.show();
|
||||
}
|
||||
} else if (this._searchResultsSingleShownSection == null) {
|
||||
// Don't show the section if it has no results
|
||||
if (itemCount == 0) {
|
||||
section.header.actor.hide();
|
||||
section.resultArea.actor.hide();
|
||||
} else {
|
||||
section.header.actor.show();
|
||||
section.resultArea.actor.show();
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh the selection when a new search is applied.
|
||||
section.resultArea.display.unsetSelected();
|
||||
if (!selectionSet && section.resultArea.display.hasItems() &&
|
||||
(this._searchResultsSingleShownSection == null || this._searchResultsSingleShownSection == section.type)) {
|
||||
section.resultArea.display.selectFirstItem();
|
||||
selectionSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Here work around a bug that I never quite tracked down
|
||||
// the root cause of; it appeared that the search results
|
||||
// section was getting a 0 height allocation.
|
||||
this._searchResultsSection.content.queue_relayout();
|
||||
this.searchResults.updateSearch(text);
|
||||
|
||||
return false;
|
||||
},
|
||||
@ -794,101 +942,6 @@ Dash.prototype = {
|
||||
}
|
||||
}));
|
||||
Main.overview.addPane(pane);
|
||||
},
|
||||
|
||||
_updateDashActors: function() {
|
||||
if (this._searchPending) {
|
||||
this._searchResultsSection.actor.show();
|
||||
// We initially hide all sections when we start a search. When the search timeout
|
||||
// first runs, the sections that have matching results are shown. As the search
|
||||
// is refined, only the sections that have matching results will be shown.
|
||||
for (let i = 0; i < this._searchSections.length; i++) {
|
||||
let section = this._searchSections[i];
|
||||
section.header.actor.hide();
|
||||
section.resultArea.actor.hide();
|
||||
}
|
||||
this._appsSection.actor.hide();
|
||||
this._placesSection.actor.hide();
|
||||
this._docsSection.actor.hide();
|
||||
} else if (!this._searchActive) {
|
||||
this._showAllSearchSections();
|
||||
this._searchResultsSection.actor.hide();
|
||||
this._appsSection.actor.show();
|
||||
this._placesSection.actor.show();
|
||||
this._docsSection.actor.show();
|
||||
}
|
||||
},
|
||||
|
||||
_showSingleSearchSection: function(type) {
|
||||
// We currently don't allow going from showing one section to showing another section.
|
||||
if (this._searchResultsSingleShownSection != null) {
|
||||
throw new Error("We were already showing a single search section: '" + this._searchResultsSingleShownSection
|
||||
+ "' when _showSingleSearchSection() was called for '" + type + "'");
|
||||
}
|
||||
for (var i = 0; i < this._searchSections.length; i++) {
|
||||
let section = this._searchSections[i];
|
||||
if (section.type == type) {
|
||||
// This will be the only section shown.
|
||||
section.resultArea.display.selectFirstItem();
|
||||
let itemCount = section.resultArea.display.getMatchedItemsCount();
|
||||
let itemCountText = itemCount + "";
|
||||
section.header.actor.hide();
|
||||
this._searchResultsSection.header.setTitle(section.title);
|
||||
this._searchResultsSection.header.setBackLinkVisible(true);
|
||||
this._searchResultsSection.header.setCountText(itemCountText);
|
||||
} else {
|
||||
// We need to hide this section.
|
||||
section.header.actor.hide();
|
||||
section.resultArea.actor.hide();
|
||||
section.resultArea.display.unsetSelected();
|
||||
}
|
||||
}
|
||||
this._searchResultsSingleShownSection = type;
|
||||
},
|
||||
|
||||
_showAllSearchSections: function() {
|
||||
if (this._searchResultsSingleShownSection != null) {
|
||||
let selectionSet = false;
|
||||
for (var i = 0; i < this._searchSections.length; i++) {
|
||||
let section = this._searchSections[i];
|
||||
if (section.type == this._searchResultsSingleShownSection) {
|
||||
// This will no longer be the only section shown.
|
||||
let itemCount = section.resultArea.display.getMatchedItemsCount();
|
||||
if (itemCount != 0) {
|
||||
section.header.actor.show();
|
||||
section.resultArea.display.selectFirstItem();
|
||||
selectionSet = true;
|
||||
}
|
||||
this._searchResultsSection.header.setTitle(_("SEARCH RESULTS"));
|
||||
this._searchResultsSection.header.setBackLinkVisible(false);
|
||||
this._searchResultsSection.header.setCountText("");
|
||||
} else {
|
||||
// We need to restore this section.
|
||||
let itemCount = section.resultArea.display.getMatchedItemsCount();
|
||||
if (itemCount != 0) {
|
||||
section.header.actor.show();
|
||||
section.resultArea.actor.show();
|
||||
// This ensures that some other section will have the selection if the
|
||||
// single section that was being displayed did not have any items.
|
||||
if (!selectionSet) {
|
||||
section.resultArea.display.selectFirstItem();
|
||||
selectionSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this._searchResultsSingleShownSection = null;
|
||||
}
|
||||
},
|
||||
|
||||
_findAnotherSectionWithItems: function(index, increment) {
|
||||
let pos = _getIndexWrapped(index, increment, this._searchSections.length);
|
||||
while (pos != index) {
|
||||
if (this._searchSections[pos].resultArea.display.hasItems())
|
||||
return pos;
|
||||
pos = _getIndexWrapped(pos, increment, this._searchSections.length);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(Dash.prototype);
|
||||
|
@ -10,12 +10,16 @@ const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const DocInfo = imports.misc.docInfo;
|
||||
const DND = imports.ui.dnd;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
const Main = imports.ui.main;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const MAX_DASH_DOCS = 50;
|
||||
const DASH_DOCS_ICON_SIZE = 16;
|
||||
|
||||
const DEFAULT_SPACING = 4;
|
||||
@ -152,7 +156,8 @@ DocDisplay.prototype = {
|
||||
_refreshCache : function() {
|
||||
if (!this._docsStale)
|
||||
return true;
|
||||
this._allItems = this._docManager.getItems();
|
||||
this._allItems = {};
|
||||
Lang.copyProperties(this._docManager.getInfosByUri(), this._allItems);
|
||||
this._docsStale = false;
|
||||
return false;
|
||||
},
|
||||
@ -177,13 +182,8 @@ DocDisplay.prototype = {
|
||||
this._matchedItemKeys = [];
|
||||
let docIdsToRemove = [];
|
||||
for (docId in this._allItems) {
|
||||
// this._allItems[docId].exists() checks if the resource still exists
|
||||
if (this._allItems[docId].exists()) {
|
||||
this._matchedItems[docId] = 1;
|
||||
this._matchedItemKeys.push(docId);
|
||||
} else {
|
||||
docIdsToRemove.push(docId);
|
||||
}
|
||||
this._matchedItems[docId] = 1;
|
||||
this._matchedItemKeys.push(docId);
|
||||
}
|
||||
|
||||
for (docId in docIdsToRemove) {
|
||||
@ -275,16 +275,21 @@ DashDocDisplayItem.prototype = {
|
||||
Main.overview.hide();
|
||||
}));
|
||||
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
|
||||
let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
|
||||
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
||||
let name = new St.Label({ style_class: "dash-recent-docs-item",
|
||||
let name = new St.Label({ style_class: 'dash-recent-docs-item',
|
||||
text: docInfo.name });
|
||||
this.actor.append(name, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
this.actor._delegate = this;
|
||||
},
|
||||
|
||||
getUri: function() {
|
||||
return this._info.uri;
|
||||
},
|
||||
|
||||
getDragActorSource: function() {
|
||||
@ -316,12 +321,14 @@ DashDocDisplay.prototype = {
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
||||
|
||||
this._actorsByUri = {};
|
||||
|
||||
this._docManager = DocInfo.getDocManager();
|
||||
this._docManager.connect('changed', Lang.bind(this, function(mgr) {
|
||||
this._redisplay();
|
||||
}));
|
||||
this._redisplay();
|
||||
this._docManager.connect('changed', Lang.bind(this, this._onDocsChanged));
|
||||
this._pendingDocsChange = true;
|
||||
this._checkDocExistence = false;
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
@ -355,15 +362,17 @@ DashDocDisplay.prototype = {
|
||||
|
||||
let firstColumnChildren = Math.ceil(children.length / 2);
|
||||
|
||||
let natural = 0;
|
||||
for (let i = 0; i < firstColumnChildren; i++) {
|
||||
let child = children[i];
|
||||
let [minSize, naturalSize] = child.get_preferred_height(forWidth);
|
||||
alloc.natural_size += naturalSize;
|
||||
let [minSize, naturalSize] = child.get_preferred_height(-1);
|
||||
natural += naturalSize;
|
||||
|
||||
if (i > 0 && i < children.length - 1) {
|
||||
alloc.natural_size += DEFAULT_SPACING;
|
||||
natural += DEFAULT_SPACING;
|
||||
}
|
||||
}
|
||||
alloc.natural_size = natural;
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
@ -418,28 +427,49 @@ DashDocDisplay.prototype = {
|
||||
i++;
|
||||
}
|
||||
|
||||
// Everything else didn't fit, just hide it.
|
||||
for (; i < children.length; i++) {
|
||||
children[i].hide();
|
||||
if (this._checkDocExistence) {
|
||||
// Now we know how many docs we are displaying, queue a check to see if any of them
|
||||
// have been deleted. If they are deleted, then we'll get a 'changed' signal; since
|
||||
// we'll now be displaying items we weren't previously, we'll check again to see
|
||||
// if they were deleted, and so forth and so on.
|
||||
// TODO: We should change this to ask for as many as we can fit in the given space:
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=603522#c23
|
||||
this._docManager.queueExistenceCheck(i);
|
||||
this._checkDocExistence = false;
|
||||
}
|
||||
|
||||
let skipPaint = [];
|
||||
for (; i < children.length; i++)
|
||||
this.actor.set_skip_paint(children[i], true);
|
||||
},
|
||||
|
||||
_onDocsChanged: function() {
|
||||
this._checkDocExistence = true;
|
||||
Main.queueDeferredWork(this._workId);
|
||||
},
|
||||
|
||||
_redisplay: function() {
|
||||
// Should be kept alive by the _actorsByUri
|
||||
this.actor.remove_all();
|
||||
|
||||
let docs = this._docManager.getItems();
|
||||
let docUrls = [];
|
||||
for (let url in docs) {
|
||||
docUrls.push(url);
|
||||
let docs = this._docManager.getTimestampOrderedInfos();
|
||||
for (let i = 0; i < docs.length && i < MAX_DASH_DOCS; i++) {
|
||||
let doc = docs[i];
|
||||
let display = this._actorsByUri[doc.uri];
|
||||
if (display) {
|
||||
this.actor.add_actor(display.actor);
|
||||
} else {
|
||||
let display = new DashDocDisplayItem(doc);
|
||||
this.actor.add_actor(display.actor);
|
||||
this._actorsByUri[doc.uri] = display;
|
||||
}
|
||||
}
|
||||
docUrls.sort(function (urlA, urlB) { return docs[urlB].timestamp - docs[urlA].timestamp; });
|
||||
let textureCache = Shell.TextureCache.get_default();
|
||||
|
||||
for (let i = 0; i < docUrls.length; i++) {
|
||||
let url = docUrls[i];
|
||||
let docInfo = docs[url];
|
||||
let display = new DashDocDisplayItem(docInfo);
|
||||
this.actor.add_actor(display.actor);
|
||||
// Any unparented actors must have been deleted
|
||||
for (let uri in this._actorsByUri) {
|
||||
let display = this._actorsByUri[uri];
|
||||
if (display.actor.get_parent() == null) {
|
||||
display.actor.destroy();
|
||||
delete this._actorsByUri[uri];
|
||||
}
|
||||
}
|
||||
this.emit('changed');
|
||||
}
|
||||
@ -447,3 +477,41 @@ DashDocDisplay.prototype = {
|
||||
|
||||
Signals.addSignalMethods(DashDocDisplay.prototype);
|
||||
|
||||
function DocSearchProvider() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DocSearchProvider.prototype = {
|
||||
__proto__: Search.SearchProvider.prototype,
|
||||
|
||||
_init: function(name) {
|
||||
Search.SearchProvider.prototype._init.call(this, _("DOCUMENTS"));
|
||||
this._docManager = DocInfo.getDocManager();
|
||||
},
|
||||
|
||||
getResultMeta: function(resultId) {
|
||||
let docInfo = this._docManager.lookupByUri(resultId);
|
||||
if (!docInfo)
|
||||
return null;
|
||||
return { 'id': resultId,
|
||||
'name': docInfo.name,
|
||||
'icon': docInfo.createIcon(Search.RESULT_ICON_SIZE)};
|
||||
},
|
||||
|
||||
activateResult: function(id) {
|
||||
let docInfo = this._docManager.lookupByUri(id);
|
||||
docInfo.launch();
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
return this._docManager.initialSearch(terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
return this._docManager.subsearch(previousResults, terms);
|
||||
},
|
||||
|
||||
expandSearch: function(terms) {
|
||||
log("TODO expand docs search");
|
||||
}
|
||||
};
|
||||
|
158
js/ui/extensionSystem.js
Normal file
158
js/ui/extensionSystem.js
Normal file
@ -0,0 +1,158 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const ExtensionState = {
|
||||
ENABLED: 1,
|
||||
DISABLED: 2,
|
||||
ERROR: 3,
|
||||
OUT_OF_DATE: 4
|
||||
};
|
||||
|
||||
const ExtensionType = {
|
||||
SYSTEM: 1,
|
||||
PER_USER: 2
|
||||
};
|
||||
|
||||
// Maps uuid -> metadata object
|
||||
const extensionMeta = {};
|
||||
// Maps uuid -> importer object (extension directory tree)
|
||||
const extensions = {};
|
||||
// Array of uuids
|
||||
var disabledExtensions;
|
||||
// GFile for user extensions
|
||||
var userExtensionsDir = null;
|
||||
|
||||
function loadExtension(dir, enabled, type) {
|
||||
let info;
|
||||
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
|
||||
|
||||
let metadataFile = dir.get_child('metadata.json');
|
||||
if (!metadataFile.query_exists(null)) {
|
||||
global.logError(baseErrorString + 'Missing metadata.json');
|
||||
return;
|
||||
}
|
||||
|
||||
let [success, metadataContents, len, etag] = metadataFile.load_contents(null);
|
||||
let meta;
|
||||
try {
|
||||
meta = JSON.parse(metadataContents);
|
||||
} catch (e) {
|
||||
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
|
||||
return;
|
||||
}
|
||||
let requiredProperties = ['uuid', 'name', 'description'];
|
||||
for (let i = 0; i < requiredProperties; i++) {
|
||||
let prop = requiredProperties[i];
|
||||
if (!meta[prop]) {
|
||||
global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Encourage people to add this
|
||||
if (!meta['url']) {
|
||||
global.log(baseErrorString + 'Warning: Missing "url" property in metadata.json');
|
||||
}
|
||||
|
||||
let base = dir.get_basename();
|
||||
if (base != meta.uuid) {
|
||||
global.logError(baseErrorString + 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + base + '"');
|
||||
return;
|
||||
}
|
||||
|
||||
extensionMeta[meta.uuid] = meta;
|
||||
extensionMeta[meta.uuid].type = type;
|
||||
extensionMeta[meta.uuid].path = dir.get_path();
|
||||
if (!enabled) {
|
||||
extensionMeta[meta.uuid].state = ExtensionState.DISABLED;
|
||||
return;
|
||||
}
|
||||
|
||||
// Default to error, we set success as the last step
|
||||
extensionMeta[meta.uuid].state = ExtensionState.ERROR;
|
||||
|
||||
let extensionJs = dir.get_child('extension.js');
|
||||
if (!extensionJs.query_exists(null)) {
|
||||
global.logError(baseErrorString + 'Missing extension.js');
|
||||
return;
|
||||
}
|
||||
let stylesheetPath = null;
|
||||
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
||||
let theme = themeContext.get_theme();
|
||||
let stylesheetFile = dir.get_child('stylesheet.css');
|
||||
if (stylesheetFile.query_exists(null)) {
|
||||
try {
|
||||
theme.load_stylesheet(stylesheetFile.get_path());
|
||||
} catch (e) {
|
||||
global.logError(baseErrorString + 'Stylesheet parse error: ' + e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let extensionModule;
|
||||
try {
|
||||
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
|
||||
extensionModule = extensions[meta.uuid].extension;
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
global.logError(baseErrorString + e);
|
||||
return;
|
||||
}
|
||||
if (!extensionModule.main) {
|
||||
global.logError(baseErrorString + 'missing \'main\' function');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
extensionModule.main();
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
global.logError(baseErrorString + 'Failed to evaluate main function:' + e);
|
||||
return;
|
||||
}
|
||||
extensionMeta[meta.uuid].state = ExtensionState.ENABLED;
|
||||
global.log('Loaded extension ' + meta.uuid);
|
||||
}
|
||||
|
||||
function init() {
|
||||
let userConfigPath = GLib.get_user_config_dir();
|
||||
let userExtensionsPath = GLib.build_filenamev([userConfigPath, 'gnome-shell', 'extensions']);
|
||||
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
|
||||
try {
|
||||
userExtensionsDir.make_directory_with_parents(null);
|
||||
} catch (e) {
|
||||
global.logError(""+e);
|
||||
}
|
||||
|
||||
disabledExtensions = Shell.GConf.get_default().get_string_list('disabled_extensions');
|
||||
}
|
||||
|
||||
function _loadExtensionsIn(dir, type) {
|
||||
let fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||
let file, info;
|
||||
while ((info = fileEnum.next_file(null)) != null) {
|
||||
let fileType = info.get_file_type();
|
||||
if (fileType != Gio.FileType.DIRECTORY)
|
||||
continue;
|
||||
let name = info.get_name();
|
||||
let enabled = disabledExtensions.indexOf(name) < 0;
|
||||
let child = dir.get_child(name);
|
||||
loadExtension(child, enabled, type);
|
||||
}
|
||||
fileEnum.close(null);
|
||||
}
|
||||
|
||||
function loadExtensions() {
|
||||
_loadExtensionsIn(userExtensionsDir, ExtensionType.PER_USER);
|
||||
let systemDataDirs = GLib.get_system_data_dirs();
|
||||
for (let i = 0; i < systemDataDirs.length; i++) {
|
||||
let dirPath = systemDataDirs[i] + '/gnome-shell/extensions';
|
||||
let dir = Gio.file_new_for_path(dirPath);
|
||||
if (dir.query_exists(null))
|
||||
_loadExtensionsIn(dir, ExtensionType.SYSTEM);
|
||||
}
|
||||
}
|
@ -14,7 +14,6 @@ const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Link = imports.ui.link;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const RedisplayFlags = { NONE: 0,
|
||||
|
@ -3,78 +3,21 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
// Link is a clickable link. Right now it just handles properly capturing
|
||||
// press and release events and short-circuiting the button handling in
|
||||
// ClutterText, but more features like different colors for hover/pressed states
|
||||
// or a different mouse cursor could be implemented.
|
||||
//
|
||||
// The properties passed in are forwarded to the Clutter.Text() constructor,
|
||||
// so can include, 'text', 'font_name', etc.
|
||||
function Link(props) {
|
||||
this._init(props);
|
||||
}
|
||||
|
||||
Link.prototype = {
|
||||
_init : function(props) {
|
||||
let realProps = { reactive: true };
|
||||
let realProps = { reactive: true,
|
||||
style_class: 'shell-link' };
|
||||
// The user can pass in reactive: false to override the above and get
|
||||
// a non-reactive link (a link to the current page, perhaps)
|
||||
Lang.copyProperties(props, realProps);
|
||||
Lang.copyProperties(props, realProps);
|
||||
|
||||
this.actor = new Clutter.Text(realProps);
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
|
||||
this.actor.connect('enter-event', Lang.bind(this, this._onEnter));
|
||||
this.actor.connect('leave-event', Lang.bind(this, this._onLeave));
|
||||
|
||||
this._buttonDown = false;
|
||||
this._havePointer = false;
|
||||
},
|
||||
|
||||
// Update the text of the link
|
||||
setText : function(text) {
|
||||
this.actor.text = text;
|
||||
},
|
||||
|
||||
// We want to react on buttonDown, but if we override button-release-event for
|
||||
// ClutterText, but not button-press-event, we get a stuck grab. Tracking
|
||||
// buttonDown and doing the grab isn't really necessary, but doing it makes
|
||||
// the behavior perfectly correct if the user clicks on one actor, drags
|
||||
// to another and releases - that should not trigger either actor.
|
||||
_onButtonPress : function(actor, event) {
|
||||
this._buttonDown = true;
|
||||
this._havePointer = true; // Hack to work around poor enter/leave tracking in Clutter
|
||||
Clutter.grab_pointer(actor);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_onButtonRelease : function(actor, event) {
|
||||
if (this._buttonDown) {
|
||||
this._buttonDown = false;
|
||||
Clutter.ungrab_pointer(actor);
|
||||
|
||||
if (this._havePointer)
|
||||
this.emit('clicked');
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_onEnter : function(actor, event) {
|
||||
if (event.get_source() == actor)
|
||||
this._havePointer = true;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onLeave : function(actor, event) {
|
||||
if (event.get_source() == actor)
|
||||
this._havePointer = false;
|
||||
|
||||
return false;
|
||||
this.actor = new St.Button(realProps);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9,7 +9,11 @@ const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const Link = imports.ui.link;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
@ -39,24 +43,21 @@ Notebook.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ vertical: true });
|
||||
|
||||
this.tabControls = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
spacing: 4, padding: 2 });
|
||||
this.tabControls = new St.BoxLayout({ style_class: "labels" });
|
||||
|
||||
this._selectedIndex = -1;
|
||||
this._tabs = [];
|
||||
},
|
||||
|
||||
appendPage: function(name, child) {
|
||||
let labelOuterBox = new Big.Box({ padding: 2 });
|
||||
let labelBox = new St.BoxLayout({ reactive: true });
|
||||
labelOuterBox.append(labelBox, Big.BoxPackFlags.NONE);
|
||||
let label = new St.Label({ text: name });
|
||||
labelBox.connect('button-press-event', Lang.bind(this, function () {
|
||||
let labelBox = new St.BoxLayout({ style_class: "notebook-tab" });
|
||||
let label = new St.Button({ label: name });
|
||||
label.connect('clicked', Lang.bind(this, function () {
|
||||
this.selectChild(child);
|
||||
return true;
|
||||
}));
|
||||
labelBox.add(label, { expand: true });
|
||||
this.tabControls.append(labelOuterBox, Big.BoxPackFlags.NONE);
|
||||
this.tabControls.add(labelBox);
|
||||
|
||||
let scrollview = new St.ScrollView({ x_fill: true, y_fill: true });
|
||||
scrollview.get_hscroll_bar().hide();
|
||||
@ -64,6 +65,7 @@ Notebook.prototype = {
|
||||
|
||||
let tabData = { child: child,
|
||||
labelBox: labelBox,
|
||||
label: label,
|
||||
scrollView: scrollview,
|
||||
_scrollToBottom: false };
|
||||
this._tabs.push(tabData);
|
||||
@ -82,8 +84,7 @@ Notebook.prototype = {
|
||||
if (this._selectedIndex < 0)
|
||||
return;
|
||||
let tabData = this._tabs[this._selectedIndex];
|
||||
tabData.labelBox.padding = 2;
|
||||
tabData.labelBox.border = 0;
|
||||
tabData.labelBox.set_style_pseudo_class(null);
|
||||
tabData.scrollView.hide();
|
||||
this._selectedIndex = -1;
|
||||
},
|
||||
@ -97,8 +98,7 @@ Notebook.prototype = {
|
||||
return;
|
||||
}
|
||||
let tabData = this._tabs[index];
|
||||
tabData.labelBox.padding = 1;
|
||||
tabData.labelBox.border = 1;
|
||||
tabData.labelBox.set_style_pseudo_class('selected');
|
||||
tabData.scrollView.show();
|
||||
this._selectedIndex = index;
|
||||
this.emit('selection', tabData.child);
|
||||
@ -308,6 +308,135 @@ Inspector.prototype = {
|
||||
|
||||
Signals.addSignalMethods(Inspector.prototype);
|
||||
|
||||
function ErrorLog() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
ErrorLog.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout();
|
||||
this.text = new St.Label();
|
||||
this.actor.add(this.text);
|
||||
this.text.clutter_text.line_wrap = true;
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
|
||||
},
|
||||
|
||||
_formatTime: function(d){
|
||||
function pad(n) { return n < 10 ? '0' + n : n };
|
||||
return d.getUTCFullYear()+'-'
|
||||
+ pad(d.getUTCMonth()+1)+'-'
|
||||
+ pad(d.getUTCDate())+'T'
|
||||
+ pad(d.getUTCHours())+':'
|
||||
+ pad(d.getUTCMinutes())+':'
|
||||
+ pad(d.getUTCSeconds())+'Z'
|
||||
},
|
||||
|
||||
_renderText: function() {
|
||||
if (!this.actor.mapped)
|
||||
return;
|
||||
let text = this.text.text;
|
||||
let stack = Main._getAndClearErrorStack();
|
||||
for (let i = 0; i < stack.length; i++) {
|
||||
let logItem = stack[i];
|
||||
text += logItem.category + " t=" + this._formatTime(new Date(logItem.timestamp)) + " " + logItem.message + "\n";
|
||||
}
|
||||
this.text.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
function Extensions() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
Extensions.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ vertical: true,
|
||||
name: 'lookingGlassExtensions' });
|
||||
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
|
||||
text: _("No extensions installed") });
|
||||
this._extensionsList = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'lg-extensions-list' });
|
||||
this.actor.add(this._extensionsList);
|
||||
this._loadExtensionList();
|
||||
},
|
||||
|
||||
_loadExtensionList: function() {
|
||||
let extensions = ExtensionSystem.extensionMeta;
|
||||
let totalExtensions = 0;
|
||||
for (let uuid in extensions) {
|
||||
let extensionDisplay = this._createExtensionDisplay(extensions[uuid]);
|
||||
this._extensionsList.add(extensionDisplay);
|
||||
totalExtensions++;
|
||||
}
|
||||
if (totalExtensions == 0) {
|
||||
this._extensionsList.add(this._noExtensions);
|
||||
}
|
||||
},
|
||||
|
||||
_onViewSource: function (actor) {
|
||||
let meta = actor._extensionMeta;
|
||||
let file = Gio.file_new_for_path(meta.path);
|
||||
let uri = file.get_uri();
|
||||
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
|
||||
Main.lookingGlass.close();
|
||||
},
|
||||
|
||||
_onWebPage: function (actor) {
|
||||
let meta = actor._extensionMeta;
|
||||
Gio.app_info_launch_default_for_uri(meta.url, global.create_app_launch_context());
|
||||
Main.lookingGlass.close();
|
||||
},
|
||||
|
||||
_stateToString: function(extensionState) {
|
||||
switch (extensionState) {
|
||||
case ExtensionSystem.ExtensionState.ENABLED:
|
||||
return _("Enabled");
|
||||
case ExtensionSystem.ExtensionState.DISABLED:
|
||||
return _("Disabled");
|
||||
case ExtensionSystem.ExtensionState.ERROR:
|
||||
return _("Error");
|
||||
case ExtensionSystem.ExtensionState.OUT_OF_DATE:
|
||||
return _("Out of date");
|
||||
}
|
||||
return "Unknown"; // Not translated, shouldn't appear
|
||||
},
|
||||
|
||||
_createExtensionDisplay: function(meta) {
|
||||
let box = new St.BoxLayout({ style_class: 'lg-extension', vertical: true });
|
||||
let name = new St.Label({ style_class: 'lg-extension-name',
|
||||
text: meta.name });
|
||||
box.add(name, { expand: true });
|
||||
let description = new St.Label({ style_class: 'lg-extension-description',
|
||||
text: meta.description });
|
||||
box.add(description, { expand: true });
|
||||
|
||||
let metaBox = new St.BoxLayout();
|
||||
box.add(metaBox);
|
||||
let stateString = this._stateToString(meta.state);
|
||||
let state = new St.Label({ style_class: 'lg-extension-state',
|
||||
text: this._stateToString(meta.state) });
|
||||
|
||||
let actionsContainer = new St.Bin({ x_align: St.Align.END });
|
||||
metaBox.add(actionsContainer);
|
||||
let actionsBox = new St.BoxLayout({ style_class: 'lg-extension-actions' });
|
||||
actionsContainer.set_child(actionsBox);
|
||||
|
||||
let viewsource = new Link.Link({ label: _("View Source") });
|
||||
viewsource.actor._extensionMeta = meta;
|
||||
viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource));
|
||||
actionsBox.add(viewsource.actor);
|
||||
|
||||
if (meta.url) {
|
||||
let webpage = new Link.Link({ label: _("Web Page") });
|
||||
webpage.actor._extensionMeta = meta;
|
||||
webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
|
||||
actionsBox.add(webpage.actor);
|
||||
}
|
||||
|
||||
return box;
|
||||
}
|
||||
};
|
||||
|
||||
function LookingGlass() {
|
||||
this._init();
|
||||
}
|
||||
@ -406,6 +535,12 @@ LookingGlass.prototype = {
|
||||
notebook.selectIndex(0);
|
||||
}));
|
||||
|
||||
this._errorLog = new ErrorLog();
|
||||
notebook.appendPage('Errors', this._errorLog.actor);
|
||||
|
||||
this._extensions = new Extensions();
|
||||
notebook.appendPage('Extensions', this._extensions.actor);
|
||||
|
||||
this._entry.clutter_text.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
|
||||
@ -421,10 +556,7 @@ LookingGlass.prototype = {
|
||||
}));
|
||||
this._entry.clutter_text.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Escape) {
|
||||
this.close();
|
||||
return true;
|
||||
} else if (symbol == Clutter.Up) {
|
||||
if (symbol == Clutter.Up) {
|
||||
if (this._historyNavIndex >= this._history.length - 1)
|
||||
return true;
|
||||
this._historyNavIndex++;
|
||||
@ -569,6 +701,16 @@ LookingGlass.prototype = {
|
||||
this._resizeTo(actor);
|
||||
},
|
||||
|
||||
// Handle key events which are relevant for all tabs of the LookingGlass
|
||||
_globalKeyPressEvent : function(actor, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.Escape) {
|
||||
this.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
open : function() {
|
||||
if (this._open)
|
||||
return;
|
||||
@ -576,6 +718,9 @@ LookingGlass.prototype = {
|
||||
if (!Main.pushModal(this.actor))
|
||||
return;
|
||||
|
||||
this._keyPressEventId = global.stage.connect('key-press-event',
|
||||
Lang.bind(this, this._globalKeyPressEvent));
|
||||
|
||||
this.actor.show();
|
||||
this.actor.lower(Main.chrome.actor);
|
||||
this._open = true;
|
||||
@ -594,6 +739,9 @@ LookingGlass.prototype = {
|
||||
if (!this._open)
|
||||
return;
|
||||
|
||||
if (this._keyPressEventId)
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
|
||||
this._historyNavIndex = -1;
|
||||
this._open = false;
|
||||
Tweener.removeTweens(this.actor);
|
||||
|
219
js/ui/main.js
219
js/ui/main.js
@ -14,6 +14,7 @@ const St = imports.gi.St;
|
||||
|
||||
const Chrome = imports.ui.chrome;
|
||||
const Environment = imports.ui.environment;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Messaging = imports.ui.messaging;
|
||||
const Overview = imports.ui.overview;
|
||||
@ -45,6 +46,8 @@ let recorder = null;
|
||||
let shellDBusService = null;
|
||||
let modalCount = 0;
|
||||
let modalActorFocusStack = [];
|
||||
let _errorLogStack = [];
|
||||
let _startDate;
|
||||
|
||||
function start() {
|
||||
// Add a binding for "global" in the global JS namespace; (gjs
|
||||
@ -52,6 +55,12 @@ function start() {
|
||||
// called "window".)
|
||||
window.global = Shell.Global.get();
|
||||
|
||||
// Now monkey patch utility functions into the global proxy;
|
||||
// This is easier and faster than indirecting down into global
|
||||
// if we want to call back up into JS.
|
||||
global.logError = _logError;
|
||||
global.log = _logDebug;
|
||||
|
||||
Gio.DesktopAppInfo.set_desktop_env("GNOME");
|
||||
|
||||
global.grab_dbus_service();
|
||||
@ -116,6 +125,8 @@ function start() {
|
||||
notificationPopup = new MessageTray.Notification();
|
||||
messageTray = new MessageTray.MessageTray();
|
||||
|
||||
_startDate = new Date();
|
||||
|
||||
global.screen.connect('toggle-recording', function() {
|
||||
if (recorder == null) {
|
||||
recorder = new Shell.Recorder({ stage: global.stage });
|
||||
@ -130,6 +141,9 @@ function start() {
|
||||
|
||||
_relayout();
|
||||
|
||||
ExtensionSystem.init();
|
||||
ExtensionSystem.loadExtensions();
|
||||
|
||||
panel.startupAnimation();
|
||||
|
||||
let display = global.screen.get_display();
|
||||
@ -138,9 +152,52 @@ function start() {
|
||||
|
||||
global.stage.connect('captured-event', _globalKeyPressHandler);
|
||||
|
||||
_log('info', 'loaded at ' + _startDate);
|
||||
|
||||
Mainloop.idle_add(_removeUnusedWorkspaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* _log:
|
||||
* @category: string message type ('info', 'error')
|
||||
* @msg: A message string
|
||||
* ...: Any further arguments are converted into JSON notation,
|
||||
* and appended to the log message, separated by spaces.
|
||||
*
|
||||
* Log a message into the LookingGlass error
|
||||
* stream. This is primarily intended for use by the
|
||||
* extension system as well as debugging.
|
||||
*/
|
||||
function _log(category, msg) {
|
||||
let text = msg;
|
||||
if (arguments.length > 2) {
|
||||
text += ': ';
|
||||
for (let i = 2; i < arguments.length; i++) {
|
||||
text += JSON.stringify(arguments[i]);
|
||||
if (i < arguments.length - 1)
|
||||
text += " ";
|
||||
}
|
||||
}
|
||||
_errorLogStack.push({timestamp: new Date().getTime(),
|
||||
category: category,
|
||||
message: text });
|
||||
}
|
||||
|
||||
function _logError(msg) {
|
||||
return _log('error', msg);
|
||||
}
|
||||
|
||||
function _logDebug(msg) {
|
||||
return _log('debug', msg);
|
||||
}
|
||||
|
||||
// Used by the error display in lookingGlass.js
|
||||
function _getAndClearErrorStack() {
|
||||
let errors = _errorLogStack;
|
||||
_errorLogStack = [];
|
||||
return errors;
|
||||
}
|
||||
|
||||
function _relayout() {
|
||||
let primary = global.get_primary_monitor();
|
||||
panel.actor.set_position(primary.x, primary.y);
|
||||
@ -251,7 +308,7 @@ function _findModal(actor) {
|
||||
*/
|
||||
function pushModal(actor) {
|
||||
if (modalCount == 0) {
|
||||
if (!global.begin_modal(currentTime())) {
|
||||
if (!global.begin_modal(global.get_current_time())) {
|
||||
log("pushModal: invocation of begin_modal failed");
|
||||
return false;
|
||||
}
|
||||
@ -304,7 +361,7 @@ function popModal(actor) {
|
||||
if (modalCount > 0)
|
||||
return;
|
||||
|
||||
global.end_modal(currentTime());
|
||||
global.end_modal(global.get_current_time());
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
}
|
||||
|
||||
@ -323,45 +380,6 @@ function getRunDialog() {
|
||||
return runDialog;
|
||||
}
|
||||
|
||||
function createAppLaunchContext() {
|
||||
let context = new Gdk.AppLaunchContext();
|
||||
context.set_timestamp(currentTime());
|
||||
|
||||
// Make sure that the app is opened on the current workspace even if
|
||||
// the user switches before it starts
|
||||
context.set_desktop(global.screen.get_active_workspace_index());
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* currentTime:
|
||||
*
|
||||
* Gets the current X server time from the current Clutter, Gdk, or X
|
||||
* event. If called from outside an event handler, this may return
|
||||
* %Clutter.CURRENT_TIME (aka 0), or it may return a slightly
|
||||
* out-of-date timestamp.
|
||||
*/
|
||||
function currentTime() {
|
||||
// meta_display_get_current_time() will return the correct time
|
||||
// when handling an X or Gdk event, but will return CurrentTime
|
||||
// from some Clutter event callbacks.
|
||||
//
|
||||
// clutter_get_current_event_time() will return the correct time
|
||||
// from a Clutter event callback, but may return an out-of-date
|
||||
// timestamp if called at other times.
|
||||
//
|
||||
// So we try meta_display_get_current_time() first, since we
|
||||
// can recognize a "wrong" answer from that, and then fall back
|
||||
// to clutter_get_current_event_time().
|
||||
|
||||
let time = global.screen.get_display().get_current_time();
|
||||
if (time != Clutter.CURRENT_TIME)
|
||||
return time;
|
||||
|
||||
return Clutter.get_current_event_time();
|
||||
}
|
||||
|
||||
/**
|
||||
* activateWindow:
|
||||
* @window: the Meta.Window to activate
|
||||
@ -374,7 +392,7 @@ function activateWindow(window, time) {
|
||||
let windowWorkspaceNum = window.get_workspace().index();
|
||||
|
||||
if (!time)
|
||||
time = currentTime();
|
||||
time = global.get_current_time();
|
||||
|
||||
if (windowWorkspaceNum != activeWorkspaceNum) {
|
||||
let workspace = global.screen.get_workspace_by_index(windowWorkspaceNum);
|
||||
@ -383,3 +401,120 @@ function activateWindow(window, time) {
|
||||
window.activate(time);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - replace this timeout with some system to guess when the user might
|
||||
// be e.g. just reading the screen and not likely to interact.
|
||||
const DEFERRED_TIMEOUT_SECONDS = 20;
|
||||
var _deferredWorkData = {};
|
||||
// Work scheduled for some point in the future
|
||||
var _deferredWorkQueue = [];
|
||||
// Work we need to process before the next redraw
|
||||
var _beforeRedrawQueue = [];
|
||||
// Counter to assign work ids
|
||||
var _deferredWorkSequence = 0;
|
||||
var _deferredTimeoutId = 0;
|
||||
|
||||
function _runDeferredWork(workId) {
|
||||
if (!_deferredWorkData[workId])
|
||||
return;
|
||||
let index = _deferredWorkQueue.indexOf(workId);
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
_deferredWorkQueue.splice(index, 1);
|
||||
_deferredWorkData[workId].callback();
|
||||
if (_deferredWorkQueue.length == 0 && _deferredTimeoutId > 0) {
|
||||
Mainloop.source_remove(_deferredTimeoutId);
|
||||
_deferredTimeoutId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function _runAllDeferredWork() {
|
||||
while (_deferredWorkQueue.length > 0)
|
||||
_runDeferredWork(_deferredWorkQueue[0]);
|
||||
}
|
||||
|
||||
function _runBeforeRedrawQueue() {
|
||||
for (let i = 0; i < _beforeRedrawQueue.length; i++) {
|
||||
let workId = _beforeRedrawQueue[i];
|
||||
_runDeferredWork(workId);
|
||||
}
|
||||
_beforeRedrawQueue = [];
|
||||
}
|
||||
|
||||
function _queueBeforeRedraw(workId) {
|
||||
_beforeRedrawQueue.push(workId);
|
||||
if (_beforeRedrawQueue.length == 1) {
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, function () {
|
||||
_runBeforeRedrawQueue();
|
||||
return false;
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initializeDeferredWork:
|
||||
* @actor: A #ClutterActor
|
||||
* @callback: Function to invoke to perform work
|
||||
*
|
||||
* This function sets up a callback to be invoked when either the
|
||||
* given actor is mapped, or after some period of time when the machine
|
||||
* is idle. This is useful if your actor isn't always visible on the
|
||||
* screen (for example, all actors in the overview), and you don't want
|
||||
* to consume resources updating if the actor isn't actually going to be
|
||||
* displaying to the user.
|
||||
*
|
||||
* Note that queueDeferredWork is called by default immediately on
|
||||
* initialization as well, under the assumption that new actors
|
||||
* will need it.
|
||||
*
|
||||
* Returns: A string work identifer
|
||||
*/
|
||||
function initializeDeferredWork(actor, callback, props) {
|
||||
// Turn into a string so we can use as an object property
|
||||
let workId = "" + (++_deferredWorkSequence);
|
||||
_deferredWorkData[workId] = { 'actor': actor,
|
||||
'callback': callback };
|
||||
actor.connect('notify::mapped', function () {
|
||||
if (!(actor.mapped && _deferredWorkQueue.indexOf(workId) >= 0))
|
||||
return;
|
||||
_queueBeforeRedraw(workId);
|
||||
});
|
||||
actor.connect('destroy', function() {
|
||||
let index = _deferredWorkQueue.indexOf(workId);
|
||||
if (index >= 0)
|
||||
_deferredWorkQueue.splice(index, 1);
|
||||
delete _deferredWorkData[workId];
|
||||
});
|
||||
queueDeferredWork(workId);
|
||||
return workId;
|
||||
}
|
||||
|
||||
/**
|
||||
* queueDeferredWork:
|
||||
* @workId: work identifier
|
||||
*
|
||||
* Ensure that the work identified by @workId will be
|
||||
* run on map or timeout. You should call this function
|
||||
* for example when data being displayed by the actor has
|
||||
* changed.
|
||||
*/
|
||||
function queueDeferredWork(workId) {
|
||||
let data = _deferredWorkData[workId];
|
||||
if (!data) {
|
||||
global.logError("invalid work id ", workId);
|
||||
return;
|
||||
}
|
||||
if (_deferredWorkQueue.indexOf(workId) < 0)
|
||||
_deferredWorkQueue.push(workId);
|
||||
if (data.actor.mapped) {
|
||||
_queueBeforeRedraw(workId);
|
||||
return;
|
||||
} else if (_deferredTimeoutId == 0) {
|
||||
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
|
||||
_runAllDeferredWork();
|
||||
_deferredTimeoutId = 0;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ const Lang = imports.lang;
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const DocDisplay = imports.ui.docDisplay;
|
||||
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;
|
||||
@ -184,6 +183,7 @@ Overview.prototype = {
|
||||
this._dash.actor.set_size(displayGridColumnWidth, contentHeight);
|
||||
this._dash.searchArea.height = this._workspacesY - contentY;
|
||||
this._dash.sectionArea.height = this._workspacesHeight;
|
||||
this._dash.searchResults.actor.height = this._workspacesHeight;
|
||||
|
||||
// place the 'Add Workspace' button in the bottom row of the grid
|
||||
addRemoveButtonSize = Math.floor(displayGridRowHeight * 3/5);
|
||||
@ -248,7 +248,7 @@ Overview.prototype = {
|
||||
// This 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.BaseWellItem) {
|
||||
|| source instanceof AppDisplay.AppIcon) {
|
||||
if (this._activeDisplayPane != null)
|
||||
this._activeDisplayPane.close();
|
||||
return true;
|
||||
@ -457,7 +457,7 @@ Overview.prototype = {
|
||||
},
|
||||
|
||||
_addNewWorkspace: function() {
|
||||
global.screen.append_new_workspace(false, Main.currentTime());
|
||||
global.screen.append_new_workspace(false, global.get_current_time());
|
||||
},
|
||||
|
||||
_acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) {
|
||||
|
@ -15,7 +15,7 @@ const _ = Gettext.gettext;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const NAUTILUS_PREFS_DIR = '/apps/nautilus/preferences';
|
||||
const DESKTOP_IS_HOME_KEY = NAUTILUS_PREFS_DIR + '/desktop_is_home_dir';
|
||||
@ -30,19 +30,78 @@ const PLACES_ICON_SIZE = 16;
|
||||
* @iconFactory: A JavaScript callback which will create an icon texture given a size parameter
|
||||
* @launch: A JavaScript callback to launch the entry
|
||||
*/
|
||||
function PlaceInfo(name, iconFactory, launch) {
|
||||
this._init(name, iconFactory, launch);
|
||||
function PlaceInfo(id, name, iconFactory, launch) {
|
||||
this._init(id, name, iconFactory, launch);
|
||||
}
|
||||
|
||||
PlaceInfo.prototype = {
|
||||
_init: function(name, iconFactory, launch) {
|
||||
_init: function(id, name, iconFactory, launch) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this._lowerName = name.toLowerCase();
|
||||
this.iconFactory = iconFactory;
|
||||
this.launch = launch;
|
||||
this.id = null;
|
||||
},
|
||||
|
||||
matchTerms: function(terms) {
|
||||
let mtype = Search.MatchType.NONE;
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
let term = terms[i];
|
||||
let idx = this._lowerName.indexOf(term);
|
||||
if (idx == 0)
|
||||
return Search.MatchType.PREFIX;
|
||||
else if (idx > 0)
|
||||
mtype = Search.MatchType.SUBSTRING;
|
||||
}
|
||||
return mtype;
|
||||
},
|
||||
|
||||
isRemovable: function() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function PlaceDeviceInfo(mount) {
|
||||
this._init(mount);
|
||||
}
|
||||
|
||||
PlaceDeviceInfo.prototype = {
|
||||
__proto__: PlaceInfo.prototype,
|
||||
|
||||
_init: function(mount) {
|
||||
this._mount = mount;
|
||||
this.name = mount.get_name();
|
||||
this._lowerName = this.name.toLowerCase();
|
||||
this.id = "mount:" + mount.get_root().get_uri();
|
||||
},
|
||||
|
||||
iconFactory: function(size) {
|
||||
let icon = this._mount.get_icon();
|
||||
return Shell.TextureCache.get_default().load_gicon(icon, size);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
|
||||
global.create_app_launch_context());
|
||||
},
|
||||
|
||||
isRemovable: function() {
|
||||
return this._mount.can_unmount();
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
if (!this.isRemovable())
|
||||
return;
|
||||
|
||||
this._mount.unmount(0, null, Lang.bind(this, this._removeFinish), null);
|
||||
},
|
||||
|
||||
_removeFinish: function(o, res, data) {
|
||||
this._mount.unmount_finish(res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function PlacesManager() {
|
||||
this._init();
|
||||
}
|
||||
@ -52,6 +111,7 @@ PlacesManager.prototype = {
|
||||
let gconf = Shell.GConf.get_default();
|
||||
gconf.watch_directory(NAUTILUS_PREFS_DIR);
|
||||
|
||||
this._defaultPlaces = [];
|
||||
this._mounts = [];
|
||||
this._bookmarks = [];
|
||||
this._isDesktopHome = false;
|
||||
@ -60,12 +120,12 @@ PlacesManager.prototype = {
|
||||
let homeUri = homeFile.get_uri();
|
||||
let homeLabel = Shell.util_get_label_for_uri (homeUri);
|
||||
let homeIcon = Shell.util_get_icon_for_uri (homeUri);
|
||||
this._home = new PlaceInfo(homeLabel,
|
||||
this._home = new PlaceInfo('special:home', homeLabel,
|
||||
function(size) {
|
||||
return Shell.TextureCache.get_default().load_gicon(homeIcon, size);
|
||||
},
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(homeUri, Main.createAppLaunchContext());
|
||||
Gio.app_info_launch_default_for_uri(homeUri, global.create_app_launch_context());
|
||||
});
|
||||
|
||||
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
||||
@ -73,15 +133,15 @@ PlacesManager.prototype = {
|
||||
let desktopUri = desktopFile.get_uri();
|
||||
let desktopLabel = Shell.util_get_label_for_uri (desktopUri);
|
||||
let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
|
||||
this._desktopMenu = new PlaceInfo(desktopLabel,
|
||||
this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel,
|
||||
function(size) {
|
||||
return Shell.TextureCache.get_default().load_gicon(desktopIcon, size);
|
||||
},
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(desktopUri, Main.createAppLaunchContext());
|
||||
Gio.app_info_launch_default_for_uri(desktopUri, global.create_app_launch_context());
|
||||
});
|
||||
|
||||
this._connect = new PlaceInfo(_("Connect to..."),
|
||||
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
|
||||
function (size) {
|
||||
return Shell.TextureCache.get_default().load_icon_name("applications-internet", size);
|
||||
},
|
||||
@ -101,7 +161,7 @@ PlacesManager.prototype = {
|
||||
}
|
||||
|
||||
if (networkApp != null) {
|
||||
this._network = new PlaceInfo(networkApp.get_name(),
|
||||
this._network = new PlaceInfo('special:network', networkApp.get_name(),
|
||||
function(size) {
|
||||
return networkApp.create_icon_texture(size);
|
||||
},
|
||||
@ -110,6 +170,16 @@ PlacesManager.prototype = {
|
||||
});
|
||||
}
|
||||
|
||||
this._defaultPlaces.push(this._home);
|
||||
|
||||
if (!this._isDesktopHome)
|
||||
this._defaultPlaces.push(this._desktopMenu);
|
||||
|
||||
if (this._network)
|
||||
this._defaultPlaces.push(this._network);
|
||||
|
||||
this._defaultPlaces.push(this._connect);
|
||||
|
||||
/*
|
||||
* Show devices, code more or less ported from nautilus-places-sidebar.c
|
||||
*/
|
||||
@ -238,12 +308,12 @@ PlacesManager.prototype = {
|
||||
continue;
|
||||
let icon = Shell.util_get_icon_for_uri(bookmark);
|
||||
|
||||
let item = new PlaceInfo(label,
|
||||
let item = new PlaceInfo('bookmark:' + bookmark, label,
|
||||
function(size) {
|
||||
return Shell.TextureCache.get_default().load_gicon(icon, size);
|
||||
},
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(bookmark, Main.createAppLaunchContext());
|
||||
Gio.app_info_launch_default_for_uri(bookmark, global.create_app_launch_context());
|
||||
});
|
||||
this._bookmarks.push(item);
|
||||
}
|
||||
@ -263,17 +333,7 @@ PlacesManager.prototype = {
|
||||
},
|
||||
|
||||
_addMount: function(mount) {
|
||||
let mountLabel = mount.get_name();
|
||||
let mountIcon = mount.get_icon();
|
||||
let root = mount.get_root();
|
||||
let mountUri = root.get_uri();
|
||||
let devItem = new PlaceInfo(mountLabel,
|
||||
function(size) {
|
||||
return Shell.TextureCache.get_default().load_gicon(mountIcon, size);
|
||||
},
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(mountUri, Main.createAppLaunchContext());
|
||||
});
|
||||
let devItem = new PlaceDeviceInfo(mount);
|
||||
this._mounts.push(devItem);
|
||||
},
|
||||
|
||||
@ -282,16 +342,7 @@ PlacesManager.prototype = {
|
||||
},
|
||||
|
||||
getDefaultPlaces: function () {
|
||||
let places = [this._home];
|
||||
|
||||
if (!this._isDesktopHome)
|
||||
places.push(this._desktopMenu);
|
||||
|
||||
if (this._network)
|
||||
places.push(this._network);
|
||||
|
||||
places.push(this._connect);
|
||||
return places;
|
||||
return this._defaultPlaces;
|
||||
},
|
||||
|
||||
getBookmarks: function () {
|
||||
@ -300,6 +351,28 @@ PlacesManager.prototype = {
|
||||
|
||||
getMounts: function () {
|
||||
return this._mounts;
|
||||
},
|
||||
|
||||
_lookupById: function(sourceArray, id) {
|
||||
for (let i = 0; i < sourceArray.length; i++) {
|
||||
let place = sourceArray[i];
|
||||
if (place.id == id)
|
||||
return place;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
lookupPlaceById: function(id) {
|
||||
let colonIdx = id.indexOf(':');
|
||||
let type = id.substring(0, colonIdx);
|
||||
let sourceArray = null;
|
||||
if (type == 'special')
|
||||
sourceArray = this._defaultPlaces;
|
||||
else if (type == 'mount')
|
||||
sourceArray = this._mounts;
|
||||
else if (type == 'bookmark')
|
||||
sourceArray = this._bookmarks;
|
||||
return this._lookupById(sourceArray, id);
|
||||
}
|
||||
};
|
||||
|
||||
@ -319,23 +392,37 @@ DashPlaceDisplayItem.prototype = {
|
||||
this._info = info;
|
||||
this._icon = info.iconFactory(PLACES_ICON_SIZE);
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
reactive: true,
|
||||
spacing: 4 });
|
||||
this.actor.connect('button-release-event', Lang.bind(this, function (b, e) {
|
||||
this._info.launch();
|
||||
Main.overview.hide();
|
||||
}));
|
||||
let text = new St.Label({ style_class: 'places-item',
|
||||
text: info.name });
|
||||
let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
|
||||
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
||||
let text = new St.Button({ style_class: 'places-item',
|
||||
label: info.name,
|
||||
x_align: St.Align.START });
|
||||
text.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
let iconBox = new St.Bin({ child: this._icon, reactive: true });
|
||||
iconBox.connect('button-release-event',
|
||||
Lang.bind(this, this._onClicked));
|
||||
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(text, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
if (info.isRemovable()) {
|
||||
let removeIcon = Shell.TextureCache.get_default().load_icon_name ('media-eject', PLACES_ICON_SIZE);
|
||||
let removeIconBox = new St.Button({ child: removeIcon,
|
||||
reactive: true });
|
||||
this.actor.append(removeIconBox, Big.BoxPackFlags.NONE);
|
||||
removeIconBox.connect('clicked',
|
||||
Lang.bind(this, function() {
|
||||
this._info.remove();
|
||||
}));
|
||||
}
|
||||
|
||||
this.actor._delegate = this;
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
},
|
||||
|
||||
_onClicked: function(b) {
|
||||
this._info.launch();
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
getDragActorSource: function() {
|
||||
return this._icon;
|
||||
},
|
||||
@ -421,120 +508,67 @@ DashPlaceDisplay.prototype = {
|
||||
|
||||
Signals.addSignalMethods(DashPlaceDisplay.prototype);
|
||||
|
||||
|
||||
function PlaceDisplayItem(placeInfo) {
|
||||
this._init(placeInfo);
|
||||
function PlaceSearchProvider() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
PlaceDisplayItem.prototype = {
|
||||
__proto__: GenericDisplay.GenericDisplayItem.prototype,
|
||||
PlaceSearchProvider.prototype = {
|
||||
__proto__: Search.SearchProvider.prototype,
|
||||
|
||||
_init : function(placeInfo) {
|
||||
GenericDisplay.GenericDisplayItem.prototype._init.call(this);
|
||||
this._info = placeInfo;
|
||||
|
||||
this._setItemInfo(placeInfo.name, '');
|
||||
_init: function() {
|
||||
Search.SearchProvider.prototype._init.call(this, _("PLACES"));
|
||||
},
|
||||
|
||||
//// Public method overrides ////
|
||||
|
||||
// Opens an application represented by this display item.
|
||||
launch : function() {
|
||||
this._info.launch();
|
||||
getResultMeta: function(resultId) {
|
||||
let placeInfo = Main.placesManager.lookupPlaceById(resultId);
|
||||
if (!placeInfo)
|
||||
return null;
|
||||
return { 'id': resultId,
|
||||
'name': placeInfo.name,
|
||||
'icon': placeInfo.iconFactory(Search.RESULT_ICON_SIZE) };
|
||||
},
|
||||
|
||||
shellWorkspaceLaunch: function() {
|
||||
this._info.launch();
|
||||
activateResult: function(id) {
|
||||
let placeInfo = Main.placesManager.lookupPlaceById(id);
|
||||
placeInfo.launch();
|
||||
},
|
||||
|
||||
//// Protected method overrides ////
|
||||
|
||||
// Returns an icon for the item.
|
||||
_createIcon: function() {
|
||||
return this._info.iconFactory(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
|
||||
_compareResultMeta: function (idA, idB) {
|
||||
let infoA = Main.placesManager.lookupPlaceById(idA);
|
||||
let infoB = Main.placesManager.lookupPlaceById(idB);
|
||||
return infoA.name.localeCompare(infoB.name);
|
||||
},
|
||||
|
||||
// Returns a preview icon for the item.
|
||||
_createPreviewIcon: function() {
|
||||
return this._info.iconFactory(GenericDisplay.PREVIEW_ICON_SIZE);
|
||||
_searchPlaces: function(places, terms) {
|
||||
let multipleResults = [];
|
||||
let prefixResults = [];
|
||||
let substringResults = [];
|
||||
|
||||
terms = terms.map(String.toLowerCase);
|
||||
|
||||
for (let i = 0; i < places.length; i++) {
|
||||
let place = places[i];
|
||||
let mtype = place.matchTerms(terms);
|
||||
if (mtype == Search.MatchType.MULTIPLE)
|
||||
multipleResults.push(place.id);
|
||||
else if (mtype == Search.MatchType.PREFIX)
|
||||
prefixResults.push(place.id);
|
||||
else if (mtype == Search.MatchType.SUBSTRING)
|
||||
substringResults.push(place.id);
|
||||
}
|
||||
multipleResults.sort(this._compareResultMeta);
|
||||
prefixResults.sort(this._compareResultMeta);
|
||||
substringResults.sort(this._compareResultMeta);
|
||||
return multipleResults.concat(prefixResults.concat(substringResults));
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
let places = Main.placesManager.getAllPlaces();
|
||||
return this._searchPlaces(places, terms);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
let places = previousResults.map(function (id) { return Main.placesManager.lookupPlaceById(id); });
|
||||
return this._searchPlaces(places, terms);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function PlaceDisplay(flags) {
|
||||
this._init(flags);
|
||||
}
|
||||
|
||||
PlaceDisplay.prototype = {
|
||||
__proto__: GenericDisplay.GenericDisplay.prototype,
|
||||
|
||||
_init: function(flags) {
|
||||
GenericDisplay.GenericDisplay.prototype._init.call(this, flags);
|
||||
this._stale = true;
|
||||
Main.placesManager.connect('places-updated', Lang.bind(this, function (e) {
|
||||
this._stale = true;
|
||||
}));
|
||||
},
|
||||
|
||||
//// Protected method overrides ////
|
||||
_refreshCache: function () {
|
||||
if (!this._stale)
|
||||
return true;
|
||||
this._allItems = {};
|
||||
let array = Main.placesManager.getAllPlaces();
|
||||
for (let i = 0; i < array.length; i ++) {
|
||||
// We are using an array id as placeInfo id because placeInfo doesn't have any
|
||||
// other information piece that can be used as a unique id. There are different
|
||||
// types of placeInfo, such as devices and directories that would result in differently
|
||||
// structured ids. Also the home directory can show up in both the default places and in
|
||||
// bookmarks which means its URI can't be used as a unique id. (This does mean it can
|
||||
// appear twice in search results, though that doesn't happen at the moment because we
|
||||
// name it "Home Folder" in default places and it's named with the user's system name
|
||||
// if it appears as a bookmark.)
|
||||
let placeInfo = array[i];
|
||||
placeInfo.id = i;
|
||||
this._allItems[i] = placeInfo;
|
||||
}
|
||||
this._stale = false;
|
||||
return false;
|
||||
},
|
||||
|
||||
// Sets the list of the displayed items.
|
||||
_setDefaultList: function() {
|
||||
this._matchedItems = {};
|
||||
this._matchedItemKeys = [];
|
||||
for (id in this._allItems) {
|
||||
this._matchedItems[id] = 1;
|
||||
this._matchedItemKeys.push(id);
|
||||
}
|
||||
this._matchedItemKeys.sort(Lang.bind(this, this._compareItems));
|
||||
},
|
||||
|
||||
// Checks if the item info can be a match for the search string by checking
|
||||
// the name of the place. Item info is expected to be PlaceInfo.
|
||||
// Returns a boolean flag indicating if itemInfo is a match.
|
||||
_isInfoMatching: function(itemInfo, search) {
|
||||
if (search == null || search == '')
|
||||
return true;
|
||||
|
||||
let name = itemInfo.name.toLowerCase();
|
||||
if (name.indexOf(search) >= 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
// Compares items associated with the item ids based on the alphabetical order
|
||||
// of the item names.
|
||||
// Returns an integer value indicating the result of the comparison.
|
||||
_compareItems: function(itemIdA, itemIdB) {
|
||||
let placeA = this._allItems[itemIdA];
|
||||
let placeB = this._allItems[itemIdB];
|
||||
return placeA.name.localeCompare(placeB.name);
|
||||
},
|
||||
|
||||
// Creates a PlaceDisplayItem based on itemInfo, which is expected to be a PlaceInfo object.
|
||||
_createDisplayItem: function(itemInfo) {
|
||||
return new PlaceDisplayItem(itemInfo);
|
||||
}
|
||||
};
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
const Big = imports.gi.Big;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
@ -24,6 +25,144 @@ const DIALOG_WIDTH = 320;
|
||||
const DIALOG_PADDING = 6;
|
||||
const ICON_SIZE = 24;
|
||||
const ICON_BOX_SIZE = 36;
|
||||
const MAX_FILE_DELETED_BEFORE_INVALID = 10;
|
||||
|
||||
function CommandCompleter() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
CommandCompleter.prototype = {
|
||||
_init : function() {
|
||||
this._changedCount = 0;
|
||||
this._paths = GLib.getenv('PATH').split(':');
|
||||
this._valid = false;
|
||||
this._updateInProgress = false;
|
||||
this._childs = new Array(this._paths.length);
|
||||
this._monitors = new Array(this._paths.length);
|
||||
for (let i = 0; i < this._paths.length; i++) {
|
||||
this._childs[i] = [];
|
||||
let file = Gio.file_new_for_path(this._paths[i]);
|
||||
let info = file.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, null);
|
||||
|
||||
if (info.get_attribute_uint32(Gio.FILE_ATTRIBUTE_STANDARD_TYPE) != Gio.FileType.DIRECTORY)
|
||||
continue;
|
||||
|
||||
this._paths[i] = file.get_path();
|
||||
this._monitors[i] = file.monitor_directory(Gio.FileMonitorFlags.NONE, null);
|
||||
if (this._monitors[i] != null) {
|
||||
this._monitors[i].connect("changed", Lang.bind(this, this._onChanged));
|
||||
}
|
||||
}
|
||||
this._update(0);
|
||||
},
|
||||
|
||||
_onGetEnumerateComplete : function(obj, res) {
|
||||
this._enumerator = obj.enumerate_children_finish(res);
|
||||
this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete), null);
|
||||
},
|
||||
|
||||
_onNextFileComplete : function(obj, res) {
|
||||
let files = obj.next_files_finish(res);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
this._childs[this._i].push(files[i].get_name());
|
||||
}
|
||||
if (files.length) {
|
||||
this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete), null);
|
||||
} else {
|
||||
this._enumerator.close(null);
|
||||
this._enumerator = null;
|
||||
this._update(this._i + 1);
|
||||
}
|
||||
},
|
||||
|
||||
update : function() {
|
||||
if (this._valid)
|
||||
return;
|
||||
this._update(0);
|
||||
},
|
||||
|
||||
_update : function(i) {
|
||||
if (i == 0 && this._updateInProgress)
|
||||
return;
|
||||
this._updateInProgress = true;
|
||||
this._changedCount = 0;
|
||||
this._i = i;
|
||||
if (i >= this._paths.length) {
|
||||
this._valid = true;
|
||||
this._updateInProgress = false;
|
||||
return;
|
||||
}
|
||||
let file = Gio.file_new_for_path(this._paths[i]);
|
||||
this._childs[this._i] = [];
|
||||
file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onGetEnumerateComplete), null);
|
||||
},
|
||||
|
||||
_onChanged : function(m, f, of, type) {
|
||||
if (!this._valid)
|
||||
return;
|
||||
let path = f.get_parent().get_path();
|
||||
let k = undefined;
|
||||
for (let i = 0; i < this._paths.length; i++) {
|
||||
if (this._paths[i] == path)
|
||||
k = i;
|
||||
}
|
||||
if (k === undefined) {
|
||||
return;
|
||||
}
|
||||
if (type == Gio.FileMonitorEvent.CREATED) {
|
||||
this._childs[k].push(f.get_basename());
|
||||
}
|
||||
if (type == Gio.FileMonitorEvent.DELETED) {
|
||||
this._changedCount++;
|
||||
if (this._changedCount > MAX_FILE_DELETED_BEFORE_INVALID) {
|
||||
this._valid = false;
|
||||
}
|
||||
let name = f.get_basename();
|
||||
this._childs[k] = this._childs[k].filter(function(e) {
|
||||
return e != name;
|
||||
});
|
||||
}
|
||||
if (type == Gio.FileMonitorEvent.UNMOUNTED) {
|
||||
this._childs[k] = [];
|
||||
}
|
||||
},
|
||||
|
||||
getCompletion: function(text) {
|
||||
let common = "";
|
||||
let notInit = true;
|
||||
if (!this._valid) {
|
||||
this._update(0);
|
||||
return common;
|
||||
}
|
||||
function _getCommon(s1, s2) {
|
||||
let k = 0;
|
||||
for (; k < s1.length && k < s2.length; k++) {
|
||||
if (s1[k] != s2[k])
|
||||
break;
|
||||
}
|
||||
if (k == 0)
|
||||
return "";
|
||||
return s1.substr(0, k);
|
||||
}
|
||||
function _hasPrefix(s1, prefix) {
|
||||
return s1.indexOf(prefix) == 0;
|
||||
}
|
||||
for (let i = 0; i < this._childs.length; i++) {
|
||||
for (let k = 0; k < this._childs[i].length; k++) {
|
||||
if (!_hasPrefix(this._childs[i][k], text))
|
||||
continue;
|
||||
if (notInit) {
|
||||
common = this._childs[i][k];
|
||||
notInit = false;
|
||||
}
|
||||
common = _getCommon(common, this._childs[i][k]);
|
||||
}
|
||||
}
|
||||
if (common.length)
|
||||
return common.substr(text.length);
|
||||
return common;
|
||||
}
|
||||
};
|
||||
|
||||
function RunDialog() {
|
||||
this._init();
|
||||
@ -137,16 +276,55 @@ RunDialog.prototype = {
|
||||
this.close();
|
||||
}));
|
||||
|
||||
this._pathCompleter = new Gio.FilenameCompleter();
|
||||
this._commandCompleter = new CommandCompleter();
|
||||
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
|
||||
this._entry.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Escape) {
|
||||
this.close();
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.slash) {
|
||||
// Need preload data before get completion. GFilenameCompleter load content of parent directory.
|
||||
// Parent directory for /usr/include/ is /usr/. So need to add fake name('a').
|
||||
let text = o.get_text().concat('/a');
|
||||
let prefix;
|
||||
if (text.lastIndexOf(' ') == -1)
|
||||
prefix = text;
|
||||
else
|
||||
prefix = text.substr(text.lastIndexOf(' ') + 1);
|
||||
this._getCompletion(prefix);
|
||||
return false;
|
||||
}
|
||||
if (symbol == Clutter.Tab) {
|
||||
let text = o.get_text();
|
||||
let prefix;
|
||||
if (text.lastIndexOf(' ') == -1)
|
||||
prefix = text;
|
||||
else
|
||||
prefix = text.substr(text.lastIndexOf(' ') + 1);
|
||||
let postfix = this._getCompletion(prefix);
|
||||
if (postfix != null && postfix.length > 0) {
|
||||
o.insert_text(postfix, -1);
|
||||
o.set_cursor_position(text.length + postfix.length);
|
||||
if (postfix[postfix.length - 1] == '/')
|
||||
this._getCompletion(text + postfix + 'a');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
},
|
||||
|
||||
_getCompletion : function(text) {
|
||||
if (text.indexOf('/') != -1) {
|
||||
return this._pathCompleter.get_completion_suffix(text);
|
||||
} else {
|
||||
return this._commandCompleter.getCompletion(text);
|
||||
}
|
||||
},
|
||||
|
||||
_run : function(command) {
|
||||
this._commandError = false;
|
||||
let f;
|
||||
|
272
js/ui/search.js
Normal file
272
js/ui/search.js
Normal file
@ -0,0 +1,272 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const RESULT_ICON_SIZE = 24;
|
||||
|
||||
// Not currently referenced by the search API, but
|
||||
// this enumeration can be useful for provider
|
||||
// implementations.
|
||||
const MatchType = {
|
||||
NONE: 0,
|
||||
MULTIPLE: 1,
|
||||
PREFIX: 2,
|
||||
SUBSTRING: 3
|
||||
};
|
||||
|
||||
function SearchResultDisplay(provider) {
|
||||
this._init(provider);
|
||||
}
|
||||
|
||||
SearchResultDisplay.prototype = {
|
||||
_init: function(provider) {
|
||||
this.provider = provider;
|
||||
this.actor = null;
|
||||
this.selectionIndex = -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* renderResults:
|
||||
* @results: List of identifier strings
|
||||
* @terms: List of search term strings
|
||||
*
|
||||
* Display the given search matches which resulted
|
||||
* from the given terms. It's expected that not
|
||||
* all results will fit in the space for the container
|
||||
* actor; in this case, show as many as makes sense
|
||||
* for your result type.
|
||||
*
|
||||
* The terms are useful for search match highlighting.
|
||||
*/
|
||||
renderResults: function(results, terms) {
|
||||
throw new Error("not implemented");
|
||||
},
|
||||
|
||||
/**
|
||||
* clear:
|
||||
* Remove all results from this display and reset the selection index.
|
||||
*/
|
||||
clear: function() {
|
||||
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
|
||||
this.selectionIndex = -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* getSelectionIndex:
|
||||
*
|
||||
* Returns the index of the selected actor, or -1 if none.
|
||||
*/
|
||||
getSelectionIndex: function() {
|
||||
return this.selectionIndex;
|
||||
},
|
||||
|
||||
/**
|
||||
* getVisibleResultCount:
|
||||
*
|
||||
* Returns: The number of actors visible.
|
||||
*/
|
||||
getVisibleResultCount: function() {
|
||||
throw new Error("not implemented");
|
||||
},
|
||||
|
||||
/**
|
||||
* selectIndex:
|
||||
* @index: Integer index
|
||||
*
|
||||
* Move selection to the given index.
|
||||
* Return true if successful, false if no more results
|
||||
* available.
|
||||
*/
|
||||
selectIndex: function() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SearchProvider:
|
||||
*
|
||||
* Subclass this object to add a new result type
|
||||
* to the search system, then call registerProvider()
|
||||
* in SearchSystem with an instance.
|
||||
*/
|
||||
function SearchProvider(title) {
|
||||
this._init(title);
|
||||
}
|
||||
|
||||
SearchProvider.prototype = {
|
||||
_init: function(title) {
|
||||
this.title = title;
|
||||
},
|
||||
|
||||
/**
|
||||
* getInitialResultSet:
|
||||
* @terms: Array of search terms, treated as logical OR
|
||||
*
|
||||
* Called when the user first begins a search (most likely
|
||||
* therefore a single term of length one or two), or when
|
||||
* a new term is added.
|
||||
*
|
||||
* Should return an array of result identifier strings representing
|
||||
* items which match the given search terms. This
|
||||
* is expected to be a substring match on the metadata for a given
|
||||
* item. Ordering of returned results is up to the discretion of the provider,
|
||||
* but you should follow these heruistics:
|
||||
*
|
||||
* * Put items which match multiple search terms before single matches
|
||||
* * Put items which match on a prefix before non-prefix substring matches
|
||||
*
|
||||
* This function should be fast; do not perform unindexed full-text searches
|
||||
* or network queries.
|
||||
*/
|
||||
getInitialResultSet: function(terms) {
|
||||
throw new Error("not implemented");
|
||||
},
|
||||
|
||||
/**
|
||||
* getSubsearchResultSet:
|
||||
* @previousResults: Array of item identifiers
|
||||
* @newTerms: Updated search terms
|
||||
*
|
||||
* Called when a search is performed which is a "subsearch" of
|
||||
* the previous search; i.e. when every search term has exactly
|
||||
* one corresponding term in the previous search which is a prefix
|
||||
* of the new term.
|
||||
*
|
||||
* This allows search providers to only search through the previous
|
||||
* result set, rather than possibly performing a full re-query.
|
||||
*/
|
||||
getSubsearchResultSet: function(previousResults, newTerms) {
|
||||
throw new Error("not implemented");
|
||||
},
|
||||
|
||||
/**
|
||||
* getResultInfo:
|
||||
* @id: Result identifier string
|
||||
*
|
||||
* Return an object with 'id', 'name', (both strings) and 'icon' (Clutter.Texture)
|
||||
* properties which describe the given search result.
|
||||
*/
|
||||
getResultMeta: function(id) {
|
||||
throw new Error("not implemented");
|
||||
},
|
||||
|
||||
/**
|
||||
* createResultContainer:
|
||||
*
|
||||
* Search providers may optionally override this to render their
|
||||
* results in a custom fashion. The default implementation
|
||||
* will create a vertical list.
|
||||
*
|
||||
* Returns: An instance of SearchResultDisplay.
|
||||
*/
|
||||
createResultContainerActor: function() {
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* createResultActor:
|
||||
* @resultMeta: Object with result metadata
|
||||
* @terms: Array of search terms, should be used for highlighting
|
||||
*
|
||||
* Search providers may optionally override this to render a
|
||||
* particular serch result in a custom fashion. The default
|
||||
* implementation will show the icon next to the name.
|
||||
*
|
||||
* The actor should be an instance of St.Widget, with the style class
|
||||
* 'dash-search-result-content'.
|
||||
*/
|
||||
createResultActor: function(resultMeta, terms) {
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* activateResult:
|
||||
* @id: Result identifier string
|
||||
*
|
||||
* Called when the user chooses a given result.
|
||||
*/
|
||||
activateResult: function(id) {
|
||||
throw new Error("not implemented");
|
||||
},
|
||||
|
||||
/**
|
||||
* expandSearch:
|
||||
*
|
||||
* Called when the user clicks on the header for this
|
||||
* search section. Should typically launch an external program
|
||||
* displaying search results for that item type.
|
||||
*/
|
||||
expandSearch: function(terms) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(SearchProvider.prototype);
|
||||
|
||||
function SearchSystem() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
SearchSystem.prototype = {
|
||||
_init: function() {
|
||||
this._providers = [];
|
||||
this.reset();
|
||||
},
|
||||
|
||||
registerProvider: function (provider) {
|
||||
this._providers.push(provider);
|
||||
},
|
||||
|
||||
getProviders: function() {
|
||||
return this._providers;
|
||||
},
|
||||
|
||||
getTerms: function() {
|
||||
return this._previousTerms;
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._previousTerms = [];
|
||||
this._previousResults = [];
|
||||
},
|
||||
|
||||
updateSearch: function(searchString) {
|
||||
searchString = searchString.replace(/^\s+/g, "").replace(/\s+$/g, "");
|
||||
if (searchString == '')
|
||||
return null;
|
||||
|
||||
let terms = searchString.split(/\s+/);
|
||||
let isSubSearch = terms.length == this._previousTerms.length;
|
||||
if (isSubSearch) {
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
if (terms[i].indexOf(this._previousTerms[i]) != 0) {
|
||||
isSubSearch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let results = [];
|
||||
if (isSubSearch) {
|
||||
for (let i = 0; i < this._previousResults.length; i++) {
|
||||
let [provider, previousResults] = this._previousResults[i];
|
||||
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
|
||||
if (providerResults.length > 0)
|
||||
results.push([provider, providerResults]);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
let providerResults = provider.getInitialResultSet(terms);
|
||||
if (providerResults.length > 0)
|
||||
results.push([provider, providerResults]);
|
||||
}
|
||||
}
|
||||
|
||||
this._previousTerms = terms;
|
||||
this._previousResults = results;
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(SearchSystem.prototype);
|
@ -69,6 +69,8 @@ Sidebar.prototype = {
|
||||
Lang.bind(this, this._expandedChanged));
|
||||
this._gconf.connect('changed::sidebar/visible',
|
||||
Lang.bind(this, this._visibleChanged));
|
||||
|
||||
this._adjustPosition();
|
||||
},
|
||||
|
||||
addWidget: function(widget) {
|
||||
@ -82,6 +84,14 @@ Sidebar.prototype = {
|
||||
|
||||
this.box.append(widgetBox.actor, Big.BoxPackFlags.NONE);
|
||||
this._widgets.push(widgetBox);
|
||||
this._adjustPosition();
|
||||
},
|
||||
|
||||
_adjustPosition: function() {
|
||||
let primary=global.get_primary_monitor();
|
||||
|
||||
this.actor.y = Math.max(primary.y + Panel.PANEL_HEIGHT,primary.height/2 - this.actor.height/2);
|
||||
this.actor.x = primary.x;
|
||||
},
|
||||
|
||||
_visibleChanged: function() {
|
||||
|
@ -354,8 +354,7 @@ RecentDocsWidget.prototype = {
|
||||
for (i = 0; i < docs.length; i++) {
|
||||
let docInfo = new DocInfo.DocInfo (docs[i]);
|
||||
|
||||
if (docInfo.exists())
|
||||
items.push(docInfo);
|
||||
items.push(docInfo);
|
||||
}
|
||||
|
||||
items.sort(function (a,b) { return b.timestamp - a.timestamp; });
|
||||
|
@ -337,8 +337,6 @@ WindowOverlay.prototype = {
|
||||
button._overlap = 0;
|
||||
|
||||
windowClone.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
windowClone.actor.connect('notify::allocation',
|
||||
Lang.bind(this, this._positionItems));
|
||||
windowClone.actor.connect('enter-event',
|
||||
Lang.bind(this, this._onEnter));
|
||||
windowClone.actor.connect('leave-event',
|
||||
@ -395,23 +393,30 @@ WindowOverlay.prototype = {
|
||||
this.title.height + this.title._spacing;
|
||||
},
|
||||
|
||||
_positionItems: function(win) {
|
||||
/**
|
||||
* @cloneX: x position of windowClone
|
||||
* @cloneY: y position of windowClone
|
||||
* @cloneWidth: width of windowClone
|
||||
* @cloneHeight height of windowClone
|
||||
*/
|
||||
// These parameters are not the values retrieved with
|
||||
// get_transformed_position() and get_transformed_size(),
|
||||
// as windowClone might be moving.
|
||||
// See Workspace._fadeInWindowOverlay
|
||||
updatePositions: function(cloneX, cloneY, cloneWidth, cloneHeight) {
|
||||
let button = this.closeButton;
|
||||
let title = this.title;
|
||||
|
||||
let [x, y] = win.get_transformed_position();
|
||||
let [w, h] = win.get_transformed_size();
|
||||
|
||||
let buttonX = x + w - button._overlap;
|
||||
let buttonY = y - button.height + button._overlap;
|
||||
let buttonX = cloneX + cloneWidth - button._overlap;
|
||||
let buttonY = cloneY - button.height + button._overlap;
|
||||
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
|
||||
|
||||
if (!title.fullWidth)
|
||||
title.fullWidth = title.width;
|
||||
title.width = Math.min(title.fullWidth, w);
|
||||
title.width = Math.min(title.fullWidth, cloneWidth);
|
||||
|
||||
let titleX = x + (w - title.width) / 2;
|
||||
let titleY = y + h + title._spacing;
|
||||
let titleX = cloneX + (cloneWidth - title.width) / 2;
|
||||
let titleY = cloneY + cloneHeight + title._spacing;
|
||||
title.set_position(Math.floor(titleX), Math.floor(titleY));
|
||||
},
|
||||
|
||||
@ -496,8 +501,8 @@ WindowOverlay.prototype = {
|
||||
|
||||
let closeNode = this.closeButton.get_theme_node();
|
||||
|
||||
let [success, len] = closeNode.get_length('-shell-close-overlap',
|
||||
false);
|
||||
[success, len] = closeNode.get_length('-shell-close-overlap',
|
||||
false);
|
||||
if (success)
|
||||
this.closeButton._overlap = len;
|
||||
|
||||
@ -1034,7 +1039,7 @@ Workspace.prototype = {
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: "easeOutQuad",
|
||||
onComplete: Lang.bind(this, function() {
|
||||
overlay.fadeIn();
|
||||
this._fadeInWindowOverlay(clone, overlay);
|
||||
})
|
||||
});
|
||||
}
|
||||
@ -1058,13 +1063,31 @@ Workspace.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_fadeInWindowOverlay: function(clone, overlay) {
|
||||
// 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 [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;
|
||||
|
||||
overlay.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight);
|
||||
overlay.fadeIn();
|
||||
},
|
||||
|
||||
_fadeInAllOverlays: function() {
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
let overlay = this._windowOverlays[i];
|
||||
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
|
||||
continue;
|
||||
overlay.fadeIn();
|
||||
this._fadeInWindowOverlay(clone, overlay);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -6,9 +6,11 @@ de
|
||||
el
|
||||
en_GB
|
||||
es
|
||||
fi
|
||||
fr
|
||||
ga
|
||||
gl
|
||||
he
|
||||
hu
|
||||
it
|
||||
ko
|
||||
|
@ -9,5 +9,4 @@ js/ui/runDialog.js
|
||||
js/ui/widget.js
|
||||
src/gdmuser/gdm-user.c
|
||||
src/shell-global.c
|
||||
src/shell-status-menu.c
|
||||
src/shell-uri-util.c
|
||||
|
181
po/es.po
181
po/es.po
@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: gnome-shell master\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&component=general\n"
|
||||
"POT-Creation-Date: 2009-10-27 20:37+0000\n"
|
||||
"PO-Revision-Date: 2009-10-27 23:32+0100\n"
|
||||
"POT-Creation-Date: 2009-12-18 15:06+0000\n"
|
||||
"PO-Revision-Date: 2009-12-19 14:41+0100\n"
|
||||
"Last-Translator: Jorge González <jorgegonz@svn.gnome.org>\n"
|
||||
"Language-Team: Español <gnome-es-list@gnome.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -25,76 +25,64 @@ msgstr "GNOME Shell"
|
||||
msgid "Window management and application launching"
|
||||
msgstr "Gestión de ventanas e inicio de aplicaciones"
|
||||
|
||||
#: ../js/ui/appDisplay.js:332
|
||||
msgid "Frequent"
|
||||
msgstr "Frecuentes"
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/appDisplay.js:252 ../js/ui/dash.js:852
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "APLICACIONES"
|
||||
|
||||
#: ../js/ui/appDisplay.js:867
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "Arrastrar aquí para añadir a los favoritos"
|
||||
#: ../js/ui/appDisplay.js:276
|
||||
msgid "PREFERENCES"
|
||||
msgstr "PREFERENCIAS"
|
||||
|
||||
#: ../js/ui/appIcon.js:426
|
||||
#: ../js/ui/appDisplay.js:707 ../js/ui/appIcon.js:425
|
||||
msgid "New Window"
|
||||
msgstr "Ventana nueva"
|
||||
|
||||
#: ../js/ui/appIcon.js:430
|
||||
#: ../js/ui/appDisplay.js:711 ../js/ui/appIcon.js:429
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Quitar de los favoritos"
|
||||
|
||||
#: ../js/ui/appIcon.js:431
|
||||
#: ../js/ui/appDisplay.js:712 ../js/ui/appIcon.js:430
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Añadir a los favoritos"
|
||||
|
||||
#: ../js/ui/dash.js:283
|
||||
#: ../js/ui/appDisplay.js:1064
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "Arrastrar aquí para añadir a los favoritos"
|
||||
|
||||
#: ../js/ui/dash.js:240
|
||||
msgid "Find..."
|
||||
msgstr "Buscar…"
|
||||
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "More"
|
||||
msgstr "Más"
|
||||
|
||||
#: ../js/ui/dash.js:543
|
||||
msgid "(see all)"
|
||||
msgstr "(ver todo)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:725 ../js/ui/dash.js:787
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "APLICACIONES"
|
||||
#: ../js/ui/dash.js:437
|
||||
#| msgid "Search"
|
||||
msgid "Searching..."
|
||||
msgstr "Buscando…"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:745
|
||||
#: ../js/ui/dash.js:872 ../js/ui/placeDisplay.js:471
|
||||
msgid "PLACES"
|
||||
msgstr "LUGARES"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:752 ../js/ui/dash.js:797
|
||||
#: ../js/ui/dash.js:879
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "DOCUMENTOS RECIENTES"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:777 ../js/ui/dash.js:961
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "RESULTADOS DE LA BÚSQUEDA"
|
||||
|
||||
#: ../js/ui/dash.js:792
|
||||
msgid "PREFERENCES"
|
||||
msgstr "PREFERENCIAS"
|
||||
|
||||
#. Button on the left side of the panel.
|
||||
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:274
|
||||
#: ../js/ui/panel.js:227
|
||||
msgid "Activities"
|
||||
msgstr "Actividades"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:491
|
||||
#: ../js/ui/panel.js:440
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %H:%M"
|
||||
|
||||
#: ../js/ui/places.js:178
|
||||
#: ../js/ui/placeDisplay.js:99
|
||||
msgid "Connect to..."
|
||||
msgstr "Conectar a…"
|
||||
|
||||
@ -120,101 +108,49 @@ msgstr "Aplicaciones"
|
||||
msgid "Recent Documents"
|
||||
msgstr "Documentos recientes"
|
||||
|
||||
#: ../src/shell-global.c:812
|
||||
#: ../src/shell-global.c:826
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Hace menos de un minuto"
|
||||
|
||||
#: ../src/shell-global.c:815
|
||||
#: ../src/shell-global.c:829
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "Hace %d minuto"
|
||||
msgstr[1] "Hace %d minutos"
|
||||
|
||||
#: ../src/shell-global.c:818
|
||||
#: ../src/shell-global.c:832
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "Hace %d hora"
|
||||
msgstr[1] "Hace %d horas"
|
||||
|
||||
#: ../src/shell-global.c:821
|
||||
#: ../src/shell-global.c:835
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "Hace %d día"
|
||||
msgstr[1] "Hace %d días"
|
||||
|
||||
#: ../src/shell-global.c:824
|
||||
#: ../src/shell-global.c:838
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
msgstr[0] "Hace %d semana"
|
||||
msgstr[1] "Hace %d semanas"
|
||||
|
||||
#: ../src/shell-status-menu.c:156
|
||||
msgid "Unknown"
|
||||
msgstr "Desconocido"
|
||||
|
||||
#: ../src/shell-status-menu.c:212
|
||||
#, c-format
|
||||
msgid "Can't lock screen: %s"
|
||||
msgstr "No se puede bloquear la pantalla: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:227
|
||||
#, c-format
|
||||
msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||
msgstr ""
|
||||
"No se puede establecer temporalmente el salvapantallas a oscurecer pantalla: "
|
||||
"%s"
|
||||
|
||||
#: ../src/shell-status-menu.c:351
|
||||
#, c-format
|
||||
msgid "Can't logout: %s"
|
||||
msgstr "No se puede salir de la sesión: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:492
|
||||
msgid "Account Information..."
|
||||
msgstr "Información de la cuenta…"
|
||||
|
||||
#: ../src/shell-status-menu.c:502
|
||||
msgid "Sidebar"
|
||||
msgstr "Barra lateral"
|
||||
|
||||
#: ../src/shell-status-menu.c:510
|
||||
msgid "System Preferences..."
|
||||
msgstr "Preferencias del sistema…"
|
||||
|
||||
#: ../src/shell-status-menu.c:525
|
||||
msgid "Lock Screen"
|
||||
msgstr "Bloquear la pantalla"
|
||||
|
||||
#: ../src/shell-status-menu.c:535
|
||||
msgid "Switch User"
|
||||
msgstr "Cambiar de usuario"
|
||||
|
||||
#. Only show switch user if there are other users
|
||||
#. Log Out
|
||||
#: ../src/shell-status-menu.c:546
|
||||
msgid "Log Out..."
|
||||
msgstr "Salir…"
|
||||
|
||||
#. Shut down
|
||||
#: ../src/shell-status-menu.c:557
|
||||
msgid "Shut Down..."
|
||||
msgstr "Apagar…"
|
||||
|
||||
#: ../src/shell-uri-util.c:87
|
||||
#: ../src/shell-uri-util.c:89
|
||||
msgid "Home Folder"
|
||||
msgstr "Carpeta personal"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-uri-util.c:102
|
||||
#: ../src/shell-uri-util.c:104
|
||||
msgid "File System"
|
||||
msgstr "Sistema de archivos"
|
||||
|
||||
#: ../src/shell-uri-util.c:248
|
||||
#: ../src/shell-uri-util.c:250
|
||||
msgid "Search"
|
||||
msgstr "Buscar"
|
||||
|
||||
@ -223,11 +159,58 @@ msgstr "Buscar"
|
||||
#. * example, "Trash: some-directory". It means that the
|
||||
#. * directory called "some-directory" is in the trash.
|
||||
#.
|
||||
#: ../src/shell-uri-util.c:298
|
||||
#: ../src/shell-uri-util.c:300
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "Frequent"
|
||||
#~ msgstr "Frecuentes"
|
||||
|
||||
#~ msgid "More"
|
||||
#~ msgstr "Más"
|
||||
|
||||
#~ msgid "(see all)"
|
||||
#~ msgstr "(ver todo)"
|
||||
|
||||
#~ msgid "SEARCH RESULTS"
|
||||
#~ msgstr "RESULTADOS DE LA BÚSQUEDA"
|
||||
|
||||
#~ msgid "Unknown"
|
||||
#~ msgstr "Desconocido"
|
||||
|
||||
#~ msgid "Can't lock screen: %s"
|
||||
#~ msgstr "No se puede bloquear la pantalla: %s"
|
||||
|
||||
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||
#~ msgstr ""
|
||||
#~ "No se puede establecer temporalmente el salvapantallas a oscurecer "
|
||||
#~ "pantalla: %s"
|
||||
|
||||
#~ msgid "Can't logout: %s"
|
||||
#~ msgstr "No se puede salir de la sesión: %s"
|
||||
|
||||
#~ msgid "Account Information..."
|
||||
#~ msgstr "Información de la cuenta…"
|
||||
|
||||
#~ msgid "Sidebar"
|
||||
#~ msgstr "Barra lateral"
|
||||
|
||||
#~ msgid "System Preferences..."
|
||||
#~ msgstr "Preferencias del sistema…"
|
||||
|
||||
#~ msgid "Lock Screen"
|
||||
#~ msgstr "Bloquear la pantalla"
|
||||
|
||||
#~ msgid "Switch User"
|
||||
#~ msgstr "Cambiar de usuario"
|
||||
|
||||
#~ msgid "Log Out..."
|
||||
#~ msgstr "Salir…"
|
||||
|
||||
#~ msgid "Shut Down..."
|
||||
#~ msgstr "Apagar…"
|
||||
|
||||
#~ msgid "Browse"
|
||||
#~ msgstr "Examine"
|
||||
|
||||
|
102
po/fr.po
102
po/fr.po
@ -6,13 +6,13 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: HEAD\n"
|
||||
"Project-Id-Version: gnome-shell master fr\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&component=general\n"
|
||||
"POT-Creation-Date: 2009-09-09 21:30+0000\n"
|
||||
"PO-Revision-Date: 2009-09-11 21:40+0200\n"
|
||||
"Last-Translator: Mathieu Bridon <bochecha@fedoraproject.org>\n"
|
||||
"Language-Team: GNOME French Team\n"
|
||||
"POT-Creation-Date: 2009-11-13 17:44+0000\n"
|
||||
"PO-Revision-Date: 2009-12-05 16:43+0100\n"
|
||||
"Last-Translator: Pablo Martin-Gomez <pablo.martin-gomez@laposte.net>\n"
|
||||
"Language-Team: GNOME French Team <gnomefr@traduc.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@ -26,80 +26,115 @@ msgstr "GNOME Shell"
|
||||
msgid "Window management and application launching"
|
||||
msgstr "Gestion des fenêtres et lancement des applications"
|
||||
|
||||
#. left side
|
||||
#: ../js/ui/panel.js:269
|
||||
msgid "Activities"
|
||||
msgstr "Activités"
|
||||
#: ../js/ui/appDisplay.js:696
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "Glisser ici pour ajouter aux favoris"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:452
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %H:%M"
|
||||
#: ../js/ui/appIcon.js:425
|
||||
msgid "New Window"
|
||||
msgstr "Nouvelle fenêtre"
|
||||
|
||||
#: ../js/ui/dash.js:255
|
||||
#: ../js/ui/appIcon.js:429
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Enlever des favoris"
|
||||
|
||||
#: ../js/ui/appIcon.js:430
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Ajouter aux favoris"
|
||||
|
||||
#: ../js/ui/dash.js:237
|
||||
msgid "Find..."
|
||||
msgstr "Rechercher..."
|
||||
|
||||
#: ../js/ui/dash.js:372
|
||||
msgid "Browse"
|
||||
msgstr "Parcourir"
|
||||
|
||||
#: ../js/ui/dash.js:508
|
||||
msgid "(see all)"
|
||||
msgstr "(tout afficher)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:700 ../js/ui/dash.js:756 ../js/ui/dash.js:887
|
||||
#: ../js/ui/dash.js:656 ../js/ui/dash.js:718
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "APPLICATIONS"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:720
|
||||
#: ../js/ui/dash.js:676 ../js/ui/dash.js:733
|
||||
msgid "PLACES"
|
||||
msgstr "RACCOURCIS"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:727 ../js/ui/dash.js:768 ../js/ui/dash.js:861
|
||||
#: ../js/ui/dash.js:683 ../js/ui/dash.js:728
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "DOCUMENTS RÉCENTS"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:746 ../js/ui/dash.js:850 ../js/ui/dash.js:876
|
||||
#: ../js/ui/dash.js:708 ../js/ui/dash.js:898
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "RÉSULTATS DE LA RECHERCHE"
|
||||
|
||||
#: ../js/ui/runDialog.js:90
|
||||
#: ../js/ui/dash.js:723
|
||||
msgid "PREFERENCES"
|
||||
msgstr "PRÉFÉRENCES"
|
||||
|
||||
#. Button on the left side of the panel.
|
||||
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:274
|
||||
msgid "Activities"
|
||||
msgstr "Activités"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:491
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %H:%M"
|
||||
|
||||
#: ../js/ui/placeDisplay.js:84
|
||||
msgid "Connect to..."
|
||||
msgstr "Connexion à..."
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Veuillez saisir une commande :"
|
||||
|
||||
#: ../src/shell-global.c:799
|
||||
#: ../js/ui/runDialog.js:173
|
||||
#, c-format
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "Exécution de « %s » impossible :"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/widget.js:163
|
||||
msgid "%H:%M"
|
||||
msgstr "%H:%M"
|
||||
|
||||
#: ../js/ui/widget.js:317
|
||||
msgid "Applications"
|
||||
msgstr "Applications"
|
||||
|
||||
#: ../js/ui/widget.js:339
|
||||
msgid "Recent Documents"
|
||||
msgstr "Documents récents"
|
||||
|
||||
#: ../src/shell-global.c:821
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Il y a moins d'une minute"
|
||||
|
||||
#: ../src/shell-global.c:802
|
||||
#: ../src/shell-global.c:824
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "Il y a %d minute"
|
||||
msgstr[1] "Il y a %d minutes"
|
||||
|
||||
#: ../src/shell-global.c:805
|
||||
#: ../src/shell-global.c:827
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "Il y a %d heure"
|
||||
msgstr[1] "Il y a %d heures"
|
||||
|
||||
#: ../src/shell-global.c:808
|
||||
#: ../src/shell-global.c:830
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "Il y a %d jour"
|
||||
msgstr[1] "Il y a %d jours"
|
||||
|
||||
#: ../src/shell-global.c:811
|
||||
#: ../src/shell-global.c:833
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
@ -180,3 +215,6 @@ msgstr "Recherche"
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s : %2$s"
|
||||
|
||||
#~ msgid "Browse"
|
||||
#~ msgstr "Parcourir"
|
||||
|
240
po/gl.po
240
po/gl.po
@ -2,19 +2,22 @@
|
||||
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the gnome-shell package.
|
||||
# Fran Diéguez <fran.dieguez@mabishu.com>, 2009.
|
||||
#
|
||||
# Anton Meixome <certima@certima.net>, 2009.
|
||||
# Antón Méixome <meixome@certima.net>, 2009.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell master\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-09-15 23:06+0200\n"
|
||||
"PO-Revision-Date: 2009-09-10 22:32+0100\n"
|
||||
"Last-Translator: Fran Diéguez <fran.dieguez@glug.es>\n"
|
||||
"Language-Team: Galician <gnome@mancomun.org>\n"
|
||||
"POT-Creation-Date: 2009-12-28 17:07+0100\n"
|
||||
"PO-Revision-Date: 2009-12-28 08:11+0100\n"
|
||||
"Last-Translator: Antón Méixome <meixome@certima.net>\n"
|
||||
"Language-Team: Galician Proxecto Trasno <proxecto@trasno.net>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: gl\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Virtaal 0.4.0\n"
|
||||
|
||||
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||
msgid "GNOME Shell"
|
||||
@ -22,154 +25,133 @@ msgstr "GNOME Shell"
|
||||
|
||||
#: ../data/gnome-shell.desktop.in.in.h:2
|
||||
msgid "Window management and application launching"
|
||||
msgstr "Xestor de xanelas e lanzado de aplicativos"
|
||||
msgstr "Xestor de xanelas e lanzamento de aplicativos"
|
||||
|
||||
#. left side
|
||||
#: ../js/ui/panel.js:269
|
||||
msgid "Activities"
|
||||
msgstr "Actividades"
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/appDisplay.js:252 ../js/ui/dash.js:858
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "APLICATIVOS"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:452
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %l:%M %p"
|
||||
#: ../js/ui/appDisplay.js:276
|
||||
msgid "PREFERENCES"
|
||||
msgstr "PREFERENCIAS"
|
||||
|
||||
#: ../js/ui/dash.js:283
|
||||
#: ../js/ui/appDisplay.js:707 ../js/ui/appIcon.js:425
|
||||
msgid "New Window"
|
||||
msgstr "Xanela nova"
|
||||
|
||||
#: ../js/ui/appDisplay.js:711 ../js/ui/appIcon.js:429
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Eliminar de Favoritos"
|
||||
|
||||
#: ../js/ui/appDisplay.js:712 ../js/ui/appIcon.js:430
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Engadir a Favoritos"
|
||||
|
||||
#: ../js/ui/appDisplay.js:1064
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "Arrastra aquí para engadir favoritos"
|
||||
|
||||
#: ../js/ui/dash.js:240
|
||||
msgid "Find..."
|
||||
msgstr "Buscar..."
|
||||
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "Browse"
|
||||
msgstr "Explorar"
|
||||
|
||||
#: ../js/ui/dash.js:536
|
||||
msgid "(see all)"
|
||||
msgstr "(ver todos)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "APLICATIVOS"
|
||||
#: ../js/ui/dash.js:437
|
||||
msgid "Searching..."
|
||||
msgstr "Buscando..."
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:773
|
||||
#: ../js/ui/dash.js:878 ../js/ui/placeDisplay.js:519
|
||||
msgid "PLACES"
|
||||
msgstr "LUGARES"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
|
||||
#: ../js/ui/dash.js:885
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "DOCUMENTOS RECENTES"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "RESULTADOS DA BÚSQUEDA"
|
||||
#. Button on the left side of the panel.
|
||||
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:227
|
||||
msgid "Activities"
|
||||
msgstr "Actividades"
|
||||
|
||||
#: ../js/ui/dash.js:814
|
||||
msgid "PREFERENCES"
|
||||
msgstr ""
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:440
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %l:%M %p"
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
#: ../js/ui/placeDisplay.js:144
|
||||
msgid "Connect to..."
|
||||
msgstr "Conectar con..."
|
||||
|
||||
#: ../js/ui/runDialog.js:235
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Insira unha orde:"
|
||||
|
||||
#: ../src/shell-global.c:799
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Menos de un minuto"
|
||||
#: ../js/ui/runDialog.js:351
|
||||
#, c-format
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "Fallou a execución de %s"
|
||||
|
||||
#: ../src/shell-global.c:802
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/widget.js:163
|
||||
msgid "%H:%M"
|
||||
msgstr "%H:%M"
|
||||
|
||||
#: ../js/ui/widget.js:317
|
||||
msgid "Applications"
|
||||
msgstr "Aplicativos"
|
||||
|
||||
#: ../js/ui/widget.js:339
|
||||
msgid "Recent Documents"
|
||||
msgstr "Documentos recentes"
|
||||
|
||||
#: ../src/shell-global.c:890
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Hai menos dun minuto"
|
||||
|
||||
#: ../src/shell-global.c:893
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "fai %d minuto"
|
||||
msgstr[1] "fai %d minutos"
|
||||
msgstr[0] "hai %d minuto"
|
||||
msgstr[1] "hai %d minutos"
|
||||
|
||||
#: ../src/shell-global.c:805
|
||||
#: ../src/shell-global.c:896
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "fai %d hora"
|
||||
msgstr[1] "fai %d horas"
|
||||
msgstr[0] "hai %d hora"
|
||||
msgstr[1] "hai %d horas"
|
||||
|
||||
#: ../src/shell-global.c:808
|
||||
#: ../src/shell-global.c:899
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "fai %d día"
|
||||
msgstr[1] "fai %d días"
|
||||
msgstr[0] "hai %d día"
|
||||
msgstr[1] "hai %d días"
|
||||
|
||||
#: ../src/shell-global.c:811
|
||||
#: ../src/shell-global.c:902
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
msgstr[0] "fai %d semana"
|
||||
msgstr[1] "fai %d semanas"
|
||||
msgstr[0] "hai %d semana"
|
||||
msgstr[1] "hai %d semanas"
|
||||
|
||||
#: ../src/shell-status-menu.c:156
|
||||
msgid "Unknown"
|
||||
msgstr "Descoñecido"
|
||||
|
||||
#: ../src/shell-status-menu.c:212
|
||||
#, c-format
|
||||
msgid "Can't lock screen: %s"
|
||||
msgstr "Non foi posíbel bloquear a pantalla: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:227
|
||||
#, c-format
|
||||
msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||
msgstr ""
|
||||
"Non foi posíbel establecer o salvapantallas a unha pantalla en branco: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:351
|
||||
#, c-format
|
||||
msgid "Can't logout: %s"
|
||||
msgstr "Non foi posíbel pechar a sesión: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:492
|
||||
msgid "Account Information..."
|
||||
msgstr "Información da conta..."
|
||||
|
||||
#: ../src/shell-status-menu.c:502
|
||||
msgid "Sidebar"
|
||||
msgstr "Barra lateral"
|
||||
|
||||
#: ../src/shell-status-menu.c:510
|
||||
msgid "System Preferences..."
|
||||
msgstr "Preferenzas do sistema..."
|
||||
|
||||
#: ../src/shell-status-menu.c:525
|
||||
msgid "Lock Screen"
|
||||
msgstr "Bloquear pantalla"
|
||||
|
||||
#: ../src/shell-status-menu.c:535
|
||||
msgid "Switch User"
|
||||
msgstr "Cambiar de usuario"
|
||||
|
||||
#. Only show switch user if there are other users
|
||||
#. Log Out
|
||||
#: ../src/shell-status-menu.c:546
|
||||
msgid "Log Out..."
|
||||
msgstr "Pechar sesión..."
|
||||
|
||||
#. Shut down
|
||||
#: ../src/shell-status-menu.c:557
|
||||
msgid "Shut Down..."
|
||||
msgstr "Apagar..."
|
||||
|
||||
#: ../src/shell-uri-util.c:87
|
||||
#: ../src/shell-uri-util.c:89
|
||||
msgid "Home Folder"
|
||||
msgstr "Cartafol persoal"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-uri-util.c:102
|
||||
#: ../src/shell-uri-util.c:104
|
||||
msgid "File System"
|
||||
msgstr "Sistema de ficheiros"
|
||||
|
||||
#: ../src/shell-uri-util.c:248
|
||||
#: ../src/shell-uri-util.c:250
|
||||
msgid "Search"
|
||||
msgstr "Buscar"
|
||||
|
||||
@ -178,11 +160,55 @@ msgstr "Buscar"
|
||||
#. * example, "Trash: some-directory". It means that the
|
||||
#. * directory called "some-directory" is in the trash.
|
||||
#.
|
||||
#: ../src/shell-uri-util.c:298
|
||||
#: ../src/shell-uri-util.c:300
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "SEARCH RESULTS"
|
||||
#~ msgstr "RESULTADOS DA BUSCA"
|
||||
|
||||
#~ msgid "Unknown"
|
||||
#~ msgstr "Descoñecido"
|
||||
|
||||
#~ msgid "Can't lock screen: %s"
|
||||
#~ msgstr "Non foi posíbel bloquear a pantalla: %s"
|
||||
|
||||
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||
#~ msgstr ""
|
||||
#~ "Non foi posíbel estabelecer temporalmente o salvapantallas a unha "
|
||||
#~ "pantalla en branco: %s"
|
||||
|
||||
#~ msgid "Can't logout: %s"
|
||||
#~ msgstr "Non foi posíbel pechar a sesión: %s"
|
||||
|
||||
#~ msgid "Account Information..."
|
||||
#~ msgstr "Información da conta..."
|
||||
|
||||
#~ msgid "Sidebar"
|
||||
#~ msgstr "Barra lateral"
|
||||
|
||||
#~ msgid "System Preferences..."
|
||||
#~ msgstr "Preferencias do sistema..."
|
||||
|
||||
#~ msgid "Lock Screen"
|
||||
#~ msgstr "Bloquear pantalla"
|
||||
|
||||
#~ msgid "Switch User"
|
||||
#~ msgstr "Cambiar de usuario"
|
||||
|
||||
#~ msgid "Log Out..."
|
||||
#~ msgstr "Saír da sesión..."
|
||||
|
||||
#~ msgid "Shut Down..."
|
||||
#~ msgstr "Apagar..."
|
||||
|
||||
#~ msgid "Browse"
|
||||
#~ msgstr "Explorar"
|
||||
|
||||
#~ msgid "(see all)"
|
||||
#~ msgstr "(ver todos)"
|
||||
|
||||
#~ msgid "Find apps or documents"
|
||||
#~ msgstr "Atopar aplicativos ou documentos"
|
||||
|
||||
|
215
po/he.po
Normal file
215
po/he.po
Normal file
@ -0,0 +1,215 @@
|
||||
# Hebrew translation for gnome-shell.
|
||||
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the gnome-shell package.
|
||||
# liel <lielft@gmail.com>, 2009.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell master\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&component=general\n"
|
||||
"POT-Creation-Date: 2009-11-13 17:44+0000\n"
|
||||
"PO-Revision-Date: 2009-11-28 17:33+0200\n"
|
||||
"Last-Translator: Liel Fridman <lielft@gmail.com>\n"
|
||||
"Language-Team: Hebrew <he@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||
msgid "GNOME Shell"
|
||||
msgstr "מעטפת GNOME"
|
||||
|
||||
#: ../data/gnome-shell.desktop.in.in.h:2
|
||||
msgid "Window management and application launching"
|
||||
msgstr "ניהול חלונות והרצת יישומים"
|
||||
|
||||
#: ../js/ui/appDisplay.js:696
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "יש לגרור פריטים לכאן כדי להוסיף מועדפים"
|
||||
|
||||
#: ../js/ui/appIcon.js:425
|
||||
msgid "New Window"
|
||||
msgstr "חלון חדש"
|
||||
|
||||
#: ../js/ui/appIcon.js:429
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "הסר מהמועדפים"
|
||||
|
||||
#: ../js/ui/appIcon.js:430
|
||||
msgid "Add to Favorites"
|
||||
msgstr "הוסף למועדפים"
|
||||
|
||||
#: ../js/ui/dash.js:237
|
||||
msgid "Find..."
|
||||
msgstr "חפש..."
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:656 ../js/ui/dash.js:718
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "יישומים"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:676 ../js/ui/dash.js:733
|
||||
msgid "PLACES"
|
||||
msgstr "מקומות"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:683 ../js/ui/dash.js:728
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "מסמכים אחרונים"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:708 ../js/ui/dash.js:898
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "תוצאות חיפוש"
|
||||
|
||||
#: ../js/ui/dash.js:723
|
||||
msgid "PREFERENCES"
|
||||
msgstr "העדפות"
|
||||
|
||||
#. Button on the left side of the panel.
|
||||
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:274
|
||||
msgid "Activities"
|
||||
msgstr "פעילויות"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:491
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %l:%M %p"
|
||||
|
||||
#: ../js/ui/placeDisplay.js:84
|
||||
msgid "Connect to..."
|
||||
msgstr "התחבר אל..."
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
msgid "Please enter a command:"
|
||||
msgstr "נא להזין פקודה:"
|
||||
|
||||
#: ../js/ui/runDialog.js:173
|
||||
#, c-format
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "ההרצה של '%s' נכשלה:"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/widget.js:163
|
||||
msgid "%H:%M"
|
||||
msgstr "%H:%M"
|
||||
|
||||
#: ../js/ui/widget.js:317
|
||||
msgid "Applications"
|
||||
msgstr "יישומים"
|
||||
|
||||
#: ../js/ui/widget.js:339
|
||||
msgid "Recent Documents"
|
||||
msgstr "מסמכים אחרונים"
|
||||
|
||||
#: ../src/shell-global.c:821
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "לפני פחות מדקה"
|
||||
|
||||
#: ../src/shell-global.c:824
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "לפני דקה"
|
||||
msgstr[1] "לפני %d דקות"
|
||||
|
||||
#: ../src/shell-global.c:827
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "לפני שעה"
|
||||
msgstr[1] "לפני %d שעות"
|
||||
|
||||
#: ../src/shell-global.c:830
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "לפני יום"
|
||||
msgstr[1] "לפני %d ימים"
|
||||
|
||||
#: ../src/shell-global.c:833
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
msgstr[0] "לפני שבוע"
|
||||
msgstr[1] "לפני %d שבועות"
|
||||
|
||||
#: ../src/shell-status-menu.c:156
|
||||
msgid "Unknown"
|
||||
msgstr "לא ידוע"
|
||||
|
||||
#: ../src/shell-status-menu.c:212
|
||||
#, c-format
|
||||
msgid "Can't lock screen: %s"
|
||||
msgstr "לא ניתן לנעול את המסך: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:227
|
||||
#, c-format
|
||||
msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||
msgstr "לא ניתן זמנית לקבוע שומר מסך כמסך שחור: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:351
|
||||
#, c-format
|
||||
msgid "Can't logout: %s"
|
||||
msgstr "לא ניתן להתנתק: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:492
|
||||
msgid "Account Information..."
|
||||
msgstr "מידע על המשתמש..."
|
||||
|
||||
#: ../src/shell-status-menu.c:502
|
||||
msgid "Sidebar"
|
||||
msgstr "סרגל צד"
|
||||
|
||||
#: ../src/shell-status-menu.c:510
|
||||
msgid "System Preferences..."
|
||||
msgstr "העדפות מערכת..."
|
||||
|
||||
#: ../src/shell-status-menu.c:525
|
||||
msgid "Lock Screen"
|
||||
msgstr "נעילת המסך"
|
||||
|
||||
#: ../src/shell-status-menu.c:535
|
||||
msgid "Switch User"
|
||||
msgstr "החלף משתמש"
|
||||
|
||||
#. Only show switch user if there are other users
|
||||
#. Log Out
|
||||
#: ../src/shell-status-menu.c:546
|
||||
msgid "Log Out..."
|
||||
msgstr "ניתוק..."
|
||||
|
||||
#. Shut down
|
||||
#: ../src/shell-status-menu.c:557
|
||||
msgid "Shut Down..."
|
||||
msgstr "כיבוי..."
|
||||
|
||||
#: ../src/shell-uri-util.c:87
|
||||
msgid "Home Folder"
|
||||
msgstr "תיקיית הבית"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-uri-util.c:102
|
||||
msgid "File System"
|
||||
msgstr "מערכת הקבצים"
|
||||
|
||||
#: ../src/shell-uri-util.c:248
|
||||
msgid "Search"
|
||||
msgstr "חפש"
|
||||
|
||||
#. 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.
|
||||
#.
|
||||
#: ../src/shell-uri-util.c:298
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
186
po/it.po
186
po/it.po
@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-11-03 22:26+0100\n"
|
||||
"PO-Revision-Date: 2009-11-03 22:28+0100\n"
|
||||
"POT-Creation-Date: 2009-12-28 21:58+0100\n"
|
||||
"PO-Revision-Date: 2009-12-28 21:59+0100\n"
|
||||
"Last-Translator: Milo Casagrande <milo@ubuntu.com>\n"
|
||||
"Language-Team: Italian <tp@lists.linux.it>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -24,85 +24,72 @@ msgstr "GNOME Shell"
|
||||
msgid "Window management and application launching"
|
||||
msgstr "Gestione finestre e avvio applicazioni"
|
||||
|
||||
#: ../js/ui/appDisplay.js:332
|
||||
msgid "Frequent"
|
||||
msgstr "Frequente"
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/appDisplay.js:252 ../js/ui/dash.js:858
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "Applicazioni"
|
||||
|
||||
#: ../js/ui/appDisplay.js:867
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "Trascinare qui per aggiungere ai preferiti"
|
||||
#: ../js/ui/appDisplay.js:276
|
||||
msgid "PREFERENCES"
|
||||
msgstr "Preferenze"
|
||||
|
||||
#: ../js/ui/appIcon.js:426
|
||||
#: ../js/ui/appDisplay.js:707 ../js/ui/appIcon.js:425
|
||||
msgid "New Window"
|
||||
msgstr "Nuova finestra"
|
||||
|
||||
#: ../js/ui/appIcon.js:430
|
||||
#: ../js/ui/appDisplay.js:711 ../js/ui/appIcon.js:429
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Rimuovi dai preferiti"
|
||||
|
||||
#: ../js/ui/appIcon.js:431
|
||||
#: ../js/ui/appDisplay.js:712 ../js/ui/appIcon.js:430
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Aggiungi ai preferiti"
|
||||
|
||||
#: ../js/ui/dash.js:283
|
||||
#: ../js/ui/appDisplay.js:1064
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "Trascinare qui per aggiungere ai preferiti"
|
||||
|
||||
#: ../js/ui/dash.js:240
|
||||
msgid "Find..."
|
||||
msgstr "Trova..."
|
||||
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "More"
|
||||
msgstr "Altro"
|
||||
|
||||
#: ../js/ui/dash.js:543
|
||||
msgid "(see all)"
|
||||
msgstr "(vedi tutto)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:725 ../js/ui/dash.js:787
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "Applicazioni"
|
||||
#: ../js/ui/dash.js:437
|
||||
msgid "Searching..."
|
||||
msgstr "Ricerca..."
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:745
|
||||
#: ../js/ui/dash.js:878 ../js/ui/placeDisplay.js:519
|
||||
msgid "PLACES"
|
||||
msgstr "Risorse"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:752 ../js/ui/dash.js:797
|
||||
#: ../js/ui/dash.js:885
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "Documenti recenti"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:777 ../js/ui/dash.js:961
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "Risultati ricerca"
|
||||
|
||||
#: ../js/ui/dash.js:792
|
||||
msgid "PREFERENCES"
|
||||
msgstr "Preferenze"
|
||||
|
||||
#. Button on the left side of the panel.
|
||||
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:274
|
||||
#: ../js/ui/panel.js:227
|
||||
msgid "Activities"
|
||||
msgstr "Attività"
|
||||
|
||||
# (ndt) proviamo col k, se non funge, sappiamo il perché...
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:491
|
||||
#: ../js/ui/panel.js:440
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %k.%M"
|
||||
|
||||
#: ../js/ui/places.js:178
|
||||
#: ../js/ui/placeDisplay.js:144
|
||||
msgid "Connect to..."
|
||||
msgstr "Connetti a..."
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
#: ../js/ui/runDialog.js:235
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Inserire un comando:"
|
||||
|
||||
#: ../js/ui/runDialog.js:173
|
||||
#: ../js/ui/runDialog.js:351
|
||||
#, c-format
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "Esecuzione di «%s» non riuscita:"
|
||||
@ -120,102 +107,49 @@ msgstr "Applicazioni"
|
||||
msgid "Recent Documents"
|
||||
msgstr "Documenti recenti"
|
||||
|
||||
#: ../src/shell-global.c:821
|
||||
#: ../src/shell-global.c:890
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Meno di un minuto fa"
|
||||
|
||||
#: ../src/shell-global.c:824
|
||||
#: ../src/shell-global.c:893
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "%d minuto fa"
|
||||
msgstr[1] "%d minuti fa"
|
||||
|
||||
#: ../src/shell-global.c:827
|
||||
#: ../src/shell-global.c:896
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "%d ora fa"
|
||||
msgstr[1] "%d ore fa"
|
||||
|
||||
#: ../src/shell-global.c:830
|
||||
#: ../src/shell-global.c:899
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "%d giorno fa"
|
||||
msgstr[1] "%d giorni fa"
|
||||
|
||||
#: ../src/shell-global.c:833
|
||||
#: ../src/shell-global.c:902
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
msgstr[0] "%d settimana fa"
|
||||
msgstr[1] "%d settimane fa"
|
||||
|
||||
# (ndt) valutare se vada al femminile
|
||||
#: ../src/shell-status-menu.c:156
|
||||
msgid "Unknown"
|
||||
msgstr "Sconosciuto"
|
||||
|
||||
#: ../src/shell-status-menu.c:212
|
||||
#, c-format
|
||||
msgid "Can't lock screen: %s"
|
||||
msgstr "Impossibile bloccare lo schermo: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:227
|
||||
#, c-format
|
||||
msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||
msgstr ""
|
||||
"Impossibile impostare temporaneamente il salva schermo a schermo nero: %s "
|
||||
|
||||
#: ../src/shell-status-menu.c:351
|
||||
#, c-format
|
||||
msgid "Can't logout: %s"
|
||||
msgstr "Impossibile terminare la sessione: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:492
|
||||
msgid "Account Information..."
|
||||
msgstr "Informazioni account..."
|
||||
|
||||
#: ../src/shell-status-menu.c:502
|
||||
msgid "Sidebar"
|
||||
msgstr "Barra laterale"
|
||||
|
||||
#: ../src/shell-status-menu.c:510
|
||||
msgid "System Preferences..."
|
||||
msgstr "Preferenze di sistema..."
|
||||
|
||||
#: ../src/shell-status-menu.c:525
|
||||
msgid "Lock Screen"
|
||||
msgstr "Blocca schermo"
|
||||
|
||||
#: ../src/shell-status-menu.c:535
|
||||
msgid "Switch User"
|
||||
msgstr "Cambia utente"
|
||||
|
||||
#. Only show switch user if there are other users
|
||||
#. Log Out
|
||||
#: ../src/shell-status-menu.c:546
|
||||
msgid "Log Out..."
|
||||
msgstr "Termina sessione..."
|
||||
|
||||
# (ndt) da valutare... pare che ora anche Windows usi 'Arresta...'...
|
||||
#. Shut down
|
||||
#: ../src/shell-status-menu.c:557
|
||||
msgid "Shut Down..."
|
||||
msgstr "Spegni..."
|
||||
|
||||
#: ../src/shell-uri-util.c:87
|
||||
#: ../src/shell-uri-util.c:89
|
||||
msgid "Home Folder"
|
||||
msgstr "Cartella home"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-uri-util.c:102
|
||||
#: ../src/shell-uri-util.c:104
|
||||
msgid "File System"
|
||||
msgstr "File system"
|
||||
|
||||
#: ../src/shell-uri-util.c:248
|
||||
#: ../src/shell-uri-util.c:250
|
||||
msgid "Search"
|
||||
msgstr "Cerca"
|
||||
|
||||
@ -225,7 +159,55 @@ msgstr "Cerca"
|
||||
#. * example, "Trash: some-directory". It means that the
|
||||
#. * directory called "some-directory" is in the trash.
|
||||
#.
|
||||
#: ../src/shell-uri-util.c:298
|
||||
#: ../src/shell-uri-util.c:300
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "Frequent"
|
||||
#~ msgstr "Frequente"
|
||||
|
||||
#~ msgid "More"
|
||||
#~ msgstr "Altro"
|
||||
|
||||
#~ msgid "(see all)"
|
||||
#~ msgstr "(vedi tutto)"
|
||||
|
||||
#~ msgid "SEARCH RESULTS"
|
||||
#~ msgstr "Risultati ricerca"
|
||||
|
||||
# (ndt) valutare se vada al femminile
|
||||
#~ msgid "Unknown"
|
||||
#~ msgstr "Sconosciuto"
|
||||
|
||||
#~ msgid "Can't lock screen: %s"
|
||||
#~ msgstr "Impossibile bloccare lo schermo: %s"
|
||||
|
||||
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||
#~ msgstr ""
|
||||
#~ "Impossibile impostare temporaneamente il salva schermo a schermo nero: %s "
|
||||
|
||||
#~ msgid "Can't logout: %s"
|
||||
#~ msgstr "Impossibile terminare la sessione: %s"
|
||||
|
||||
#~ msgid "Account Information..."
|
||||
#~ msgstr "Informazioni account..."
|
||||
|
||||
#~ msgid "Sidebar"
|
||||
#~ msgstr "Barra laterale"
|
||||
|
||||
#~ msgid "System Preferences..."
|
||||
#~ msgstr "Preferenze di sistema..."
|
||||
|
||||
#~ msgid "Lock Screen"
|
||||
#~ msgstr "Blocca schermo"
|
||||
|
||||
#~ msgid "Switch User"
|
||||
#~ msgstr "Cambia utente"
|
||||
|
||||
#~ msgid "Log Out..."
|
||||
#~ msgstr "Termina sessione..."
|
||||
|
||||
# (ndt) da valutare... pare che ora anche Windows usi 'Arresta...'...
|
||||
#~ msgid "Shut Down..."
|
||||
#~ msgstr "Spegni..."
|
||||
|
165
po/sl.po
165
po/sl.po
@ -1,13 +1,15 @@
|
||||
# Slovenian translation for gnome-shell.
|
||||
# Slovenian translation of gnome-shell package.
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
# This file is distributed under the same license as the gnome-shell package.
|
||||
# Matej Urbančič <mateju@svn.gnome.org>, 2009.
|
||||
#
|
||||
# Matej Urbančič <mateju@svn.gnome.org>, 2009 - 2010.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell master\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=general\n"
|
||||
"POT-Creation-Date: 2009-11-12 21:33+0000\n"
|
||||
"PO-Revision-Date: 2009-11-14 10:19+0100\n"
|
||||
"POT-Creation-Date: 2010-01-05 01:21+0000\n"
|
||||
"PO-Revision-Date: 2010-01-05 09:09+0100\n"
|
||||
"Last-Translator: Matej Urbančič <mateju@svn.gnome.org>\n"
|
||||
"Language-Team: Slovenian GNOME Translation Team <gnome-si@googlegroups.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -27,76 +29,76 @@ msgstr "Gnome lupina"
|
||||
msgid "Window management and application launching"
|
||||
msgstr "Upravljanje oken in zaganjanje programov"
|
||||
|
||||
#: ../js/ui/appDisplay.js:696
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "S potegom na to mesto se izbor doda med priljubljene"
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/appDisplay.js:252
|
||||
#: ../js/ui/dash.js:858
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "Programi"
|
||||
|
||||
#: ../js/ui/appDisplay.js:276
|
||||
msgid "PREFERENCES"
|
||||
msgstr "Možnosti"
|
||||
|
||||
#: ../js/ui/appDisplay.js:707
|
||||
#: ../js/ui/appIcon.js:425
|
||||
msgid "New Window"
|
||||
msgstr "Novo okno"
|
||||
|
||||
#: ../js/ui/appDisplay.js:711
|
||||
#: ../js/ui/appIcon.js:429
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Odstrani iz priljubljenih"
|
||||
|
||||
#: ../js/ui/appDisplay.js:712
|
||||
#: ../js/ui/appIcon.js:430
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Dodaj med priljubljene"
|
||||
|
||||
#: ../js/ui/dash.js:237
|
||||
msgid "Find..."
|
||||
msgstr "Poišči ..."
|
||||
#: ../js/ui/appDisplay.js:1064
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "S potegom na to mesto se izbor doda med priljubljene"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:656
|
||||
#: ../js/ui/dash.js:718
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "Programi"
|
||||
#: ../js/ui/dash.js:240
|
||||
msgid "Find..."
|
||||
msgstr "Najdi ..."
|
||||
|
||||
#: ../js/ui/dash.js:437
|
||||
msgid "Searching..."
|
||||
msgstr "Iskanje ..."
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:676
|
||||
#: ../js/ui/dash.js:733
|
||||
#: ../js/ui/dash.js:878
|
||||
#: ../js/ui/placeDisplay.js:519
|
||||
msgid "PLACES"
|
||||
msgstr "Mesta"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:683
|
||||
#: ../js/ui/dash.js:728
|
||||
#: ../js/ui/dash.js:885
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "Nedavni dokumenti"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:708
|
||||
#: ../js/ui/dash.js:898
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "Rezultati iskanja"
|
||||
|
||||
#: ../js/ui/dash.js:723
|
||||
msgid "PREFERENCES"
|
||||
msgstr "Lastnosti"
|
||||
|
||||
#. Button on the left side of the panel.
|
||||
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:274
|
||||
#: ../js/ui/panel.js:227
|
||||
msgid "Activities"
|
||||
msgstr "Dejavnosti"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:491
|
||||
#: ../js/ui/panel.js:440
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a, %H:%M"
|
||||
|
||||
#: ../js/ui/placeDisplay.js:84
|
||||
#: ../js/ui/placeDisplay.js:144
|
||||
msgid "Connect to..."
|
||||
msgstr "Povezava z ..."
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
#: ../js/ui/runDialog.js:235
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Vnos ukaza:"
|
||||
|
||||
#: ../js/ui/runDialog.js:173
|
||||
#: ../js/ui/runDialog.js:351
|
||||
#, c-format
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "Izvajanje '%s' je spodletelo:"
|
||||
@ -114,11 +116,11 @@ msgstr "Programi"
|
||||
msgid "Recent Documents"
|
||||
msgstr "Nedavni dokumenti"
|
||||
|
||||
#: ../src/shell-global.c:821
|
||||
#: ../src/shell-global.c:890
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Pred manj kot eno minuto"
|
||||
|
||||
#: ../src/shell-global.c:824
|
||||
#: ../src/shell-global.c:893
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
@ -127,7 +129,7 @@ msgstr[1] "Pred %d minuto"
|
||||
msgstr[2] "Pred %d minutama"
|
||||
msgstr[3] "Pred %d minutami"
|
||||
|
||||
#: ../src/shell-global.c:827
|
||||
#: ../src/shell-global.c:896
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
@ -136,7 +138,7 @@ msgstr[1] "Pred %d uro"
|
||||
msgstr[2] "Pred %d urama"
|
||||
msgstr[3] "Pred %d urami"
|
||||
|
||||
#: ../src/shell-global.c:830
|
||||
#: ../src/shell-global.c:899
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
@ -145,7 +147,7 @@ msgstr[1] "Pred %d dnevom"
|
||||
msgstr[2] "Pred %d dnevoma"
|
||||
msgstr[3] "Pred %d dnevi"
|
||||
|
||||
#: ../src/shell-global.c:833
|
||||
#: ../src/shell-global.c:902
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
@ -154,80 +156,55 @@ msgstr[1] "Pred %d tednom"
|
||||
msgstr[2] "Pred %d tednoma"
|
||||
msgstr[3] "Pred %d tedni"
|
||||
|
||||
#: ../src/shell-status-menu.c:156
|
||||
msgid "Unknown"
|
||||
msgstr "Neznano"
|
||||
|
||||
#: ../src/shell-status-menu.c:212
|
||||
#, c-format
|
||||
msgid "Can't lock screen: %s"
|
||||
msgstr "Ni mogoče zakleniti zaslona: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:227
|
||||
#, c-format
|
||||
msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||
msgstr "Ni mogoče začasno nastaviti črnega zaslona za ohranjevalnik zaslona: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:351
|
||||
#, c-format
|
||||
msgid "Can't logout: %s"
|
||||
msgstr "Ni se mogoče odjaviti: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:492
|
||||
msgid "Account Information..."
|
||||
msgstr "Podrobnosti računa ..."
|
||||
|
||||
#: ../src/shell-status-menu.c:502
|
||||
msgid "Sidebar"
|
||||
msgstr "Stranska vrstica"
|
||||
|
||||
#: ../src/shell-status-menu.c:510
|
||||
msgid "System Preferences..."
|
||||
msgstr "Sistemske lastnosti ..."
|
||||
|
||||
#: ../src/shell-status-menu.c:525
|
||||
msgid "Lock Screen"
|
||||
msgstr "Zakleni zaslon"
|
||||
|
||||
#: ../src/shell-status-menu.c:535
|
||||
msgid "Switch User"
|
||||
msgstr "Preklop uporabnika"
|
||||
|
||||
#. Only show switch user if there are other users
|
||||
#. Log Out
|
||||
#: ../src/shell-status-menu.c:546
|
||||
msgid "Log Out..."
|
||||
msgstr "Odjava ..."
|
||||
|
||||
#. Shut down
|
||||
#: ../src/shell-status-menu.c:557
|
||||
msgid "Shut Down..."
|
||||
msgstr "Izklopi ..."
|
||||
|
||||
#: ../src/shell-uri-util.c:87
|
||||
#: ../src/shell-uri-util.c:89
|
||||
msgid "Home Folder"
|
||||
msgstr "Domača mapa"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-uri-util.c:102
|
||||
#: ../src/shell-uri-util.c:104
|
||||
msgid "File System"
|
||||
msgstr "Datotečni sistem"
|
||||
|
||||
#: ../src/shell-uri-util.c:248
|
||||
#: ../src/shell-uri-util.c:250
|
||||
msgid "Search"
|
||||
msgstr "Iskanje"
|
||||
msgstr "Poišči"
|
||||
|
||||
#. 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.
|
||||
#.
|
||||
#: ../src/shell-uri-util.c:298
|
||||
#: ../src/shell-uri-util.c:300
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "SEARCH RESULTS"
|
||||
#~ msgstr "Rezultati iskanja"
|
||||
#~ msgid "Unknown"
|
||||
#~ msgstr "Neznano"
|
||||
#~ msgid "Can't lock screen: %s"
|
||||
#~ msgstr "Ni mogoče zakleniti zaslona: %s"
|
||||
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||
#~ msgstr ""
|
||||
#~ "Ni mogoče začasno nastaviti črnega zaslona za ohranjevalnik zaslona: %s"
|
||||
#~ msgid "Can't logout: %s"
|
||||
#~ msgstr "Ni se mogoče odjaviti: %s"
|
||||
#~ msgid "Account Information..."
|
||||
#~ msgstr "Podrobnosti računa ..."
|
||||
#~ msgid "Sidebar"
|
||||
#~ msgstr "Stranska vrstica"
|
||||
#~ msgid "System Preferences..."
|
||||
#~ msgstr "Sistemske možnosti ..."
|
||||
#~ msgid "Lock Screen"
|
||||
#~ msgstr "Zakleni zaslon"
|
||||
#~ msgid "Switch User"
|
||||
#~ msgstr "Preklop uporabnika"
|
||||
#~ msgid "Log Out..."
|
||||
#~ msgstr "Odjava ..."
|
||||
#~ msgid "Shut Down..."
|
||||
#~ msgstr "Izklopi ..."
|
||||
#~ msgid "Frequent"
|
||||
#~ msgstr "Pogosto"
|
||||
#~ msgid "More"
|
||||
|
185
po/sv.po
185
po/sv.po
@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-09-18 13:02+0200\n"
|
||||
"PO-Revision-Date: 2009-09-18 13:02+0100\n"
|
||||
"POT-Creation-Date: 2009-12-01 23:52+0100\n"
|
||||
"PO-Revision-Date: 2009-12-01 23:53+0100\n"
|
||||
"Last-Translator: Daniel Nylander <po@danielnylander.se>\n"
|
||||
"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -24,154 +24,139 @@ msgstr "GNOME-skal"
|
||||
msgid "Window management and application launching"
|
||||
msgstr "Fönsterhantering och programstarter"
|
||||
|
||||
#. left side
|
||||
#: ../js/ui/panel.js:269
|
||||
msgid "Activities"
|
||||
msgstr "Aktiviteter"
|
||||
#: ../js/ui/appDisplay.js:580
|
||||
#: ../js/ui/appIcon.js:425
|
||||
msgid "New Window"
|
||||
msgstr "Nytt fönster"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:452
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %H.%M"
|
||||
#: ../js/ui/appDisplay.js:584
|
||||
#: ../js/ui/appIcon.js:429
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Ta bort från favoriter"
|
||||
|
||||
#: ../js/ui/dash.js:283
|
||||
#: ../js/ui/appDisplay.js:585
|
||||
#: ../js/ui/appIcon.js:430
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Lägg till som favorit"
|
||||
|
||||
#: ../js/ui/appDisplay.js:1029
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "Dra hit för att lägga till favorit"
|
||||
|
||||
#: ../js/ui/dash.js:236
|
||||
msgid "Find..."
|
||||
msgstr "Sök..."
|
||||
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "Browse"
|
||||
msgstr "Bläddra"
|
||||
|
||||
#: ../js/ui/dash.js:536
|
||||
msgid "(see all)"
|
||||
msgstr "(se alla)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:753
|
||||
#: ../js/ui/dash.js:809
|
||||
#: ../js/ui/dash.js:620
|
||||
#: ../js/ui/dash.js:682
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "PROGRAM"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:773
|
||||
#: ../js/ui/dash.js:640
|
||||
#: ../js/ui/dash.js:697
|
||||
msgid "PLACES"
|
||||
msgstr "PLATSER"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:780
|
||||
#: ../js/ui/dash.js:819
|
||||
#: ../js/ui/dash.js:647
|
||||
#: ../js/ui/dash.js:692
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "SENASTE DOKUMENT"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:799
|
||||
#: ../js/ui/dash.js:931
|
||||
#: ../js/ui/dash.js:672
|
||||
#: ../js/ui/dash.js:862
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "SÖKRESULTAT"
|
||||
|
||||
#: ../js/ui/dash.js:814
|
||||
#: ../js/ui/dash.js:687
|
||||
msgid "PREFERENCES"
|
||||
msgstr "INSTÄLLNINGAR"
|
||||
|
||||
#: ../js/ui/runDialog.js:101
|
||||
#. Button on the left side of the panel.
|
||||
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||
#: ../js/ui/panel.js:227
|
||||
msgid "Activities"
|
||||
msgstr "Aktiviteter"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:440
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %H.%M"
|
||||
|
||||
#: ../js/ui/placeDisplay.js:84
|
||||
msgid "Connect to..."
|
||||
msgstr "Anslut till..."
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Ange ett kommando:"
|
||||
|
||||
#: ../src/shell-global.c:799
|
||||
#: ../js/ui/runDialog.js:173
|
||||
#, c-format
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "Körning av \"%s\" misslyckades:"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/widget.js:163
|
||||
msgid "%H:%M"
|
||||
msgstr "%H.%M"
|
||||
|
||||
#: ../js/ui/widget.js:317
|
||||
msgid "Applications"
|
||||
msgstr "Program"
|
||||
|
||||
#: ../js/ui/widget.js:339
|
||||
msgid "Recent Documents"
|
||||
msgstr "Senaste dokument"
|
||||
|
||||
#: ../src/shell-global.c:826
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Mindre än en minut sedan"
|
||||
|
||||
#: ../src/shell-global.c:802
|
||||
#: ../src/shell-global.c:829
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "%d minut sedan"
|
||||
msgstr[1] "%d minuter sedan"
|
||||
|
||||
#: ../src/shell-global.c:805
|
||||
#: ../src/shell-global.c:832
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "%d timme sedan"
|
||||
msgstr[1] "%d timmar sedan"
|
||||
|
||||
#: ../src/shell-global.c:808
|
||||
#: ../src/shell-global.c:835
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "%d dag sedan"
|
||||
msgstr[1] "%d dagar sedan"
|
||||
|
||||
#: ../src/shell-global.c:811
|
||||
#: ../src/shell-global.c:838
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
msgstr[0] "%d vecka sedan"
|
||||
msgstr[1] "%d veckor sedan"
|
||||
|
||||
#: ../src/shell-status-menu.c:156
|
||||
msgid "Unknown"
|
||||
msgstr "Okänt"
|
||||
|
||||
#: ../src/shell-status-menu.c:212
|
||||
#, c-format
|
||||
msgid "Can't lock screen: %s"
|
||||
msgstr "Kan inte låsa skärmen: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:227
|
||||
#, c-format
|
||||
msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||
msgstr "Kan inte temporärt ställa in skärmsläckaren till blank skärm: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:351
|
||||
#, c-format
|
||||
msgid "Can't logout: %s"
|
||||
msgstr "Kan inte logga ut: %s"
|
||||
|
||||
#: ../src/shell-status-menu.c:492
|
||||
msgid "Account Information..."
|
||||
msgstr "Kontoinformation..."
|
||||
|
||||
#: ../src/shell-status-menu.c:502
|
||||
msgid "Sidebar"
|
||||
msgstr "Sidopanel"
|
||||
|
||||
#: ../src/shell-status-menu.c:510
|
||||
msgid "System Preferences..."
|
||||
msgstr "Systeminställningar..."
|
||||
|
||||
#: ../src/shell-status-menu.c:525
|
||||
msgid "Lock Screen"
|
||||
msgstr "Lås skärmen"
|
||||
|
||||
#: ../src/shell-status-menu.c:535
|
||||
msgid "Switch User"
|
||||
msgstr "Växla användare"
|
||||
|
||||
#. Only show switch user if there are other users
|
||||
#. Log Out
|
||||
#: ../src/shell-status-menu.c:546
|
||||
msgid "Log Out..."
|
||||
msgstr "Logga ut..."
|
||||
|
||||
#. Shut down
|
||||
#: ../src/shell-status-menu.c:557
|
||||
msgid "Shut Down..."
|
||||
msgstr "Stäng av..."
|
||||
|
||||
#: ../src/shell-uri-util.c:87
|
||||
#: ../src/shell-uri-util.c:89
|
||||
msgid "Home Folder"
|
||||
msgstr "Hemmapp"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-uri-util.c:102
|
||||
#: ../src/shell-uri-util.c:104
|
||||
msgid "File System"
|
||||
msgstr "Filsystem"
|
||||
|
||||
#: ../src/shell-uri-util.c:248
|
||||
#: ../src/shell-uri-util.c:250
|
||||
msgid "Search"
|
||||
msgstr "Sök"
|
||||
|
||||
@ -180,11 +165,37 @@ msgstr "Sök"
|
||||
#. * example, "Trash: some-directory". It means that the
|
||||
#. * directory called "some-directory" is in the trash.
|
||||
#.
|
||||
#: ../src/shell-uri-util.c:298
|
||||
#: ../src/shell-uri-util.c:300
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "(see all)"
|
||||
#~ msgstr "(se alla)"
|
||||
#~ msgid "Unknown"
|
||||
#~ msgstr "Okänt"
|
||||
#~ msgid "Can't lock screen: %s"
|
||||
#~ msgstr "Kan inte låsa skärmen: %s"
|
||||
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||
#~ msgstr "Kan inte temporärt ställa in skärmsläckaren till blank skärm: %s"
|
||||
#~ msgid "Can't logout: %s"
|
||||
#~ msgstr "Kan inte logga ut: %s"
|
||||
#~ msgid "Account Information..."
|
||||
#~ msgstr "Kontoinformation..."
|
||||
#~ msgid "Sidebar"
|
||||
#~ msgstr "Sidopanel"
|
||||
#~ msgid "System Preferences..."
|
||||
#~ msgstr "Systeminställningar..."
|
||||
#~ msgid "Lock Screen"
|
||||
#~ msgstr "Lås skärmen"
|
||||
#~ msgid "Switch User"
|
||||
#~ msgstr "Växla användare"
|
||||
#~ msgid "Log Out..."
|
||||
#~ msgstr "Logga ut..."
|
||||
#~ msgid "Shut Down..."
|
||||
#~ msgstr "Stäng av..."
|
||||
#~ msgid "Browse"
|
||||
#~ msgstr "Bläddra"
|
||||
#~ msgid "Find apps or documents"
|
||||
#~ msgstr "Hitta program eller dokument"
|
||||
#~ msgid "DOCUMENTS"
|
||||
|
@ -78,6 +78,7 @@ st_source_h = \
|
||||
st/st-entry.h \
|
||||
st/st-im-text.h \
|
||||
st/st-label.h \
|
||||
st/st-overflow-box.h \
|
||||
st/st-private.h \
|
||||
st/st-scrollable.h \
|
||||
st/st-scroll-bar.h \
|
||||
@ -116,6 +117,7 @@ st_source_c = \
|
||||
st/st-entry.c \
|
||||
st/st-im-text.c \
|
||||
st/st-label.c \
|
||||
st/st-overflow-box.c \
|
||||
st/st-private.c \
|
||||
st/st-scrollable.c \
|
||||
st/st-scroll-bar.c \
|
||||
|
@ -63,6 +63,8 @@ libgnome_shell_la_SOURCES = \
|
||||
shell-app-usage.h \
|
||||
shell-arrow.c \
|
||||
shell-arrow.h \
|
||||
shell-doc-system.c \
|
||||
shell-doc-system.h \
|
||||
shell-drawing.c \
|
||||
shell-drawing.h \
|
||||
shell-embedded-window.c \
|
||||
|
87
src/gnome-shell.in
Executable file → Normal file
87
src/gnome-shell.in
Executable file → Normal file
@ -212,6 +212,8 @@ parser.add_option("-w", "--wide", action="store_true",
|
||||
help="Use widescreen (1280x800) with Xephyr")
|
||||
parser.add_option("", "--eval-file", metavar="EVAL_FILE",
|
||||
help="Evaluate the contents of the given JavaScript file")
|
||||
parser.add_option("", "--create-extension", action="store_true",
|
||||
help="Create a new GNOME Shell extension")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
@ -219,6 +221,91 @@ if args:
|
||||
parser.print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
if options.create_extension:
|
||||
import json
|
||||
|
||||
print
|
||||
print '''Name should be a very short (ideally descriptive) string.
|
||||
Examples are: "Click To Focus", "Adblock", "Shell Window Shrinker".
|
||||
'''
|
||||
name = raw_input('Name: ').strip()
|
||||
print
|
||||
print '''Description is a single-sentence explanation of what your extension does.
|
||||
Examples are: "Make windows visible on click", "Block advertisement popups"
|
||||
"Animate windows shrinking on minimize"
|
||||
'''
|
||||
description = raw_input('Description: ').strip()
|
||||
underifier = re.compile('[^A-Za-z]')
|
||||
sample_uuid = underifier.sub('_', name)
|
||||
# TODO use evolution data server
|
||||
hostname = subprocess.Popen(['hostname'], stdout=subprocess.PIPE).communicate()[0].strip()
|
||||
sample_uuid = sample_uuid + '@' + hostname
|
||||
|
||||
print
|
||||
print '''Uuid is a globally-unique identifier for your extension.
|
||||
This should be in the format of an email address (foo.bar@extensions.example.com), but
|
||||
need not be an actual email address, though it's a good idea to base the uuid on your
|
||||
email address. For example, if your email address is janedoe@example.com, you might
|
||||
use an extension title clicktofocus@janedoe.example.com.'''
|
||||
uuid = raw_input('Uuid [%s]: ' % (sample_uuid, )).strip()
|
||||
if uuid == '':
|
||||
uuid = sample_uuid
|
||||
|
||||
extension_path = os.path.join(os.path.expanduser('~/.config'), 'gnome-shell', 'extensions', uuid)
|
||||
if os.path.exists(extension_path):
|
||||
print "Extension path %r already exists" % (extension_path, )
|
||||
sys.exit(0)
|
||||
os.makedirs(extension_path)
|
||||
meta = { 'name': name,
|
||||
'description': description,
|
||||
'uuid': uuid }
|
||||
f = open(os.path.join(extension_path, 'metadata.json'), 'w')
|
||||
try:
|
||||
json.dump(meta, f)
|
||||
except AttributeError:
|
||||
# For Python versions older than 2.6, try using the json-py module
|
||||
f.write(json.write(meta) + '\n')
|
||||
f.close()
|
||||
|
||||
extensionjs_path = os.path.join(extension_path, 'extension.js')
|
||||
f = open(extensionjs_path, 'w')
|
||||
f.write('''// Sample extension code, makes clicking on the panel show a message
|
||||
const St = imports.gi.St;
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
|
||||
function _showHello() {
|
||||
let text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" });
|
||||
let monitor = global.get_primary_monitor();
|
||||
global.stage.add_actor(text);
|
||||
text.set_position(Math.floor (monitor.width / 2 - text.width / 2), Math.floor(monitor.height / 2 - text.height / 2));
|
||||
Mainloop.timeout_add(3000, function () { text.destroy(); });
|
||||
}
|
||||
|
||||
// Put your extension initialization code here
|
||||
function main() {
|
||||
Main.panel.actor.reactive = true;
|
||||
Main.panel.actor.connect('button-release-event', _showHello);
|
||||
}
|
||||
''')
|
||||
f.close()
|
||||
|
||||
f = open(os.path.join(extension_path, 'stylesheet.css'), 'w')
|
||||
f.write('''/* Example stylesheet */
|
||||
.helloworld-label {
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
background-color: rgba(10,10,10,0.7);
|
||||
border-radius: 5px;
|
||||
}
|
||||
''')
|
||||
f.close()
|
||||
|
||||
subprocess.Popen(['gnome-open', extensionjs_path])
|
||||
sys.exit(0)
|
||||
|
||||
if options.eval_file:
|
||||
import dbus
|
||||
|
||||
|
@ -48,9 +48,7 @@ struct _ShellAppSystemPrivate {
|
||||
GHashTable *app_id_to_info;
|
||||
GHashTable *app_id_to_app;
|
||||
|
||||
GHashTable *cached_menu_contents; /* <char *id, GSList<ShellAppInfo*>> */
|
||||
GSList *cached_app_menus; /* ShellAppMenuEntry */
|
||||
|
||||
GSList *cached_flattened_apps; /* ShellAppInfo */
|
||||
GSList *cached_settings; /* ShellAppInfo */
|
||||
|
||||
gint app_monitor_id;
|
||||
@ -58,7 +56,6 @@ struct _ShellAppSystemPrivate {
|
||||
guint app_change_timeout_id;
|
||||
};
|
||||
|
||||
static void free_appinfo_gslist (gpointer list);
|
||||
static void shell_app_system_finalize (GObject *object);
|
||||
static gboolean on_tree_changed (gpointer user_data);
|
||||
static void on_tree_changed_cb (GMenuTree *tree, gpointer user_data);
|
||||
@ -83,6 +80,10 @@ struct _ShellAppInfo {
|
||||
*/
|
||||
guint refcount;
|
||||
|
||||
char *casefolded_name;
|
||||
char *name_collation_key;
|
||||
char *casefolded_description;
|
||||
|
||||
GMenuTreeItem *entry;
|
||||
|
||||
GKeyFile *keyfile;
|
||||
@ -104,6 +105,11 @@ shell_app_info_unref (ShellAppInfo *info)
|
||||
{
|
||||
if (--info->refcount > 0)
|
||||
return;
|
||||
|
||||
g_free (info->casefolded_name);
|
||||
g_free (info->name_collation_key);
|
||||
g_free (info->casefolded_description);
|
||||
|
||||
switch (info->type)
|
||||
{
|
||||
case SHELL_APP_INFO_TYPE_ENTRY:
|
||||
@ -129,7 +135,7 @@ shell_app_info_new_from_tree_item (GMenuTreeItem *item)
|
||||
if (!item)
|
||||
return NULL;
|
||||
|
||||
info = g_slice_alloc (sizeof (ShellAppInfo));
|
||||
info = g_slice_alloc0 (sizeof (ShellAppInfo));
|
||||
info->type = SHELL_APP_INFO_TYPE_ENTRY;
|
||||
info->refcount = 1;
|
||||
info->entry = gmenu_tree_item_ref (item);
|
||||
@ -141,7 +147,7 @@ shell_app_info_new_from_window (MetaWindow *window)
|
||||
{
|
||||
ShellAppInfo *info;
|
||||
|
||||
info = g_slice_alloc (sizeof (ShellAppInfo));
|
||||
info = g_slice_alloc0 (sizeof (ShellAppInfo));
|
||||
info->type = SHELL_APP_INFO_TYPE_WINDOW;
|
||||
info->refcount = 1;
|
||||
info->window = g_object_ref (window);
|
||||
@ -159,7 +165,7 @@ shell_app_info_new_from_keyfile_take_ownership (GKeyFile *keyfile,
|
||||
{
|
||||
ShellAppInfo *info;
|
||||
|
||||
info = g_slice_alloc (sizeof (ShellAppInfo));
|
||||
info = g_slice_alloc0 (sizeof (ShellAppInfo));
|
||||
info->type = SHELL_APP_INFO_TYPE_DESKTOP_FILE;
|
||||
info->refcount = 1;
|
||||
info->keyfile = keyfile;
|
||||
@ -167,29 +173,6 @@ shell_app_info_new_from_keyfile_take_ownership (GKeyFile *keyfile,
|
||||
return info;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
shell_app_menu_entry_copy (gpointer entryp)
|
||||
{
|
||||
ShellAppMenuEntry *entry;
|
||||
ShellAppMenuEntry *copy;
|
||||
entry = entryp;
|
||||
copy = g_new0 (ShellAppMenuEntry, 1);
|
||||
copy->name = g_strdup (entry->name);
|
||||
copy->id = g_strdup (entry->id);
|
||||
copy->icon = g_strdup (entry->icon);
|
||||
return copy;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_app_menu_entry_free (gpointer entryp)
|
||||
{
|
||||
ShellAppMenuEntry *entry = entryp;
|
||||
g_free (entry->name);
|
||||
g_free (entry->id);
|
||||
g_free (entry->icon);
|
||||
g_free (entry);
|
||||
}
|
||||
|
||||
static void shell_app_system_class_init(ShellAppSystemClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *)klass;
|
||||
@ -225,9 +208,6 @@ shell_app_system_init (ShellAppSystem *self)
|
||||
/* Key is owned by info */
|
||||
priv->app_id_to_app = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
priv->cached_menu_contents = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, free_appinfo_gslist);
|
||||
|
||||
/* 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.
|
||||
@ -257,15 +237,12 @@ shell_app_system_finalize (GObject *object)
|
||||
gmenu_tree_unref (priv->apps_tree);
|
||||
gmenu_tree_unref (priv->settings_tree);
|
||||
|
||||
g_hash_table_destroy (priv->cached_menu_contents);
|
||||
|
||||
g_hash_table_destroy (priv->app_id_to_info);
|
||||
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_flattened_apps, (GFunc)shell_app_info_unref, NULL);
|
||||
g_slist_free (priv->cached_flattened_apps);
|
||||
priv->cached_flattened_apps = NULL;
|
||||
g_slist_foreach (priv->cached_settings, (GFunc)shell_app_info_unref, NULL);
|
||||
g_slist_free (priv->cached_settings);
|
||||
priv->cached_settings = NULL;
|
||||
@ -273,60 +250,10 @@ shell_app_system_finalize (GObject *object)
|
||||
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize(object);
|
||||
}
|
||||
|
||||
static void
|
||||
free_appinfo_gslist (gpointer listp)
|
||||
{
|
||||
GSList *list = listp;
|
||||
g_slist_foreach (list, (GFunc) shell_app_info_unref, NULL);
|
||||
g_slist_free (list);
|
||||
}
|
||||
|
||||
static void
|
||||
reread_directories (ShellAppSystem *self, GSList **cache, GMenuTree *tree)
|
||||
{
|
||||
GMenuTreeDirectory *trunk;
|
||||
GSList *entries;
|
||||
GSList *iter;
|
||||
|
||||
trunk = gmenu_tree_get_root_directory (tree);
|
||||
entries = gmenu_tree_directory_get_contents (trunk);
|
||||
|
||||
g_slist_foreach (*cache, (GFunc)shell_app_menu_entry_free, NULL);
|
||||
g_slist_free (*cache);
|
||||
*cache = NULL;
|
||||
|
||||
for (iter = entries; iter; iter = iter->next)
|
||||
{
|
||||
GMenuTreeItem *item = iter->data;
|
||||
|
||||
switch (gmenu_tree_item_get_type (item))
|
||||
{
|
||||
case GMENU_TREE_ITEM_DIRECTORY:
|
||||
{
|
||||
GMenuTreeDirectory *dir = iter->data;
|
||||
ShellAppMenuEntry *shell_entry = g_new0 (ShellAppMenuEntry, 1);
|
||||
shell_entry->name = g_strdup (gmenu_tree_directory_get_name (dir));
|
||||
shell_entry->id = g_strdup (gmenu_tree_directory_get_menu_id (dir));
|
||||
shell_entry->icon = g_strdup (gmenu_tree_directory_get_icon (dir));
|
||||
|
||||
*cache = g_slist_prepend (*cache, shell_entry);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
gmenu_tree_item_unref (item);
|
||||
}
|
||||
*cache = g_slist_reverse (*cache);
|
||||
|
||||
g_slist_free (entries);
|
||||
gmenu_tree_item_unref (trunk);
|
||||
}
|
||||
|
||||
static GSList *
|
||||
gather_entries_recurse (ShellAppSystem *monitor,
|
||||
GSList *apps,
|
||||
GHashTable *unique,
|
||||
GMenuTreeDirectory *root)
|
||||
{
|
||||
GSList *contents;
|
||||
@ -342,13 +269,17 @@ 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);
|
||||
if (!g_hash_table_lookup (unique, shell_app_info_get_id (app)))
|
||||
{
|
||||
apps = g_slist_prepend (apps, app);
|
||||
g_hash_table_insert (unique, (char*)shell_app_info_get_id (app), app);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GMENU_TREE_ITEM_DIRECTORY:
|
||||
{
|
||||
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
|
||||
apps = gather_entries_recurse (monitor, apps, dir);
|
||||
apps = gather_entries_recurse (monitor, apps, unique, dir);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -365,6 +296,7 @@ gather_entries_recurse (ShellAppSystem *monitor,
|
||||
static void
|
||||
reread_entries (ShellAppSystem *self,
|
||||
GSList **cache,
|
||||
GHashTable *unique,
|
||||
GMenuTree *tree)
|
||||
{
|
||||
GMenuTreeDirectory *trunk;
|
||||
@ -375,46 +307,40 @@ reread_entries (ShellAppSystem *self,
|
||||
g_slist_free (*cache);
|
||||
*cache = NULL;
|
||||
|
||||
*cache = gather_entries_recurse (self, *cache, trunk);
|
||||
*cache = gather_entries_recurse (self, *cache, unique, trunk);
|
||||
|
||||
gmenu_tree_item_unref (trunk);
|
||||
}
|
||||
|
||||
static void
|
||||
cache_by_id (ShellAppSystem *self, GSList *apps, gboolean ref)
|
||||
cache_by_id (ShellAppSystem *self, GSList *apps)
|
||||
{
|
||||
GSList *iter;
|
||||
|
||||
for (iter = apps; iter; iter = iter->next)
|
||||
{
|
||||
ShellAppInfo *info = iter->data;
|
||||
if (ref)
|
||||
shell_app_info_ref (info);
|
||||
shell_app_info_ref (info);
|
||||
/* the name is owned by the info itself */
|
||||
g_hash_table_insert (self->priv->app_id_to_info, (char*)shell_app_info_get_id (info),
|
||||
info);
|
||||
g_hash_table_replace (self->priv->app_id_to_info, (char*)shell_app_info_get_id (info),
|
||||
info);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reread_menus (ShellAppSystem *self)
|
||||
{
|
||||
GSList *apps;
|
||||
GMenuTreeDirectory *trunk;
|
||||
GHashTable *unique = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
reread_directories (self, &(self->priv->cached_app_menus), self->priv->apps_tree);
|
||||
reread_entries (self, &(self->priv->cached_flattened_apps), unique, self->priv->apps_tree);
|
||||
g_hash_table_remove_all (unique);
|
||||
reread_entries (self, &(self->priv->cached_settings), unique, self->priv->settings_tree);
|
||||
g_hash_table_destroy (unique);
|
||||
|
||||
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_info);
|
||||
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);
|
||||
|
||||
cache_by_id (self, self->priv->cached_flattened_apps);
|
||||
cache_by_id (self, self->priv->cached_settings);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -423,7 +349,6 @@ on_tree_changed (gpointer user_data)
|
||||
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
|
||||
|
||||
reread_menus (self);
|
||||
g_hash_table_remove_all (self->priv->cached_menu_contents);
|
||||
|
||||
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
|
||||
|
||||
@ -469,21 +394,8 @@ shell_app_info_get_type (void)
|
||||
return gtype;
|
||||
}
|
||||
|
||||
GType
|
||||
shell_app_menu_entry_get_type (void)
|
||||
{
|
||||
static GType gtype = G_TYPE_INVALID;
|
||||
if (gtype == G_TYPE_INVALID)
|
||||
{
|
||||
gtype = g_boxed_type_register_static ("ShellAppMenuEntry",
|
||||
shell_app_menu_entry_copy,
|
||||
shell_app_menu_entry_free);
|
||||
}
|
||||
return gtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_system_get_applications_for_menu:
|
||||
* shell_app_system_get_flattened_apps:
|
||||
*
|
||||
* Traverses a toplevel menu, and returns all items under it. Nested items
|
||||
* are flattened. This value is computed on initial call and cached thereafter
|
||||
@ -492,41 +404,9 @@ shell_app_menu_entry_get_type (void)
|
||||
* Return value: (transfer none) (element-type ShellAppInfo): List of applications
|
||||
*/
|
||||
GSList *
|
||||
shell_app_system_get_applications_for_menu (ShellAppSystem *self,
|
||||
const char *menu)
|
||||
shell_app_system_get_flattened_apps (ShellAppSystem *self)
|
||||
{
|
||||
GSList *apps;
|
||||
|
||||
apps = g_hash_table_lookup (self->priv->cached_menu_contents, menu);
|
||||
if (!apps)
|
||||
{
|
||||
char *path;
|
||||
GMenuTreeDirectory *menu_entry;
|
||||
path = g_strdup_printf ("/%s", menu);
|
||||
menu_entry = gmenu_tree_get_directory_from_path (self->priv->apps_tree, path);
|
||||
g_free (path);
|
||||
g_assert (menu_entry != NULL);
|
||||
|
||||
apps = gather_entries_recurse (self, NULL, menu_entry);
|
||||
g_hash_table_insert (self->priv->cached_menu_contents, g_strdup (menu), apps);
|
||||
|
||||
gmenu_tree_item_unref (menu_entry);
|
||||
}
|
||||
|
||||
return apps;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_system_get_menus:
|
||||
*
|
||||
* Returns a list of toplevel #ShellAppMenuEntry items
|
||||
*
|
||||
* Return value: (transfer none) (element-type AppMenuEntry): List of toplevel menus
|
||||
*/
|
||||
GSList *
|
||||
shell_app_system_get_menus (ShellAppSystem *monitor)
|
||||
{
|
||||
return monitor->priv->cached_app_menus;
|
||||
return self->priv->cached_flattened_apps;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -711,6 +591,253 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
MATCH_NONE,
|
||||
MATCH_MULTIPLE, /* Matches multiple terms */
|
||||
MATCH_PREFIX, /* Strict prefix */
|
||||
MATCH_SUBSTRING /* Not prefix, substring */
|
||||
} ShellAppInfoSearchMatch;
|
||||
|
||||
static char *
|
||||
normalize_and_casefold (const char *str)
|
||||
{
|
||||
char *normalized, *result;
|
||||
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
|
||||
result = g_utf8_casefold (normalized, -1);
|
||||
g_free (normalized);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_app_info_init_search_data (ShellAppInfo *info)
|
||||
{
|
||||
const char *name;
|
||||
const char *comment;
|
||||
|
||||
g_assert (info->type == SHELL_APP_INFO_TYPE_ENTRY);
|
||||
|
||||
name = gmenu_tree_entry_get_name ((GMenuTreeEntry*)info->entry);
|
||||
info->casefolded_name = normalize_and_casefold (name);
|
||||
|
||||
comment = gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info->entry);
|
||||
info->casefolded_description = normalize_and_casefold (comment);
|
||||
}
|
||||
|
||||
static ShellAppInfoSearchMatch
|
||||
shell_app_info_match_terms (ShellAppInfo *info,
|
||||
GSList *terms)
|
||||
{
|
||||
GSList *iter;
|
||||
ShellAppInfoSearchMatch match;
|
||||
|
||||
if (G_UNLIKELY(!info->casefolded_name))
|
||||
shell_app_info_init_search_data (info);
|
||||
|
||||
match = MATCH_NONE;
|
||||
for (iter = terms; iter; iter = iter->next)
|
||||
{
|
||||
const char *term = iter->data;
|
||||
const char *p;
|
||||
|
||||
p = strstr (info->casefolded_name, term);
|
||||
if (p == info->casefolded_name)
|
||||
{
|
||||
if (match != MATCH_NONE)
|
||||
return MATCH_MULTIPLE;
|
||||
else
|
||||
match = MATCH_PREFIX;
|
||||
}
|
||||
else if (p != NULL)
|
||||
match = MATCH_SUBSTRING;
|
||||
|
||||
if (!info->casefolded_description)
|
||||
continue;
|
||||
p = strstr (info->casefolded_description, term);
|
||||
if (p != NULL)
|
||||
match = MATCH_SUBSTRING;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
static gint
|
||||
shell_app_info_compare (gconstpointer a,
|
||||
gconstpointer b,
|
||||
gpointer data)
|
||||
{
|
||||
ShellAppSystem *system = data;
|
||||
const char *id_a = a;
|
||||
const char *id_b = b;
|
||||
ShellAppInfo *info_a = g_hash_table_lookup (system->priv->app_id_to_info, id_a);
|
||||
ShellAppInfo *info_b = g_hash_table_lookup (system->priv->app_id_to_info, id_b);
|
||||
|
||||
if (!info_a->name_collation_key)
|
||||
info_a->name_collation_key = g_utf8_collate_key (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info_a->entry), -1);
|
||||
if (!info_b->name_collation_key)
|
||||
info_b->name_collation_key = g_utf8_collate_key (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info_b->entry), -1);
|
||||
|
||||
return strcmp (info_a->name_collation_key, info_b->name_collation_key);
|
||||
}
|
||||
|
||||
static GSList *
|
||||
sort_and_concat_results (ShellAppSystem *system,
|
||||
GSList *multiple_matches,
|
||||
GSList *prefix_matches,
|
||||
GSList *substring_matches)
|
||||
{
|
||||
multiple_matches = g_slist_sort_with_data (multiple_matches, shell_app_info_compare, system);
|
||||
prefix_matches = g_slist_sort_with_data (prefix_matches, shell_app_info_compare, system);
|
||||
substring_matches = g_slist_sort_with_data (substring_matches, shell_app_info_compare, system);
|
||||
return g_slist_concat (multiple_matches, g_slist_concat (prefix_matches, substring_matches));
|
||||
}
|
||||
|
||||
/**
|
||||
* normalize_terms:
|
||||
* @terms: (element-type utf8): Input search terms
|
||||
*
|
||||
* Returns: (element-type utf8) (transfer full): Unicode-normalized and lowercased terms
|
||||
*/
|
||||
static GSList *
|
||||
normalize_terms (GSList *terms)
|
||||
{
|
||||
GSList *normalized_terms = NULL;
|
||||
GSList *iter;
|
||||
for (iter = terms; iter; iter = iter->next)
|
||||
{
|
||||
const char *term = iter->data;
|
||||
normalized_terms = g_slist_prepend (normalized_terms, normalize_and_casefold (term));
|
||||
}
|
||||
return normalized_terms;
|
||||
}
|
||||
|
||||
static inline void
|
||||
shell_app_system_do_match (ShellAppSystem *system,
|
||||
ShellAppInfo *info,
|
||||
GSList *terms,
|
||||
GSList **multiple_results,
|
||||
GSList **prefix_results,
|
||||
GSList **substring_results)
|
||||
{
|
||||
const char *id = shell_app_info_get_id (info);
|
||||
ShellAppInfoSearchMatch match;
|
||||
|
||||
if (shell_app_info_get_is_nodisplay (info))
|
||||
return;
|
||||
|
||||
match = shell_app_info_match_terms (info, terms);
|
||||
switch (match)
|
||||
{
|
||||
case MATCH_NONE:
|
||||
break;
|
||||
case MATCH_MULTIPLE:
|
||||
*multiple_results = g_slist_prepend (*multiple_results, (char *) id);
|
||||
break;
|
||||
case MATCH_PREFIX:
|
||||
*prefix_results = g_slist_prepend (*prefix_results, (char *) id);
|
||||
break;
|
||||
case MATCH_SUBSTRING:
|
||||
*substring_results = g_slist_prepend (*substring_results, (char *) id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GSList *
|
||||
shell_app_system_initial_search_internal (ShellAppSystem *self,
|
||||
GSList *terms,
|
||||
GSList *source)
|
||||
{
|
||||
GSList *multiple_results = NULL;
|
||||
GSList *prefix_results = NULL;
|
||||
GSList *substring_results = NULL;
|
||||
GSList *iter;
|
||||
GSList *normalized_terms = normalize_terms (terms);
|
||||
|
||||
for (iter = source; iter; iter = iter->next)
|
||||
{
|
||||
ShellAppInfo *info = iter->data;
|
||||
|
||||
shell_app_system_do_match (self, info, normalized_terms, &multiple_results, &prefix_results, &substring_results);
|
||||
}
|
||||
g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
|
||||
g_slist_free (normalized_terms);
|
||||
|
||||
return sort_and_concat_results (self, multiple_results, prefix_results, substring_results);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_system_initial_search:
|
||||
* @self: A #ShellAppSystem
|
||||
* @prefs: %TRUE iff we should search preferences instead of apps
|
||||
* @terms: (element-type utf8): List of terms, logical OR
|
||||
*
|
||||
* Search through applications for the given search terms. Note that returned
|
||||
* strings are only valid until a return to the main loop.
|
||||
*
|
||||
* Returns: (transfer container) (element-type utf8): List of application identifiers
|
||||
*/
|
||||
GSList *
|
||||
shell_app_system_initial_search (ShellAppSystem *self,
|
||||
gboolean prefs,
|
||||
GSList *terms)
|
||||
{
|
||||
return shell_app_system_initial_search_internal (self, terms,
|
||||
prefs ? self->priv->cached_settings : self->priv->cached_flattened_apps);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_system_subsearch:
|
||||
* @self: A #ShellAppSystem
|
||||
* @prefs: %TRUE iff we should search preferences instead of apps
|
||||
* @previous_results: (element-type utf8): List of previous results
|
||||
* @terms: (element-type utf8): List of terms, logical OR
|
||||
*
|
||||
* Search through a previous result set; for more information, see
|
||||
* js/ui/search.js. Note the value of @prefs must be
|
||||
* the same as passed to shell_app_system_initial_search(). Note that returned
|
||||
* strings are only valid until a return to the main loop.
|
||||
*
|
||||
* Returns: (transfer container) (element-type utf8): List of application identifiers
|
||||
*/
|
||||
GSList *
|
||||
shell_app_system_subsearch (ShellAppSystem *system,
|
||||
gboolean prefs,
|
||||
GSList *previous_results,
|
||||
GSList *terms)
|
||||
{
|
||||
GSList *iter;
|
||||
GSList *multiple_results = NULL;
|
||||
GSList *prefix_results = NULL;
|
||||
GSList *substring_results = NULL;
|
||||
GSList *normalized_terms = normalize_terms (terms);
|
||||
|
||||
/* Note prefs is deliberately ignored; both apps and prefs are in app_id_to_app,
|
||||
* but we have the parameter for consistency and in case in the future
|
||||
* they're not in the same data structure.
|
||||
*/
|
||||
|
||||
for (iter = previous_results; iter; iter = iter->next)
|
||||
{
|
||||
const char *id = iter->data;
|
||||
ShellAppInfo *info;
|
||||
|
||||
info = g_hash_table_lookup (system->priv->app_id_to_info, id);
|
||||
if (!info)
|
||||
continue;
|
||||
|
||||
shell_app_system_do_match (system, info, normalized_terms, &multiple_results, &prefix_results, &substring_results);
|
||||
}
|
||||
g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
|
||||
g_slist_free (normalized_terms);
|
||||
|
||||
/* Note that a shorter term might have matched as a prefix, but
|
||||
when extended only as a substring, so we have to redo the
|
||||
sort rather than reusing the existing ordering */
|
||||
return sort_and_concat_results (system, multiple_results, prefix_results, substring_results);
|
||||
}
|
||||
|
||||
const char *
|
||||
shell_app_info_get_id (ShellAppInfo *info)
|
||||
{
|
||||
|
@ -37,18 +37,6 @@ struct _ShellAppSystemClass
|
||||
GType shell_app_system_get_type (void) G_GNUC_CONST;
|
||||
ShellAppSystem* shell_app_system_get_default(void);
|
||||
|
||||
GSList *shell_app_system_get_applications_for_menu (ShellAppSystem *system, const char *menu);
|
||||
|
||||
typedef struct _ShellAppMenuEntry ShellAppMenuEntry;
|
||||
|
||||
struct _ShellAppMenuEntry {
|
||||
char *name;
|
||||
char *id;
|
||||
char *icon;
|
||||
};
|
||||
|
||||
GType shell_app_menu_entry_get_type (void);
|
||||
|
||||
typedef struct _ShellAppInfo ShellAppInfo;
|
||||
|
||||
#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ())
|
||||
@ -85,8 +73,17 @@ ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, co
|
||||
|
||||
ShellAppInfo *shell_app_system_create_from_window (ShellAppSystem *system, MetaWindow *window);
|
||||
|
||||
GSList *shell_app_system_get_menus (ShellAppSystem *system);
|
||||
GSList *shell_app_system_get_flattened_apps (ShellAppSystem *system);
|
||||
|
||||
GSList *shell_app_system_get_all_settings (ShellAppSystem *system);
|
||||
|
||||
GSList *shell_app_system_initial_search (ShellAppSystem *system,
|
||||
gboolean prefs,
|
||||
GSList *terms);
|
||||
|
||||
GSList *shell_app_system_subsearch (ShellAppSystem *system,
|
||||
gboolean prefs,
|
||||
GSList *previous_results,
|
||||
GSList *terms);
|
||||
|
||||
#endif /* __SHELL_APP_SYSTEM_H__ */
|
||||
|
367
src/shell-doc-system.c
Normal file
367
src/shell-doc-system.c
Normal file
@ -0,0 +1,367 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "shell-doc-system.h"
|
||||
|
||||
#include "shell-global.h"
|
||||
#include "shell-texture-cache.h"
|
||||
|
||||
|
||||
/**
|
||||
* SECTION:shell-doc-system
|
||||
* @short_description: Track recently used documents
|
||||
*
|
||||
* Wraps #GtkRecentManager, caching recently used document information, and adds
|
||||
* APIs for asynchronous queries.
|
||||
*/
|
||||
enum {
|
||||
CHANGED,
|
||||
DELETED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
struct _ShellDocSystemPrivate {
|
||||
GtkRecentManager *manager;
|
||||
GHashTable *infos_by_uri;
|
||||
GSList *infos_by_timestamp;
|
||||
|
||||
guint idle_recent_changed_id;
|
||||
|
||||
GHashTable *deleted_infos;
|
||||
guint idle_emit_deleted_id;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(ShellDocSystem, shell_doc_system, G_TYPE_OBJECT);
|
||||
|
||||
/**
|
||||
* shell_doc_system_get_all:
|
||||
* @self: A #ShellDocSystem
|
||||
*
|
||||
* Returns the currently cached set of recent files. Recent files are read initially
|
||||
* from the underlying #GtkRecentManager, and updated when it changes.
|
||||
* This function does not perform I/O.
|
||||
*
|
||||
* Returns: (transfer none) (element-type GtkRecentInfo): Cached recent file infos
|
||||
*/
|
||||
GSList *
|
||||
shell_doc_system_get_all (ShellDocSystem *self)
|
||||
{
|
||||
return self->priv->infos_by_timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @self: A #ShellDocSystem
|
||||
* @uri: Url
|
||||
*
|
||||
* Returns: (transfer none): Recent file info corresponding to given @uri
|
||||
*/
|
||||
GtkRecentInfo *
|
||||
shell_doc_system_lookup_by_uri (ShellDocSystem *self,
|
||||
const char *uri)
|
||||
{
|
||||
return g_hash_table_lookup (self->priv->infos_by_uri, uri);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
shell_doc_system_idle_emit_deleted (gpointer data)
|
||||
{
|
||||
ShellDocSystem *self = SHELL_DOC_SYSTEM (data);
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
|
||||
self->priv->idle_emit_deleted_id = 0;
|
||||
|
||||
g_hash_table_iter_init (&iter, self->priv->deleted_infos);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
{
|
||||
GtkRecentInfo *info = key;
|
||||
g_signal_emit (self, signals[DELETED], 0, info);
|
||||
}
|
||||
|
||||
g_signal_emit (self, signals[CHANGED], 0);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ShellDocSystem *self;
|
||||
GtkRecentInfo *info;
|
||||
} ShellDocSystemRecentQueryData;
|
||||
|
||||
static void
|
||||
on_recent_file_query_result (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellDocSystemRecentQueryData *data = user_data;
|
||||
ShellDocSystem *self = data->self;
|
||||
GError *error = NULL;
|
||||
GFileInfo *fileinfo;
|
||||
|
||||
fileinfo = g_file_query_info_finish (G_FILE (source), result, &error);
|
||||
if (fileinfo)
|
||||
g_object_unref (fileinfo);
|
||||
/* This is a strict error check; we don't want to cause recent files to
|
||||
* vanish for anything potentially transient.
|
||||
*/
|
||||
if (error != NULL && error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND)
|
||||
{
|
||||
self->priv->infos_by_timestamp = g_slist_remove (self->priv->infos_by_timestamp, data->info);
|
||||
g_hash_table_remove (self->priv->infos_by_uri, gtk_recent_info_get_uri (data->info));
|
||||
|
||||
g_hash_table_insert (self->priv->deleted_infos, gtk_recent_info_ref (data->info), NULL);
|
||||
|
||||
if (self->priv->idle_emit_deleted_id == 0)
|
||||
self->priv->idle_emit_deleted_id = g_timeout_add (0, shell_doc_system_idle_emit_deleted, self);
|
||||
}
|
||||
g_clear_error (&error);
|
||||
|
||||
gtk_recent_info_unref (data->info);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_doc_system_queue_existence_check:
|
||||
* @self: A #ShellDocSystem
|
||||
* @n_items: Count of items to check for existence, starting from most recent
|
||||
*
|
||||
* Asynchronously start a check of a number of recent file for existence;
|
||||
* any deleted files will be emitted from the #ShellDocSystem::deleted
|
||||
* signal. Note that this function ignores non-local files; they
|
||||
* will simply always appear to exist (until they are removed from
|
||||
* the recent file list manually).
|
||||
*
|
||||
* The intent of this function is to be called after a #ShellDocSystem::changed
|
||||
* signal has been emitted, and a display has shown a subset of those files.
|
||||
*/
|
||||
void
|
||||
shell_doc_system_queue_existence_check (ShellDocSystem *self,
|
||||
guint n_items)
|
||||
{
|
||||
GSList *iter;
|
||||
guint i;
|
||||
|
||||
for (i = 0, iter = self->priv->infos_by_timestamp; i < n_items && iter; i++, iter = iter->next)
|
||||
{
|
||||
GtkRecentInfo *info = iter->data;
|
||||
const char *uri;
|
||||
GFile *file;
|
||||
ShellDocSystemRecentQueryData *data;
|
||||
|
||||
if (!gtk_recent_info_is_local (info))
|
||||
continue;
|
||||
|
||||
data = g_new0 (ShellDocSystemRecentQueryData, 1);
|
||||
data->self = self;
|
||||
data->info = gtk_recent_info_ref (info);
|
||||
|
||||
uri = gtk_recent_info_get_uri (info);
|
||||
file = g_file_new_for_uri (uri);
|
||||
|
||||
g_file_query_info_async (file, "standard::type", G_FILE_QUERY_INFO_NONE,
|
||||
G_PRIORITY_DEFAULT, NULL, on_recent_file_query_result, data);
|
||||
g_object_unref (file);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sort_infos_by_timestamp_descending (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
GtkRecentInfo *info_a = (GtkRecentInfo*)a;
|
||||
GtkRecentInfo *info_b = (GtkRecentInfo*)b;
|
||||
time_t modified_a, modified_b;
|
||||
|
||||
modified_a = gtk_recent_info_get_modified (info_a);
|
||||
modified_b = gtk_recent_info_get_modified (info_b);
|
||||
|
||||
return modified_b - modified_a;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
idle_handle_recent_changed (gpointer data)
|
||||
{
|
||||
ShellDocSystem *self = SHELL_DOC_SYSTEM (data);
|
||||
GList *items, *iter;
|
||||
|
||||
self->priv->idle_recent_changed_id = 0;
|
||||
|
||||
g_hash_table_remove_all (self->priv->deleted_infos);
|
||||
g_hash_table_remove_all (self->priv->infos_by_uri);
|
||||
g_slist_free (self->priv->infos_by_timestamp);
|
||||
self->priv->infos_by_timestamp = NULL;
|
||||
|
||||
items = gtk_recent_manager_get_items (self->priv->manager);
|
||||
for (iter = items; iter; iter = iter->next)
|
||||
{
|
||||
GtkRecentInfo *info = iter->data;
|
||||
const char *uri = gtk_recent_info_get_uri (info);
|
||||
|
||||
/* uri is owned by the info */
|
||||
g_hash_table_insert (self->priv->infos_by_uri, (char*) uri, info);
|
||||
|
||||
self->priv->infos_by_timestamp = g_slist_prepend (self->priv->infos_by_timestamp, info);
|
||||
}
|
||||
g_list_free (items);
|
||||
|
||||
self->priv->infos_by_timestamp = g_slist_sort (self->priv->infos_by_timestamp, sort_infos_by_timestamp_descending);
|
||||
|
||||
g_signal_emit (self, signals[CHANGED], 0);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_doc_system_on_recent_changed (GtkRecentManager *manager,
|
||||
ShellDocSystem *self)
|
||||
{
|
||||
if (self->priv->idle_recent_changed_id != 0)
|
||||
return;
|
||||
self->priv->idle_recent_changed_id = g_timeout_add (0, idle_handle_recent_changed, self);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_doc_system_open:
|
||||
* @system: A #ShellDocSystem
|
||||
* @info: A #GtkRecentInfo
|
||||
*
|
||||
* Launch the default application associated with the mime type of
|
||||
* @info, using its uri.
|
||||
*/
|
||||
void
|
||||
shell_doc_system_open (ShellDocSystem *system,
|
||||
GtkRecentInfo *info)
|
||||
{
|
||||
GFile *file;
|
||||
GAppInfo *app_info;
|
||||
gboolean needs_uri;
|
||||
|
||||
file = g_file_new_for_uri (gtk_recent_info_get_uri (info));
|
||||
needs_uri = g_file_get_path (file) == NULL;
|
||||
g_object_unref (file);
|
||||
|
||||
app_info = g_app_info_get_default_for_type (gtk_recent_info_get_mime_type (info), needs_uri);
|
||||
if (app_info != NULL)
|
||||
{
|
||||
GList *uris;
|
||||
uris = g_list_prepend (NULL, (gpointer)gtk_recent_info_get_uri (info));
|
||||
g_app_info_launch_uris (app_info, uris, shell_global_create_app_launch_context (shell_global_get ()), NULL);
|
||||
g_list_free (uris);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *app_name;
|
||||
#if GTK_MINOR_VERSION >= 18
|
||||
const char *app_exec;
|
||||
#else
|
||||
char *app_exec;
|
||||
#endif
|
||||
char *app_exec_quoted;
|
||||
guint count;
|
||||
time_t time;
|
||||
|
||||
app_name = gtk_recent_info_last_application (info);
|
||||
if (gtk_recent_info_get_application_info (info, app_name, &app_exec, &count, &time))
|
||||
{
|
||||
GRegex *regex;
|
||||
GAppLaunchContext *context;
|
||||
|
||||
/* TODO: Change this once better support for creating
|
||||
GAppInfo is added to GtkRecentInfo, as right now
|
||||
this relies on the fact that the file uri is
|
||||
already a part of appExec, so we don't supply any
|
||||
files to app_info.launch().
|
||||
|
||||
The 'command line' passed to
|
||||
create_from_command_line is allowed to contain
|
||||
'%<something>' macros that are expanded to file
|
||||
name / icon name, etc, so we need to escape % as %%
|
||||
*/
|
||||
|
||||
regex = g_regex_new ("%", 0, 0, NULL);
|
||||
app_exec_quoted = g_regex_replace (regex, app_exec, -1, 0, "%%", 0, NULL);
|
||||
g_regex_unref (regex);
|
||||
|
||||
app_info = g_app_info_create_from_commandline (app_exec, NULL, 0, NULL);
|
||||
|
||||
/* The point of passing an app launch context to
|
||||
launch() is mostly to get startup notification and
|
||||
associated benefits like the app appearing on the
|
||||
right desktop; but it doesn't really work for now
|
||||
because with the way we create the appInfo we
|
||||
aren't reading the application's desktop file, and
|
||||
thus don't find the StartupNotify=true in it. So,
|
||||
despite passing the app launch context, no startup
|
||||
notification occurs.
|
||||
*/
|
||||
context = shell_global_create_app_launch_context (shell_global_get ());
|
||||
g_app_info_launch (app_info, NULL, context, NULL);
|
||||
g_object_unref (context);
|
||||
}
|
||||
|
||||
g_free (app_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
shell_doc_system_class_init(ShellDocSystemClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *)klass;
|
||||
|
||||
signals[CHANGED] =
|
||||
g_signal_new ("changed",
|
||||
SHELL_TYPE_DOC_SYSTEM,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
signals[DELETED] =
|
||||
g_signal_new ("deleted",
|
||||
SHELL_TYPE_DOC_SYSTEM,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__BOXED,
|
||||
G_TYPE_NONE, 1, GTK_TYPE_RECENT_INFO);
|
||||
|
||||
g_type_class_add_private (gobject_class, sizeof (ShellDocSystemPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
shell_doc_system_init (ShellDocSystem *self)
|
||||
{
|
||||
ShellDocSystemPrivate *priv;
|
||||
|
||||
self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
||||
SHELL_TYPE_DOC_SYSTEM,
|
||||
ShellDocSystemPrivate);
|
||||
self->priv->manager = gtk_recent_manager_get_default ();
|
||||
|
||||
self->priv->deleted_infos = g_hash_table_new_full (NULL, NULL, (GDestroyNotify)gtk_recent_info_unref, NULL);
|
||||
self->priv->infos_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)gtk_recent_info_unref);
|
||||
|
||||
g_signal_connect (self->priv->manager, "changed", G_CALLBACK(shell_doc_system_on_recent_changed), self);
|
||||
shell_doc_system_on_recent_changed (self->priv->manager, self);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_doc_system_get_default:
|
||||
*
|
||||
* Return Value: (transfer none): The global #ShellDocSystem singleton
|
||||
*/
|
||||
ShellDocSystem *
|
||||
shell_doc_system_get_default ()
|
||||
{
|
||||
static ShellDocSystem *instance = NULL;
|
||||
|
||||
if (instance == NULL)
|
||||
instance = g_object_new (SHELL_TYPE_DOC_SYSTEM, NULL);
|
||||
|
||||
return instance;
|
||||
}
|
46
src/shell-doc-system.h
Normal file
46
src/shell-doc-system.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __SHELL_DOC_SYSTEM_H__
|
||||
#define __SHELL_DOC_SYSTEM_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define SHELL_TYPE_DOC_SYSTEM (shell_doc_system_get_type ())
|
||||
#define SHELL_DOC_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_DOC_SYSTEM, ShellDocSystem))
|
||||
#define SHELL_DOC_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_DOC_SYSTEM, ShellDocSystemClass))
|
||||
#define SHELL_IS_DOC_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_DOC_SYSTEM))
|
||||
#define SHELL_IS_DOC_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_DOC_SYSTEM))
|
||||
#define SHELL_DOC_SYSTEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_DOC_SYSTEM, ShellDocSystemClass))
|
||||
|
||||
typedef struct _ShellDocSystem ShellDocSystem;
|
||||
typedef struct _ShellDocSystemClass ShellDocSystemClass;
|
||||
typedef struct _ShellDocSystemPrivate ShellDocSystemPrivate;
|
||||
|
||||
struct _ShellDocSystem
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
ShellDocSystemPrivate *priv;
|
||||
};
|
||||
|
||||
struct _ShellDocSystemClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType shell_doc_system_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ShellDocSystem* shell_doc_system_get_default (void);
|
||||
|
||||
GSList *shell_doc_system_get_all (ShellDocSystem *system);
|
||||
|
||||
GtkRecentInfo *shell_doc_system_lookup_by_uri (ShellDocSystem *system,
|
||||
const char *uri);
|
||||
|
||||
void shell_doc_system_queue_existence_check (ShellDocSystem *system,
|
||||
guint n_recent);
|
||||
|
||||
void shell_doc_system_open (ShellDocSystem *system,
|
||||
GtkRecentInfo *info);
|
||||
|
||||
#endif /* __SHELL_DOC_SYSTEM_H__ */
|
@ -105,7 +105,7 @@ function runTestFixedBox() {
|
||||
G_DEFINE_TYPE(ShellGenericContainer, shell_generic_container, CLUTTER_TYPE_GROUP);
|
||||
|
||||
struct _ShellGenericContainerPrivate {
|
||||
gpointer dummy;
|
||||
GHashTable *skip_paint;
|
||||
};
|
||||
|
||||
/* Signals */
|
||||
@ -182,15 +182,119 @@ shell_generic_container_get_preferred_height (ClutterActor *actor,
|
||||
shell_generic_container_allocation_unref (alloc);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_paint (ClutterActor *actor)
|
||||
{
|
||||
ShellGenericContainer *self = (ShellGenericContainer*) actor;
|
||||
GList *iter, *children;
|
||||
|
||||
children = clutter_container_get_children ((ClutterContainer*) actor);
|
||||
|
||||
for (iter = children; iter; iter = iter->next)
|
||||
{
|
||||
ClutterActor *child = iter->data;
|
||||
|
||||
if (g_hash_table_lookup (self->priv->skip_paint, child))
|
||||
continue;
|
||||
|
||||
clutter_actor_paint (child);
|
||||
}
|
||||
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_pick (ClutterActor *actor,
|
||||
const ClutterColor *color)
|
||||
{
|
||||
ShellGenericContainer *self = (ShellGenericContainer*) actor;
|
||||
GList *iter, *children;
|
||||
|
||||
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->pick (actor, color);
|
||||
|
||||
children = clutter_container_get_children ((ClutterContainer*) actor);
|
||||
|
||||
for (iter = children; iter; iter = iter->next)
|
||||
{
|
||||
ClutterActor *child = iter->data;
|
||||
|
||||
if (g_hash_table_lookup (self->priv->skip_paint, child))
|
||||
continue;
|
||||
|
||||
clutter_actor_paint (child);
|
||||
}
|
||||
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static void
|
||||
on_skip_paint_weakref (gpointer user_data,
|
||||
GObject *location)
|
||||
{
|
||||
ShellGenericContainer *self = SHELL_GENERIC_CONTAINER (user_data);
|
||||
|
||||
g_hash_table_remove (self->priv->skip_paint, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_generic_container_set_skip_paint:
|
||||
* @container: A #ShellGenericContainer
|
||||
* @child: Child #ClutterActor
|
||||
* @skip %TRUE if we should skip painting
|
||||
*
|
||||
* Set whether or not we should skip painting @actor. Workaround for
|
||||
* lack of gjs ability to override _paint vfunc.
|
||||
*/
|
||||
void
|
||||
shell_generic_container_set_skip_paint (ShellGenericContainer *self,
|
||||
ClutterActor *child,
|
||||
gboolean skip)
|
||||
{
|
||||
gboolean currently_skipping;
|
||||
|
||||
currently_skipping = g_hash_table_lookup (self->priv->skip_paint, child) != NULL;
|
||||
if (!!skip == currently_skipping)
|
||||
return;
|
||||
|
||||
if (!skip)
|
||||
{
|
||||
g_object_weak_unref ((GObject*) child, on_skip_paint_weakref, self);
|
||||
g_hash_table_remove (self->priv->skip_paint, child);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_object_weak_ref ((GObject*) child, on_skip_paint_weakref, self);
|
||||
g_hash_table_insert (self->priv->skip_paint, child, child);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_dispose (GObject *object)
|
||||
{
|
||||
ShellGenericContainer *self = (ShellGenericContainer*) object;
|
||||
|
||||
if (self->priv->skip_paint != NULL)
|
||||
{
|
||||
g_hash_table_destroy (self->priv->skip_paint);
|
||||
self->priv->skip_paint = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (shell_generic_container_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_generic_container_class_init (ShellGenericContainerClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = shell_generic_container_dispose;
|
||||
|
||||
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;
|
||||
actor_class->paint = shell_generic_container_paint;
|
||||
actor_class->pick = shell_generic_container_pick;
|
||||
|
||||
shell_generic_container_signals[GET_PREFERRED_WIDTH] =
|
||||
g_signal_new ("get-preferred-width",
|
||||
@ -227,6 +331,7 @@ shell_generic_container_init (ShellGenericContainer *area)
|
||||
{
|
||||
area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_GENERIC_CONTAINER,
|
||||
ShellGenericContainerPrivate);
|
||||
area->priv->skip_paint = g_hash_table_new_full (NULL, NULL, (GDestroyNotify)g_object_unref, NULL);
|
||||
}
|
||||
|
||||
GType shell_generic_container_allocation_get_type (void)
|
||||
|
@ -42,4 +42,8 @@ struct _ShellGenericContainerClass
|
||||
|
||||
GType shell_generic_container_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void shell_generic_container_set_skip_paint (ShellGenericContainer *container,
|
||||
ClutterActor *actor,
|
||||
gboolean skip);
|
||||
|
||||
#endif /* __SHELL_GENERIC_CONTAINER_H__ */
|
||||
|
@ -522,6 +522,70 @@ shell_global_display_is_grabbed (ShellGlobal *global)
|
||||
return meta_display_get_grab_op (display) != META_GRAB_OP_NONE;
|
||||
}
|
||||
|
||||
/* Defining this here for now, see
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=604075
|
||||
* for upstreaming status.
|
||||
*/
|
||||
JSContext * gjs_context_get_context (GjsContext *context);
|
||||
|
||||
/**
|
||||
* shell_global_add_extension_importer:
|
||||
* @target_object_script: JavaScript code evaluating to a target object
|
||||
* @target_property: Name of property to use for importer
|
||||
* @directory: Source directory:
|
||||
* @error: A #GError
|
||||
*
|
||||
* This function sets a property named @target_property on the object
|
||||
* resulting from the evaluation of @target_object_script code, which
|
||||
* acts as a GJS importer for directory @directory.
|
||||
*
|
||||
* Returns: %TRUE on success
|
||||
*/
|
||||
gboolean
|
||||
shell_global_add_extension_importer (ShellGlobal *global,
|
||||
const char *target_object_script,
|
||||
const char *target_property,
|
||||
const char *directory,
|
||||
GError **error)
|
||||
{
|
||||
jsval target_object;
|
||||
JSObject *importer;
|
||||
JSContext *context = gjs_context_get_context (global->js_context);
|
||||
char *search_path[2] = { 0, 0 };
|
||||
|
||||
// This is a bit of a hack; ideally we'd be able to pass our target
|
||||
// object directly into this function, but introspection doesn't
|
||||
// support that at the moment. Instead evaluate a string to get it.
|
||||
if (!JS_EvaluateScript(context,
|
||||
JS_GetGlobalObject(context),
|
||||
target_object_script,
|
||||
strlen (target_object_script),
|
||||
"<target_object_script>",
|
||||
0,
|
||||
&target_object))
|
||||
{
|
||||
char *message;
|
||||
gjs_log_exception(context,
|
||||
&message);
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_FAILED,
|
||||
"%s", message ? message : "(unknown)");
|
||||
g_free(message);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!JSVAL_IS_OBJECT (target_object))
|
||||
{
|
||||
g_error ("shell_global_add_extension_importer: invalid target object");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
search_path[0] = (char*)directory;
|
||||
importer = gjs_define_importer (context, JSVAL_TO_OBJECT (target_object), target_property, (const char **)search_path, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Code to close all file descriptors before we exec; copied from gspawn.c in GLib.
|
||||
*
|
||||
* Authors: Padraig O'Briain, Matthias Clasen, Lennart Poettering
|
||||
@ -1064,3 +1128,63 @@ shell_popup_menu (GtkMenu *menu, int button, guint32 time,
|
||||
gtk_menu_popup (menu, NULL, NULL, shell_popup_menu_position_func, NULL,
|
||||
button, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_current_time:
|
||||
* @global: A #ShellGlobal
|
||||
*
|
||||
* Returns: the current X server time from the current Clutter, Gdk, or X
|
||||
* event. If called from outside an event handler, this may return
|
||||
* %Clutter.CURRENT_TIME (aka 0), or it may return a slightly
|
||||
* out-of-date timestamp.
|
||||
*/
|
||||
guint32
|
||||
shell_global_get_current_time (ShellGlobal *global)
|
||||
{
|
||||
guint32 time;
|
||||
MetaDisplay *display;
|
||||
|
||||
/* meta_display_get_current_time() will return the correct time
|
||||
when handling an X or Gdk event, but will return CurrentTime
|
||||
from some Clutter event callbacks.
|
||||
|
||||
clutter_get_current_event_time() will return the correct time
|
||||
from a Clutter event callback, but may return an out-of-date
|
||||
timestamp if called at other times.
|
||||
|
||||
So we try meta_display_get_current_time() first, since we
|
||||
can recognize a "wrong" answer from that, and then fall back
|
||||
to clutter_get_current_event_time().
|
||||
*/
|
||||
|
||||
display = meta_screen_get_display (shell_global_get_screen (global));
|
||||
time = meta_display_get_current_time (display);
|
||||
if (time != CLUTTER_CURRENT_TIME)
|
||||
return time;
|
||||
|
||||
return clutter_get_current_event_time ();
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_app_launch_context:
|
||||
* @global: A #ShellGlobal
|
||||
*
|
||||
* Create a #GAppLaunchContext set up with the correct timestamp, and
|
||||
* targeted to activate on the current workspace.
|
||||
*
|
||||
* Return value: A new #GAppLaunchContext
|
||||
*/
|
||||
GAppLaunchContext *
|
||||
shell_global_create_app_launch_context (ShellGlobal *global)
|
||||
{
|
||||
GdkAppLaunchContext *context;
|
||||
|
||||
context = gdk_app_launch_context_new ();
|
||||
gdk_app_launch_context_set_timestamp (context, shell_global_get_current_time (global));
|
||||
|
||||
// Make sure that the app is opened on the current workspace even if
|
||||
// the user switches before it starts
|
||||
gdk_app_launch_context_set_desktop (context, meta_screen_get_active_workspace_index (shell_global_get_screen (global)));
|
||||
|
||||
return (GAppLaunchContext *)context;
|
||||
}
|
||||
|
@ -43,6 +43,12 @@ ShellGlobal *shell_global_get (void);
|
||||
|
||||
MetaScreen *shell_global_get_screen (ShellGlobal *global);
|
||||
|
||||
gboolean shell_global_add_extension_importer (ShellGlobal *global,
|
||||
const char *target_object_script,
|
||||
const char *target_property,
|
||||
const char *directory,
|
||||
GError **error);
|
||||
|
||||
void shell_global_grab_dbus_service (ShellGlobal *global);
|
||||
|
||||
typedef enum {
|
||||
@ -82,6 +88,9 @@ ClutterModifierType shell_get_event_state (ClutterEvent *event);
|
||||
void shell_popup_menu (GtkMenu *menu, int button, guint32 time,
|
||||
int menu_x, int menu_y);
|
||||
|
||||
guint32 shell_global_get_current_time (ShellGlobal *global);
|
||||
|
||||
GAppLaunchContext *shell_global_create_app_launch_context (ShellGlobal *global);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -98,10 +98,6 @@ static void
|
||||
st_button_update_label_style (StButton *button)
|
||||
{
|
||||
ClutterActor *label;
|
||||
StThemeNode *theme_node;
|
||||
ClutterColor color;
|
||||
const PangoFontDescription *font;
|
||||
gchar *font_string = NULL;
|
||||
|
||||
label = st_bin_get_child ((StBin*) button);
|
||||
|
||||
@ -109,15 +105,7 @@ st_button_update_label_style (StButton *button)
|
||||
if (!CLUTTER_IS_TEXT (label))
|
||||
return;
|
||||
|
||||
theme_node = st_widget_get_theme_node (ST_WIDGET (button));
|
||||
|
||||
st_theme_node_get_foreground_color (theme_node, &color);
|
||||
clutter_text_set_color (CLUTTER_TEXT (label), &color);
|
||||
|
||||
font = st_theme_node_get_font (theme_node);
|
||||
font_string = pango_font_description_to_string (font);
|
||||
clutter_text_set_font_name (CLUTTER_TEXT (label), font_string);
|
||||
g_free (font_string);
|
||||
_st_set_text_from_style ((ClutterText*) label, st_widget_get_theme_node (ST_WIDGET (button)));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -43,7 +43,7 @@
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include "st-label.h"
|
||||
|
||||
#include "st-private.h"
|
||||
#include "st-widget.h"
|
||||
|
||||
enum
|
||||
@ -110,21 +110,9 @@ st_label_get_property (GObject *gobject,
|
||||
static void
|
||||
st_label_style_changed (StWidget *self)
|
||||
{
|
||||
StLabelPrivate *priv;
|
||||
StThemeNode *theme_node;
|
||||
ClutterColor color;
|
||||
const PangoFontDescription *font;
|
||||
gchar *font_string;
|
||||
StLabelPrivate *priv = ST_LABEL(self)->priv;
|
||||
|
||||
priv = ST_LABEL (self)->priv;
|
||||
theme_node = st_widget_get_theme_node (self);
|
||||
st_theme_node_get_foreground_color (theme_node, &color);
|
||||
clutter_text_set_color (CLUTTER_TEXT (priv->label), &color);
|
||||
|
||||
font = st_theme_node_get_font (theme_node);
|
||||
font_string = pango_font_description_to_string (font);
|
||||
clutter_text_set_font_name (CLUTTER_TEXT (priv->label), font_string);
|
||||
g_free (font_string);
|
||||
_st_set_text_from_style ((ClutterText *)priv->label, st_widget_get_theme_node (self));
|
||||
|
||||
ST_WIDGET_CLASS (st_label_parent_class)->style_changed (self);
|
||||
}
|
||||
|
706
src/st/st-overflow-box.c
Normal file
706
src/st/st-overflow-box.c
Normal file
@ -0,0 +1,706 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* Portions derived from st-box-layout.c, which is
|
||||
* Copyright 2009 Intel Corporation.
|
||||
* Modified into -overflow-box, by Colin Walters <walters@verbum.org>, which is
|
||||
* Copyright 2009 Red Hat, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU Lesser General Public License,
|
||||
* version 2.1, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:st-overflow-box
|
||||
* @short_description: A vertical box which paints as many actors as it can fit
|
||||
*
|
||||
* This is a "flexible" box which will paint as many actors as it can within
|
||||
* its given allocation; its minimum height request will be the sum of the
|
||||
* mimimum size for the #StOverflowBox:min-children property, which is
|
||||
* by default 0.
|
||||
*
|
||||
* Every child will be allocated the full width of the box, and always be
|
||||
* given its preferred height. Even if not actually painted, every child
|
||||
* is counted for overall preferred width/height.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "st-overflow-box.h"
|
||||
|
||||
#include "st-private.h"
|
||||
#include "st-box-layout-child.h"
|
||||
|
||||
static void st_overflow_box_container_iface_init (ClutterContainerIface *iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (StOverflowBox, st_overflow_box, ST_TYPE_WIDGET,
|
||||
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
|
||||
st_overflow_box_container_iface_init));
|
||||
|
||||
#define OVERFLOW_BOX_LAYOUT_PRIVATE(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_OVERFLOW_BOX, StOverflowBoxPrivate))
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
|
||||
PROP_MIN_CHILDREN
|
||||
};
|
||||
|
||||
struct _StOverflowBoxPrivate
|
||||
{
|
||||
GList *children;
|
||||
guint min_children;
|
||||
guint n_visible;
|
||||
|
||||
guint spacing;
|
||||
};
|
||||
|
||||
/*
|
||||
* ClutterContainer Implementation
|
||||
*/
|
||||
static void
|
||||
st_overflow_box_add_actor (ClutterContainer *container,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (container)->priv;
|
||||
|
||||
clutter_actor_set_parent (actor, CLUTTER_ACTOR (container));
|
||||
|
||||
priv->children = g_list_append (priv->children, actor);
|
||||
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
|
||||
|
||||
g_signal_emit_by_name (container, "actor-added", actor);
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_remove_actor (ClutterContainer *container,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (container)->priv;
|
||||
|
||||
GList *item = NULL;
|
||||
|
||||
item = g_list_find (priv->children, actor);
|
||||
|
||||
if (item == NULL)
|
||||
{
|
||||
g_warning ("Actor of type '%s' is not a child of container of type '%s'",
|
||||
g_type_name (G_OBJECT_TYPE (actor)),
|
||||
g_type_name (G_OBJECT_TYPE (container)));
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_ref (actor);
|
||||
|
||||
priv->children = g_list_delete_link (priv->children, item);
|
||||
clutter_actor_unparent (actor);
|
||||
|
||||
g_signal_emit_by_name (container, "actor-removed", actor);
|
||||
|
||||
g_object_unref (actor);
|
||||
|
||||
clutter_actor_queue_relayout ((ClutterActor*) container);
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_foreach (ClutterContainer *container,
|
||||
ClutterCallback callback,
|
||||
gpointer callback_data)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (container)->priv;
|
||||
|
||||
g_list_foreach (priv->children, (GFunc) callback, callback_data);
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_lower (ClutterContainer *container,
|
||||
ClutterActor *actor,
|
||||
ClutterActor *sibling)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (container)->priv;
|
||||
|
||||
/* copied from clutter/clutter/clutter-group.c */
|
||||
|
||||
priv->children = g_list_remove (priv->children, actor);
|
||||
|
||||
/* Push to bottom */
|
||||
if (!sibling)
|
||||
{
|
||||
GList *last_item;
|
||||
|
||||
last_item = g_list_first (priv->children);
|
||||
|
||||
if (last_item)
|
||||
sibling = last_item->data;
|
||||
|
||||
priv->children = g_list_prepend (priv->children, actor);
|
||||
}
|
||||
else
|
||||
{
|
||||
gint pos;
|
||||
|
||||
pos = g_list_index (priv->children, sibling);
|
||||
|
||||
priv->children = g_list_insert (priv->children, actor, pos);
|
||||
}
|
||||
|
||||
/* See comment in group_raise for this */
|
||||
if (sibling &&
|
||||
clutter_actor_get_depth (sibling) != clutter_actor_get_depth (actor))
|
||||
{
|
||||
clutter_actor_set_depth (actor, clutter_actor_get_depth (sibling));
|
||||
}
|
||||
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (container))
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (container));
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_raise (ClutterContainer *container,
|
||||
ClutterActor *actor,
|
||||
ClutterActor *sibling)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (container)->priv;
|
||||
|
||||
priv->children = g_list_remove (priv->children, actor);
|
||||
|
||||
/* copied from clutter/clutter/clutter-group.c */
|
||||
|
||||
/* Raise at the top */
|
||||
if (!sibling)
|
||||
{
|
||||
GList *last_item;
|
||||
|
||||
last_item = g_list_last (priv->children);
|
||||
|
||||
if (last_item)
|
||||
sibling = last_item->data;
|
||||
|
||||
priv->children = g_list_append (priv->children, actor);
|
||||
}
|
||||
else
|
||||
{
|
||||
gint pos;
|
||||
|
||||
pos = g_list_index (priv->children, sibling) + 1;
|
||||
|
||||
priv->children = g_list_insert (priv->children, actor, pos);
|
||||
}
|
||||
|
||||
/* set Z ordering a value below, this will then call sort
|
||||
* as values are equal ordering shouldn't change but Z
|
||||
* values will be correct.
|
||||
*
|
||||
* FIXME: optimise
|
||||
*/
|
||||
if (sibling &&
|
||||
clutter_actor_get_depth (sibling) != clutter_actor_get_depth (actor))
|
||||
{
|
||||
clutter_actor_set_depth (actor, clutter_actor_get_depth (sibling));
|
||||
}
|
||||
|
||||
if (CLUTTER_ACTOR_IS_VISIBLE (container))
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (container));
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_sort_depth_order (ClutterContainer *container)
|
||||
{
|
||||
/* XXX: not yet implemented */
|
||||
g_warning ("%s() not yet implemented", __FUNCTION__);
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_container_iface_init (ClutterContainerIface *iface)
|
||||
{
|
||||
iface->add = st_overflow_box_add_actor;
|
||||
iface->remove = st_overflow_box_remove_actor;
|
||||
iface->foreach = st_overflow_box_foreach;
|
||||
iface->lower = st_overflow_box_lower;
|
||||
iface->raise = st_overflow_box_raise;
|
||||
iface->sort_depth_order = st_overflow_box_sort_depth_order;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
st_overflow_box_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (object)->priv;
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_MIN_CHILDREN:
|
||||
g_value_set_uint (value, priv->min_children);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
StOverflowBox *box = ST_OVERFLOW_BOX (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_MIN_CHILDREN:
|
||||
st_overflow_box_set_min_children (box, g_value_get_uint (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_dispose (GObject *object)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (object)->priv;
|
||||
|
||||
while (priv->children)
|
||||
clutter_actor_destroy (priv->children->data);
|
||||
|
||||
G_OBJECT_CLASS (st_overflow_box_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
get_content_preferred_width (StOverflowBox *self,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = self->priv;
|
||||
gint n_children = 0;
|
||||
gint n_fixed = 0;
|
||||
gfloat min_width, natural_width;
|
||||
GList *l;
|
||||
|
||||
min_width = 0;
|
||||
natural_width = 0;
|
||||
|
||||
for (l = priv->children; l; l = g_list_next (l))
|
||||
{
|
||||
ClutterActor *child = l->data;
|
||||
gfloat child_min = 0, child_nat = 0;
|
||||
|
||||
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
||||
continue;
|
||||
|
||||
n_children++;
|
||||
|
||||
if (clutter_actor_get_fixed_position_set (child))
|
||||
{
|
||||
n_fixed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
clutter_actor_get_preferred_width (child,
|
||||
-1,
|
||||
&child_min,
|
||||
&child_nat);
|
||||
|
||||
min_width = MAX (child_min, min_width);
|
||||
natural_width = MAX (child_nat, natural_width);
|
||||
}
|
||||
|
||||
if ((n_children - n_fixed) > 1)
|
||||
{
|
||||
min_width += priv->spacing * (n_children - n_fixed - 1);
|
||||
natural_width += priv->spacing * (n_children - n_fixed - 1);
|
||||
}
|
||||
|
||||
if (min_width_p)
|
||||
*min_width_p = min_width;
|
||||
|
||||
if (natural_width_p)
|
||||
*natural_width_p = natural_width;
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_get_preferred_width (ClutterActor *actor,
|
||||
gfloat for_height,
|
||||
gfloat *min_width_p,
|
||||
gfloat *natural_width_p)
|
||||
{
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
|
||||
st_theme_node_adjust_for_height (theme_node, &for_height);
|
||||
|
||||
get_content_preferred_width (ST_OVERFLOW_BOX (actor), for_height,
|
||||
min_width_p, natural_width_p);
|
||||
|
||||
st_theme_node_adjust_preferred_width (theme_node,
|
||||
min_width_p, natural_width_p);
|
||||
}
|
||||
|
||||
static void
|
||||
get_content_preferred_height (StOverflowBox *self,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = self->priv;
|
||||
gint n_min_children = 0;
|
||||
gint n_children = 0;
|
||||
gint n_fixed = 0;
|
||||
gfloat min_height, natural_height;
|
||||
GList *l;
|
||||
|
||||
min_height = 0;
|
||||
natural_height = 0;
|
||||
|
||||
for (l = priv->children; l; l = g_list_next (l))
|
||||
{
|
||||
ClutterActor *child = l->data;
|
||||
gfloat child_min = 0, child_nat = 0;
|
||||
|
||||
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
||||
continue;
|
||||
|
||||
n_children++;
|
||||
|
||||
if (clutter_actor_get_fixed_position_set (child))
|
||||
{
|
||||
n_fixed++;
|
||||
continue;
|
||||
}
|
||||
|
||||
clutter_actor_get_preferred_height (child,
|
||||
for_width,
|
||||
&child_min,
|
||||
&child_nat);
|
||||
|
||||
if (n_children < priv->min_children)
|
||||
{
|
||||
n_min_children++;
|
||||
min_height += child_min;
|
||||
}
|
||||
natural_height += child_nat;
|
||||
}
|
||||
|
||||
min_height += priv->spacing * MAX(0, n_min_children - 1);
|
||||
natural_height += priv->spacing * MAX(0, n_children - n_fixed - 1);
|
||||
|
||||
if (min_height_p)
|
||||
*min_height_p = min_height;
|
||||
|
||||
if (natural_height_p)
|
||||
*natural_height_p = natural_height;
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_get_preferred_height (ClutterActor *actor,
|
||||
gfloat for_width,
|
||||
gfloat *min_height_p,
|
||||
gfloat *natural_height_p)
|
||||
{
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
|
||||
st_theme_node_adjust_for_width (theme_node, &for_width);
|
||||
|
||||
get_content_preferred_height (ST_OVERFLOW_BOX (actor), for_width,
|
||||
min_height_p, natural_height_p);
|
||||
|
||||
st_theme_node_adjust_preferred_height (theme_node,
|
||||
min_height_p, natural_height_p);
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_allocate (ClutterActor *actor,
|
||||
const ClutterActorBox *box,
|
||||
ClutterAllocationFlags flags)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (actor)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
||||
ClutterActorBox content_box;
|
||||
gfloat position;
|
||||
float avail_width, avail_height;
|
||||
GList *l;
|
||||
int i;
|
||||
gboolean done_non_fixed;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (st_overflow_box_parent_class)->allocate (actor, box,
|
||||
flags);
|
||||
|
||||
if (priv->children == NULL)
|
||||
return;
|
||||
|
||||
st_theme_node_get_content_box (theme_node, box, &content_box);
|
||||
|
||||
avail_width = content_box.x2 - content_box.x1;
|
||||
avail_height = content_box.y2 - content_box.y1;
|
||||
|
||||
position = content_box.y1;
|
||||
priv->n_visible = 0;
|
||||
|
||||
done_non_fixed = FALSE;
|
||||
for (l = priv->children, i = 0; l; l = l->next, i++)
|
||||
{
|
||||
ClutterActor *child = (ClutterActor*) l->data;
|
||||
ClutterActorBox child_box;
|
||||
gfloat child_min, child_nat;
|
||||
gboolean fixed;
|
||||
|
||||
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
||||
continue;
|
||||
|
||||
fixed = clutter_actor_get_fixed_position_set (child);
|
||||
if (fixed)
|
||||
{
|
||||
clutter_actor_allocate_preferred_size (child, flags);
|
||||
continue;
|
||||
}
|
||||
else if (done_non_fixed)
|
||||
continue;
|
||||
|
||||
clutter_actor_get_preferred_height (child, avail_width,
|
||||
&child_min, &child_nat);
|
||||
|
||||
if (position + child_nat > content_box.y2)
|
||||
{
|
||||
done_non_fixed = TRUE; /* Continue iterating on non fixed */
|
||||
continue;
|
||||
}
|
||||
|
||||
priv->n_visible++;
|
||||
child_box.y1 = (int)(0.5 + position);
|
||||
child_box.y2 = child_box.y1 + (int)(0.5 + child_nat);
|
||||
child_box.x1 = content_box.x1;
|
||||
child_box.x2 = content_box.x2;
|
||||
|
||||
position += child_nat + priv->spacing;
|
||||
|
||||
clutter_actor_allocate (child, &child_box, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_internal_paint (StOverflowBox *box)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = box->priv;
|
||||
GList *l;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
for (l = priv->children; i < priv->n_visible && l; l = l->next)
|
||||
{
|
||||
ClutterActor *child = (ClutterActor*) l->data;
|
||||
|
||||
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
||||
continue;
|
||||
if (!clutter_actor_get_fixed_position_set (child))
|
||||
i++;
|
||||
|
||||
clutter_actor_paint (child);
|
||||
}
|
||||
|
||||
for (;l; l = l->next)
|
||||
{
|
||||
ClutterActor *child = (ClutterActor*) l->data;
|
||||
|
||||
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
||||
continue;
|
||||
|
||||
if (clutter_actor_get_fixed_position_set (child))
|
||||
clutter_actor_paint (child);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_paint (ClutterActor *actor)
|
||||
{
|
||||
CLUTTER_ACTOR_CLASS (st_overflow_box_parent_class)->paint (actor);
|
||||
|
||||
st_overflow_box_internal_paint (ST_OVERFLOW_BOX (actor));
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_pick (ClutterActor *actor,
|
||||
const ClutterColor *color)
|
||||
{
|
||||
CLUTTER_ACTOR_CLASS (st_overflow_box_parent_class)->pick (actor, color);
|
||||
|
||||
st_overflow_box_internal_paint (ST_OVERFLOW_BOX (actor));
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_style_changed (StWidget *self)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (self)->priv;
|
||||
StThemeNode *theme_node = st_widget_get_theme_node (self);
|
||||
int old_spacing = priv->spacing;
|
||||
double spacing = 0;
|
||||
|
||||
st_theme_node_get_length (theme_node, "spacing", FALSE, &spacing);
|
||||
priv->spacing = (int)(spacing + 0.5);
|
||||
if (priv->spacing != old_spacing)
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||||
|
||||
ST_WIDGET_CLASS (st_overflow_box_parent_class)->style_changed (self);
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_class_init (StOverflowBoxClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StOverflowBoxPrivate));
|
||||
|
||||
object_class->get_property = st_overflow_box_get_property;
|
||||
object_class->set_property = st_overflow_box_set_property;
|
||||
object_class->dispose = st_overflow_box_dispose;
|
||||
|
||||
actor_class->allocate = st_overflow_box_allocate;
|
||||
actor_class->get_preferred_width = st_overflow_box_get_preferred_width;
|
||||
actor_class->get_preferred_height = st_overflow_box_get_preferred_height;
|
||||
actor_class->paint = st_overflow_box_paint;
|
||||
actor_class->pick = st_overflow_box_pick;
|
||||
|
||||
widget_class->style_changed = st_overflow_box_style_changed;
|
||||
|
||||
pspec = g_param_spec_uint ("min-children",
|
||||
"Min Children",
|
||||
"The actor will request a minimum size large enough to include this many children",
|
||||
0, G_MAXUINT, 0,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (object_class, PROP_MIN_CHILDREN, pspec);
|
||||
}
|
||||
|
||||
static void
|
||||
st_overflow_box_init (StOverflowBox *self)
|
||||
{
|
||||
self->priv = OVERFLOW_BOX_LAYOUT_PRIVATE (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_overflow_box_get_min_children:
|
||||
* @box: A #StOverflowBox
|
||||
*
|
||||
* Get the value of the #StOverflowBox::pack-start property.
|
||||
*
|
||||
* Returns: #TRUE if pack-start is enabled
|
||||
*/
|
||||
gboolean
|
||||
st_overflow_box_get_min_children (StOverflowBox *box)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_OVERFLOW_BOX (box), FALSE);
|
||||
|
||||
return box->priv->min_children;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_box_layout_set_min_children:
|
||||
* @box: A #StOverflowBox
|
||||
* @min_children: Minimum children value
|
||||
*
|
||||
* Set the minimum number of children to be visible.
|
||||
*/
|
||||
void
|
||||
st_overflow_box_set_min_children (StOverflowBox *box,
|
||||
guint min_children)
|
||||
{
|
||||
g_return_if_fail (ST_IS_OVERFLOW_BOX (box));
|
||||
|
||||
if (box->priv->min_children != min_children)
|
||||
{
|
||||
box->priv->min_children = min_children;
|
||||
clutter_actor_queue_relayout ((ClutterActor*) box);
|
||||
|
||||
g_object_notify (G_OBJECT (box), "min-children");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
st_overflow_box_internal_remove_all (StOverflowBox *self,
|
||||
gboolean destroy)
|
||||
{
|
||||
StOverflowBoxPrivate *priv = ST_OVERFLOW_BOX (self)->priv;
|
||||
ClutterActor *child;
|
||||
|
||||
while (priv->children)
|
||||
{
|
||||
child = priv->children->data;
|
||||
|
||||
g_object_ref (child);
|
||||
priv->children = g_list_delete_link (priv->children, priv->children);
|
||||
clutter_actor_unparent (child);
|
||||
g_signal_emit_by_name (self, "actor-removed", child);
|
||||
if (destroy)
|
||||
clutter_actor_destroy (child);
|
||||
g_object_unref (child);
|
||||
}
|
||||
|
||||
clutter_actor_queue_relayout ((ClutterActor*) self);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_overflow_box_remove_all:
|
||||
* @self:
|
||||
*
|
||||
* Efficiently unparent all children currently in this box.
|
||||
*/
|
||||
void
|
||||
st_overflow_box_remove_all (StOverflowBox *self)
|
||||
{
|
||||
st_overflow_box_internal_remove_all (self, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_overflow_box_destroy_children:
|
||||
* @self:
|
||||
*
|
||||
* Efficiently unparent and destroy all children currently in this box.
|
||||
*/
|
||||
void
|
||||
st_overflow_box_destroy_children (StOverflowBox *self)
|
||||
{
|
||||
st_overflow_box_internal_remove_all (self, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_overflow_box_get_n_children:
|
||||
* @self: a #StOverflowBox
|
||||
*
|
||||
* Returns the number of children in this box.
|
||||
*/
|
||||
guint
|
||||
st_overflow_box_get_n_children (StOverflowBox *self)
|
||||
{
|
||||
return g_list_length (self->priv->children);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_overflow_box_get_n_visible:
|
||||
* @self: a #StOverflowBox
|
||||
*
|
||||
* Returns the number of children we will paint. Only valid
|
||||
* after the actor has been allocated.
|
||||
*/
|
||||
guint
|
||||
st_overflow_box_get_n_visible (StOverflowBox *self)
|
||||
{
|
||||
return self->priv->n_visible;
|
||||
}
|
75
src/st/st-overflow-box.h
Normal file
75
src/st/st-overflow-box.h
Normal file
@ -0,0 +1,75 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-overflow-box.h: box which hides actors that don't fit
|
||||
*
|
||||
* Copyright 2009 Red Hat, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU Lesser General Public License,
|
||||
* version 2.1, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
|
||||
#error "Only <st/st.h> can be included directly.h"
|
||||
#endif
|
||||
|
||||
#ifndef _ST_OVERFLOW_BOX_H
|
||||
#define _ST_OVERFLOW_BOX_H
|
||||
|
||||
#include <st/st-box-layout.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_OVERFLOW_BOX st_overflow_box_get_type()
|
||||
|
||||
#define ST_OVERFLOW_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_OVERFLOW_BOX, StOverflowBox))
|
||||
#define ST_OVERFLOW_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_OVERFLOW_BOX, StOverflowBoxClass))
|
||||
#define ST_IS_OVERFLOW_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_OVERFLOW_BOX))
|
||||
#define ST_IS_OVERFLOW_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_OVERFLOW_BOX))
|
||||
#define ST_OVERFLOW_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_OVERFLOW_BOX, StOverflowBoxClass))
|
||||
|
||||
typedef struct _StOverflowBox StOverflowBox;
|
||||
typedef struct _StOverflowBoxClass StOverflowBoxClass;
|
||||
typedef struct _StOverflowBoxPrivate StOverflowBoxPrivate;
|
||||
|
||||
/**
|
||||
* StOverflowBox:
|
||||
*
|
||||
* The contents of this structure are private and should only be accessed
|
||||
* through the public API.
|
||||
*/
|
||||
struct _StOverflowBox
|
||||
{
|
||||
/*< private >*/
|
||||
StWidget parent;
|
||||
|
||||
StOverflowBoxPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StOverflowBoxClass
|
||||
{
|
||||
StWidgetClass parent_class;
|
||||
};
|
||||
|
||||
GType st_overflow_box_get_type (void);
|
||||
|
||||
void st_overflow_box_set_min_children (StOverflowBox *self, guint min_children);
|
||||
void st_overflow_box_remove_all (StOverflowBox *box);
|
||||
void st_overflow_box_destroy_children (StOverflowBox *box);
|
||||
guint st_overflow_box_get_n_children (StOverflowBox *box);
|
||||
guint st_overflow_box_get_n_visible (StOverflowBox *box);
|
||||
gboolean st_overflow_box_get_min_children (StOverflowBox *box);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _ST_OVERFLOW_BOX_H */
|
@ -110,3 +110,52 @@ _st_allocate_fill (ClutterActor *child,
|
||||
*childbox = allocation;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* _st_set_text_from_style:
|
||||
* @text: Target #ClutterText
|
||||
* @theme_node: Source #StThemeNode
|
||||
*
|
||||
* Set various GObject properties of the @text object using
|
||||
* CSS information from @theme_node.
|
||||
*/
|
||||
void
|
||||
_st_set_text_from_style (ClutterText *text,
|
||||
StThemeNode *theme_node)
|
||||
{
|
||||
|
||||
ClutterColor color;
|
||||
StTextDecoration decoration;
|
||||
PangoAttrList *attribs;
|
||||
const PangoFontDescription *font;
|
||||
gchar *font_string;
|
||||
|
||||
st_theme_node_get_foreground_color (theme_node, &color);
|
||||
clutter_text_set_color (text, &color);
|
||||
|
||||
font = st_theme_node_get_font (theme_node);
|
||||
font_string = pango_font_description_to_string (font);
|
||||
clutter_text_set_font_name (text, font_string);
|
||||
g_free (font_string);
|
||||
|
||||
attribs = pango_attr_list_new ();
|
||||
|
||||
decoration = st_theme_node_get_text_decoration (theme_node);
|
||||
if (decoration & ST_TEXT_DECORATION_UNDERLINE)
|
||||
{
|
||||
PangoAttribute *underline = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
|
||||
pango_attr_list_insert (attribs, underline);
|
||||
}
|
||||
if (decoration & ST_TEXT_DECORATION_LINE_THROUGH)
|
||||
{
|
||||
PangoAttribute *strikethrough = pango_attr_strikethrough_new (TRUE);
|
||||
pango_attr_list_insert (attribs, strikethrough);
|
||||
}
|
||||
/* Pango doesn't have an equivalent attribute for _OVERLINE, and we deliberately
|
||||
* skip BLINK (for now...)
|
||||
*/
|
||||
|
||||
clutter_text_set_attributes (text, attribs);
|
||||
|
||||
pango_attr_list_unref (attribs);
|
||||
}
|
||||
|
@ -55,4 +55,7 @@ void _st_allocate_fill (ClutterActor *child,
|
||||
gboolean x_fill,
|
||||
gboolean y_fill);
|
||||
|
||||
void _st_set_text_from_style (ClutterText *text,
|
||||
StThemeNode *theme_node);
|
||||
|
||||
#endif /* __ST_PRIVATE_H__ */
|
||||
|
@ -1953,7 +1953,10 @@ st_theme_node_get_font (StThemeNode *node)
|
||||
}
|
||||
|
||||
if (family)
|
||||
pango_font_description_set_family (node->font_desc, family);
|
||||
{
|
||||
pango_font_description_set_family (node->font_desc, family);
|
||||
g_free (family);
|
||||
}
|
||||
|
||||
if (size_set)
|
||||
pango_font_description_set_absolute_size (node->font_desc, size);
|
||||
|
@ -44,6 +44,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "st-theme-node.h"
|
||||
#include "st-theme-private.h"
|
||||
|
||||
@ -68,6 +70,7 @@ struct _StTheme
|
||||
char *application_stylesheet;
|
||||
char *default_stylesheet;
|
||||
char *theme_stylesheet;
|
||||
GSList *custom_stylesheets;
|
||||
|
||||
GHashTable *stylesheets_by_filename;
|
||||
GHashTable *filenames_by_stylesheet;
|
||||
@ -193,24 +196,19 @@ convert_rgba_RGBA (char *buf)
|
||||
}
|
||||
|
||||
static CRStyleSheet *
|
||||
parse_stylesheet (const char *filename)
|
||||
parse_stylesheet (const char *filename,
|
||||
GError **error)
|
||||
{
|
||||
enum CRStatus status;
|
||||
char *contents;
|
||||
gsize length;
|
||||
GError *error = NULL;
|
||||
CRStyleSheet *stylesheet = NULL;
|
||||
|
||||
if (filename == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!g_file_get_contents (filename, &contents ,&length, &error))
|
||||
{
|
||||
g_warning("Couldn't read stylesheet: %s", error->message);
|
||||
g_error_free (error);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
if (!g_file_get_contents (filename, &contents, &length, error))
|
||||
return NULL;
|
||||
|
||||
convert_rgba_RGBA (contents);
|
||||
|
||||
@ -218,11 +216,14 @@ parse_stylesheet (const char *filename)
|
||||
length,
|
||||
CR_UTF_8,
|
||||
&stylesheet);
|
||||
g_free (contents);
|
||||
|
||||
if (status != CR_OK)
|
||||
g_warning ("Error parsing stylesheet '%s'", filename);
|
||||
|
||||
g_free (contents);
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Error parsing stylesheet '%s'; errcode:%d", filename, status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stylesheet;
|
||||
}
|
||||
@ -243,7 +244,8 @@ _st_theme_parse_declaration_list (const char *str)
|
||||
}
|
||||
#else /* LIBCROCO_VERSION_NUMBER >= 602 */
|
||||
static CRStyleSheet *
|
||||
parse_stylesheet (const char *filename)
|
||||
parse_stylesheet (const char *filename,
|
||||
GError **error)
|
||||
{
|
||||
enum CRStatus status;
|
||||
CRStyleSheet *stylesheet;
|
||||
@ -257,7 +259,8 @@ parse_stylesheet (const char *filename)
|
||||
|
||||
if (status != CR_OK)
|
||||
{
|
||||
g_warning ("Error parsing stylesheet '%s'", filename);
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Error parsing stylesheet '%s'; errcode:%d", filename, status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -272,6 +275,22 @@ _st_theme_parse_declaration_list (const char *str)
|
||||
}
|
||||
#endif /* LIBCROCO_VERSION_NUMBER < 602 */
|
||||
|
||||
/* Just g_warning for now until we have something nicer to do */
|
||||
static CRStyleSheet *
|
||||
parse_stylesheet_nofail (const char *filename)
|
||||
{
|
||||
GError *error = NULL;
|
||||
CRStyleSheet *result;
|
||||
|
||||
result = parse_stylesheet (filename, &error);
|
||||
if (error)
|
||||
{
|
||||
g_warning ("%s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
insert_stylesheet (StTheme *theme,
|
||||
const char *filename,
|
||||
@ -289,6 +308,42 @@ insert_stylesheet (StTheme *theme,
|
||||
g_hash_table_insert (theme->filenames_by_stylesheet, stylesheet, filename_copy);
|
||||
}
|
||||
|
||||
gboolean
|
||||
st_theme_load_stylesheet (StTheme *theme,
|
||||
const char *path,
|
||||
GError **error)
|
||||
{
|
||||
CRStyleSheet *stylesheet;
|
||||
|
||||
stylesheet = parse_stylesheet (path, error);
|
||||
if (!stylesheet)
|
||||
return FALSE;
|
||||
|
||||
insert_stylesheet (theme, path, stylesheet);
|
||||
theme->custom_stylesheets = g_slist_prepend (theme->custom_stylesheets, stylesheet);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
st_theme_unload_stylesheet (StTheme *theme,
|
||||
const char *path)
|
||||
{
|
||||
CRStyleSheet *stylesheet;
|
||||
|
||||
stylesheet = g_hash_table_lookup (theme->stylesheets_by_filename, path);
|
||||
if (!stylesheet)
|
||||
return;
|
||||
|
||||
if (!g_slist_find (theme->custom_stylesheets, stylesheet))
|
||||
return;
|
||||
|
||||
theme->custom_stylesheets = g_slist_remove (theme->custom_stylesheets, stylesheet);
|
||||
g_hash_table_remove (theme->stylesheets_by_filename, path);
|
||||
g_hash_table_remove (theme->filenames_by_stylesheet, stylesheet);
|
||||
cr_stylesheet_unref (stylesheet);
|
||||
}
|
||||
|
||||
static GObject *
|
||||
st_theme_constructor (GType type,
|
||||
guint n_construct_properties,
|
||||
@ -305,9 +360,9 @@ st_theme_constructor (GType type,
|
||||
construct_properties);
|
||||
theme = ST_THEME (object);
|
||||
|
||||
application_stylesheet = parse_stylesheet (theme->application_stylesheet);
|
||||
theme_stylesheet = parse_stylesheet (theme->theme_stylesheet);
|
||||
default_stylesheet = parse_stylesheet (theme->default_stylesheet);
|
||||
application_stylesheet = parse_stylesheet_nofail (theme->application_stylesheet);
|
||||
theme_stylesheet = parse_stylesheet_nofail (theme->theme_stylesheet);
|
||||
default_stylesheet = parse_stylesheet_nofail (theme->default_stylesheet);
|
||||
|
||||
theme->cascade = cr_cascade_new (application_stylesheet,
|
||||
theme_stylesheet,
|
||||
@ -328,6 +383,10 @@ st_theme_finalize (GObject * object)
|
||||
{
|
||||
StTheme *theme = ST_THEME (object);
|
||||
|
||||
g_slist_foreach (theme->custom_stylesheets, (GFunc) cr_stylesheet_unref, NULL);
|
||||
g_slist_free (theme->custom_stylesheets);
|
||||
theme->custom_stylesheets = NULL;
|
||||
|
||||
g_hash_table_destroy (theme->stylesheets_by_filename);
|
||||
g_hash_table_destroy (theme->filenames_by_stylesheet);
|
||||
|
||||
@ -559,6 +618,7 @@ id_add_sel_matches_style (CRAdditionalSel *a_add_sel,
|
||||
}
|
||||
|
||||
/**
|
||||
*additional_selector_matches_style:
|
||||
*Evaluates if a given additional selector matches an style node.
|
||||
*@param a_add_sel the additional selector to consider.
|
||||
*@param a_node the style node to consider.
|
||||
@ -862,7 +922,7 @@ add_matched_properties (StTheme *a_this,
|
||||
import_rule->url->stryng->str);
|
||||
|
||||
if (filename)
|
||||
import_rule->sheet = parse_stylesheet (filename);
|
||||
import_rule->sheet = parse_stylesheet (filename, NULL);
|
||||
|
||||
if (import_rule->sheet)
|
||||
{
|
||||
@ -980,6 +1040,7 @@ _st_theme_get_matched_properties (StTheme *theme,
|
||||
enum CRStyleOrigin origin = 0;
|
||||
CRStyleSheet *sheet = NULL;
|
||||
GPtrArray *props = g_ptr_array_new ();
|
||||
GSList *iter;
|
||||
|
||||
g_return_val_if_fail (ST_IS_THEME (theme), NULL);
|
||||
g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
|
||||
@ -993,6 +1054,9 @@ _st_theme_get_matched_properties (StTheme *theme,
|
||||
add_matched_properties (theme, sheet, node, props);
|
||||
}
|
||||
|
||||
for (iter = theme->custom_stylesheets; iter; iter = iter->next)
|
||||
add_matched_properties (theme, iter->data, node, props);
|
||||
|
||||
/* We count on a stable sort here so that later declarations come
|
||||
* after earlier declarations */
|
||||
g_ptr_array_sort (props, compare_declarations);
|
||||
|
@ -33,6 +33,10 @@ StTheme *st_theme_new (const char *application_stylesheet,
|
||||
const char *theme_stylesheet,
|
||||
const char *default_stylesheet);
|
||||
|
||||
gboolean st_theme_load_stylesheet (StTheme *theme, const char *path, GError **error);
|
||||
|
||||
void st_theme_unload_stylesheet (StTheme *theme, const char *path);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_THEME_H__ */
|
||||
|
@ -155,7 +155,7 @@ if test x$system = xMandrivaLinux ; then
|
||||
fi
|
||||
|
||||
SOURCE=$HOME/Source
|
||||
BASEURL=http://git.gnome.org/cgit/gnome-shell/plain/tools/build
|
||||
BASEURL=http://git.gnome.org/browse/gnome-shell/plain/tools/build
|
||||
|
||||
if [ -d $SOURCE ] ; then : ; else
|
||||
mkdir $SOURCE
|
||||
|
@ -18,7 +18,7 @@
|
||||
# Only rebuild modules that have changed
|
||||
build_policy = 'updated'
|
||||
|
||||
moduleset = 'http://git.gnome.org/cgit/gnome-shell/plain/tools/build/gnome-shell.modules'
|
||||
moduleset = 'http://git.gnome.org/browse/gnome-shell/plain/tools/build/gnome-shell.modules'
|
||||
|
||||
modules = [ 'gnome-shell' ]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user