Compare commits
	
		
			13 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					07c39959d9 | ||
| 
						 | 
					23d1d18619 | ||
| 
						 | 
					23bf5db80c | ||
| 
						 | 
					550e9973b7 | ||
| 
						 | 
					e4e6b26e15 | ||
| 
						 | 
					9551e1c2f5 | ||
| 
						 | 
					a70ea12be8 | ||
| 
						 | 
					fb5a3a53fa | ||
| 
						 | 
					30eac56691 | ||
| 
						 | 
					cb6f3a0836 | ||
| 
						 | 
					8e579024b1 | ||
| 
						 | 
					41c70293df | ||
| 
						 | 
					4b015903bc | 
@@ -10,7 +10,9 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	misc/history.js		\
 | 
			
		||||
	misc/modemManager.js	\
 | 
			
		||||
	misc/params.js		\
 | 
			
		||||
	misc/semantic.js	\
 | 
			
		||||
	misc/util.js		\
 | 
			
		||||
	misc/zeitgeist.js	\
 | 
			
		||||
	perf/core.js		\
 | 
			
		||||
	ui/altTab.js		\
 | 
			
		||||
	ui/appDisplay.js	\
 | 
			
		||||
@@ -22,7 +24,6 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/dash.js		\
 | 
			
		||||
	ui/dateMenu.js		\
 | 
			
		||||
	ui/dnd.js		\
 | 
			
		||||
	ui/docDisplay.js	\
 | 
			
		||||
	ui/endSessionDialog.js	\
 | 
			
		||||
	ui/environment.js	\
 | 
			
		||||
	ui/extensionSystem.js	\
 | 
			
		||||
@@ -64,4 +65,5 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/workspaceThumbnail.js	\
 | 
			
		||||
	ui/workspacesView.js	\
 | 
			
		||||
	ui/workspaceSwitcherPopup.js    \
 | 
			
		||||
	ui/xdndHandler.js
 | 
			
		||||
	ui/xdndHandler.js	\
 | 
			
		||||
	ui/zeitgeistSearch.js
 | 
			
		||||
 
 | 
			
		||||
@@ -1,36 +1,32 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
const THUMBNAIL_ICON_MARGIN = 2;
 | 
			
		||||
 | 
			
		||||
function DocInfo(recentInfo) {
 | 
			
		||||
    this._init(recentInfo);
 | 
			
		||||
function ZeitgeistItemInfo(event) {
 | 
			
		||||
    this._init(event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocInfo.prototype = {
 | 
			
		||||
    _init : function(recentInfo) {
 | 
			
		||||
        this.recentInfo = recentInfo;
 | 
			
		||||
        // We actually used get_modified() instead of get_visited()
 | 
			
		||||
        // here, as GtkRecentInfo doesn't updated get_visited()
 | 
			
		||||
        // correctly. See http://bugzilla.gnome.org/show_bug.cgi?id=567094
 | 
			
		||||
        this.timestamp = recentInfo.get_modified();
 | 
			
		||||
        this.name = recentInfo.get_display_name();
 | 
			
		||||
ZeitgeistItemInfo.prototype = {
 | 
			
		||||
    _init : function(event) {
 | 
			
		||||
        this.event = event;
 | 
			
		||||
        this.subject = event.subjects[0];
 | 
			
		||||
        this.timestamp = event.timestamp;
 | 
			
		||||
        this.name = this.subject.text;
 | 
			
		||||
        this._lowerName = this.name.toLowerCase();
 | 
			
		||||
        this.uri = recentInfo.get_uri();
 | 
			
		||||
        this.mimeType = recentInfo.get_mime_type();
 | 
			
		||||
        this.uri = this.subject.uri;
 | 
			
		||||
        this.mimeType = this.subject.mimetype;
 | 
			
		||||
        this.interpretation = this.subject.interpretation;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon : function(size) {
 | 
			
		||||
        return St.TextureCache.get_default().load_recent_thumbnail(size, this.recentInfo);
 | 
			
		||||
        return St.TextureCache.get_default().load_thumbnail(size, this.uri, this.subject.mimetype);
 | 
			
		||||
        // FIXME: We should consider caching icons
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    launch : function(workspaceIndex) {
 | 
			
		||||
        Shell.DocSystem.get_default().open(this.recentInfo, workspaceIndex);
 | 
			
		||||
    launch : function() {
 | 
			
		||||
        Gio.app_info_launch_default_for_uri(this.uri,
 | 
			
		||||
                                            global.create_app_launch_context());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    matchTerms: function(terms) {
 | 
			
		||||
@@ -48,93 +44,5 @@ DocInfo.prototype = {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return mtype;
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
var docManagerInstance = null;
 | 
			
		||||
 | 
			
		||||
function getDocManager() {
 | 
			
		||||
    if (docManagerInstance == null)
 | 
			
		||||
        docManagerInstance = new DocManager();
 | 
			
		||||
    return docManagerInstance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * DocManager wraps the DocSystem, primarily to expose DocInfo objects.
 | 
			
		||||
 */
 | 
			
		||||
function DocManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        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._docSystem.get_all();
 | 
			
		||||
        this._infosByTimestamp = [];
 | 
			
		||||
        this._infosByUri = {};
 | 
			
		||||
        for (let i = 0; i < docs.length; i++) {
 | 
			
		||||
            let recentInfo = docs[i];
 | 
			
		||||
 | 
			
		||||
            let docInfo = new DocInfo(recentInfo);
 | 
			
		||||
            this._infosByTimestamp.push(docInfo);
 | 
			
		||||
            this._infosByUri[docInfo.uri] = docInfo;
 | 
			
		||||
        }
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _searchDocs: function(items, terms) {
 | 
			
		||||
        let multiplePrefixMatches = [];
 | 
			
		||||
        let prefixMatches = [];
 | 
			
		||||
        let multipleSubtringMatches = [];
 | 
			
		||||
        let substringMatches = [];
 | 
			
		||||
        for (let i = 0; i < items.length; i++) {
 | 
			
		||||
            let item = items[i];
 | 
			
		||||
            let mtype = item.matchTerms(terms);
 | 
			
		||||
            if (mtype == Search.MatchType.MULTIPLE_PREFIX)
 | 
			
		||||
                multiplePrefixMatches.push(item.uri);
 | 
			
		||||
            else if (mtype == Search.MatchType.PREFIX)
 | 
			
		||||
                prefixMatches.push(item.uri);
 | 
			
		||||
            else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING)
 | 
			
		||||
                multipleSubtringMatches.push(item.uri);
 | 
			
		||||
            else if (mtype == Search.MatchType.SUBSTRING)
 | 
			
		||||
                substringMatches.push(item.uri);
 | 
			
		||||
         }
 | 
			
		||||
        return multiplePrefixMatches.concat(prefixMatches.concat(multipleSubtringMatches.concat(substringMatches)));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    initialSearch: function(terms) {
 | 
			
		||||
        return this._searchDocs(this._infosByTimestamp, terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    subsearch: function(previousResults, terms) {
 | 
			
		||||
        return this._searchDocs(previousResults.map(Lang.bind(this,
 | 
			
		||||
            function(url) {
 | 
			
		||||
                return this._infosByUri[url];
 | 
			
		||||
            })), terms);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(DocManager.prototype);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								js/misc/semantic.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								js/misc/semantic.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 *
 | 
			
		||||
 * Semantic-desktop interpretations for various data types
 | 
			
		||||
 *
 | 
			
		||||
 * Authors: Federico Mena Quintero <federico@gnome.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const NFO_AUDIO                   = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Audio";
 | 
			
		||||
const NFO_DOCUMENT                = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Document";
 | 
			
		||||
const NFO_HTML_DOCUMENT           = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#HtmlDocument";
 | 
			
		||||
const NFO_IMAGE                   = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Image";
 | 
			
		||||
const NFO_MEDIA                   = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Media";
 | 
			
		||||
const NFO_MIND_MAP                = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#MindMap";
 | 
			
		||||
const NFO_PAGINATED_TEXT_DOCUMENT = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#PaginatedTextDocument";
 | 
			
		||||
const NFO_PLAIN_TEXT_DOCUMENT     = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#PlainTextDocument";
 | 
			
		||||
const NFO_PRESENTATION            = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Presentation";
 | 
			
		||||
const NFO_RASTER_IMAGE            = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#RasterImage";
 | 
			
		||||
const NFO_SOURCE_CODE             = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SourceCode";
 | 
			
		||||
const NFO_SPREADSHEET             = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Spreadsheet";
 | 
			
		||||
const NFO_TEXT_DOCUMENT           = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#TextDocument";
 | 
			
		||||
const NFO_VECTOR_IMAGE            = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#VectorImage";
 | 
			
		||||
const NFO_VIDEO                   = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Video";
 | 
			
		||||
 | 
			
		||||
const NMM_CURSOR                  = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#Cursor";
 | 
			
		||||
const NMM_ICON                    = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#Icon";
 | 
			
		||||
const NMM_MOVIE                   = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#Movie";
 | 
			
		||||
const NMM_MUSIC_PIECE             = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#MusicPiece";
 | 
			
		||||
const NMM_TV_SHOW                 = "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#TVShow";
 | 
			
		||||
							
								
								
									
										264
									
								
								js/misc/zeitgeist.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								js/misc/zeitgeist.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,264 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2010 Seif Lotfy <seif@lotfy.com>
 | 
			
		||||
 * Copyright (C) 2011 Siegfried-Angel Gevatter Pujals <siegfried@gevatter.com>
 | 
			
		||||
 * Copyright (C) 2010-2011 Collabora Ltd.
 | 
			
		||||
 *     Authored by: Seif Lotfy <seif@lotfy.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
 | 
			
		||||
const SIG_EVENT = '(asaasay)';
 | 
			
		||||
const MAX_TIMESTAMP = 9999999999999;
 | 
			
		||||
 | 
			
		||||
// Number of results given by fullTextSearch; 100 is probably enough.
 | 
			
		||||
// Note: We can't currently increase this number to anything above 132, due to
 | 
			
		||||
// https://bugs.launchpad.net/zeitgeist-extensions/+bug/716503
 | 
			
		||||
const MAX_RESULTS = 100;
 | 
			
		||||
 | 
			
		||||
const ResultType = {
 | 
			
		||||
    // http://zeitgeist-project.com/docs/0.6/datamodel.html#resulttype
 | 
			
		||||
    // It's unfortunate to have to define these by hand; maybe if D-Bus had a way to introspect enums...
 | 
			
		||||
    MOST_RECENT_EVENTS                   : 0,
 | 
			
		||||
    LEAST_RECENT_EVENTS                  : 1,
 | 
			
		||||
    MOST_RECENT_SUBJECTS                 : 2,
 | 
			
		||||
    LEAST_RECENT_SUBJECTS                : 3,
 | 
			
		||||
    MOST_POPULAR_SUBJECTS                : 4,
 | 
			
		||||
    LEAST_POPULAR_SUBJECTS               : 5,
 | 
			
		||||
    MOST_POPULAR_ACTOR                   : 6,
 | 
			
		||||
    LEAST_POPULAR_ACTOR                  : 7,
 | 
			
		||||
    MOST_RECENT_ACTOR                    : 8,
 | 
			
		||||
    LEAST_RECENT_ACTOR                   : 9,
 | 
			
		||||
    MOST_RECENT_ORIGIN                   : 10,
 | 
			
		||||
    LEAST_RECENT_ORIGIN                  : 11,
 | 
			
		||||
    MOST_POPULAR_ORIGIN                  : 12,
 | 
			
		||||
    LEAST_POPULAR_ORIGIN                 : 13,
 | 
			
		||||
    OLDEST_ACTOR                         : 14,
 | 
			
		||||
    MOST_RECENT_SUBJECT_INTERPRETATION   : 15,
 | 
			
		||||
    LEAST_RECENT_SUBJECT_INTERPRETATION  : 16,
 | 
			
		||||
    MOST_POPULAR_SUBJECT_INTERPRETATION  : 17,
 | 
			
		||||
    LEAST_POPULAR_SUBJECT_INTERPRETATION : 18,
 | 
			
		||||
    MOST_RECENT_MIME_TYPE                : 19,
 | 
			
		||||
    LEAST_RECENT_MIME_TYPE               : 20,
 | 
			
		||||
    MOST_POPULAR_MIME_TYPE               : 21,
 | 
			
		||||
    LEAST_POPULAR_MIME_TYPE              : 22
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const StorageState = {
 | 
			
		||||
    // http://zeitgeist-project.com/docs/0.6/datamodel.html#storagestate
 | 
			
		||||
    // As with ResultType, it would be nice if we could introspect enums through D-Bus
 | 
			
		||||
    NOT_AVAILABLE : 0,
 | 
			
		||||
    AVAILABLE     : 1,
 | 
			
		||||
    ANY           : 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Zeitgeist Subjects (files, people, etc.) */
 | 
			
		||||
 | 
			
		||||
function Subject(uri, interpretation, manifestation, origin, mimetype, text, storage) {
 | 
			
		||||
    this._init(uri, interpretation, manifestation, origin, mimetype, text, storage);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Subject.prototype = {
 | 
			
		||||
    _init: function(uri, interpretation, manifestation, origin, mimetype, text, storage) {
 | 
			
		||||
        this.uri = uri;
 | 
			
		||||
        this.interpretation = interpretation;
 | 
			
		||||
        this.manifestation = manifestation;
 | 
			
		||||
        this.origin = origin;
 | 
			
		||||
        this.mimetype = mimetype;
 | 
			
		||||
        this.text = text;
 | 
			
		||||
        this.storage = storage;
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Subject.fromPlain = function(rawSubject) {
 | 
			
		||||
    return new Subject(rawSubject[0], // uri
 | 
			
		||||
                       rawSubject[1], // interpretation
 | 
			
		||||
                       rawSubject[2], // manifestation
 | 
			
		||||
                       rawSubject[3], // origin
 | 
			
		||||
                       rawSubject[4], // mimetype
 | 
			
		||||
                       rawSubject[5], // text
 | 
			
		||||
                       rawSubject[6]); // storage
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Subject.toPlain = function(subject) {
 | 
			
		||||
    let rawSubject = [];
 | 
			
		||||
    rawSubject[0] = subject.uri;
 | 
			
		||||
    rawSubject[1] = subject.interpretation;
 | 
			
		||||
    rawSubject[2] = subject.manifestation
 | 
			
		||||
    rawSubject[3] = subject.origin;
 | 
			
		||||
    rawSubject[4] = subject.mimetype;
 | 
			
		||||
    rawSubject[5] = subject.text;
 | 
			
		||||
    rawSubject[6] = subject.storage;
 | 
			
		||||
    return rawSubject;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Zeitgeist Events */
 | 
			
		||||
 | 
			
		||||
function Event(interpretation, manifestation, actor, subjects, payload) {
 | 
			
		||||
    this._init(interpretation, manifestation, actor, subjects, payload);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Event.prototype = {
 | 
			
		||||
    _init: function(interpretation, manifestation, actor, subjects, payload) {
 | 
			
		||||
        this.id = 0;
 | 
			
		||||
        this.timestamp = 0;
 | 
			
		||||
        this.actor = actor;
 | 
			
		||||
        this.interpretation = interpretation;
 | 
			
		||||
        this.manifestation = manifestation;
 | 
			
		||||
        this.actor = actor;
 | 
			
		||||
        this.payload = payload;
 | 
			
		||||
        this.subjects = subjects;
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Event.fromPlain = function(rawEvent) {
 | 
			
		||||
    let subjects = rawEvent[1].map(Subject.fromPlain);
 | 
			
		||||
    let event = new Event(rawEvent[0][2], // interpretation
 | 
			
		||||
                          rawEvent[0][3], // manifestation
 | 
			
		||||
                          rawEvent[0][4], // actor
 | 
			
		||||
                          subjects, // subjects
 | 
			
		||||
                          rawEvent[2]);// payload
 | 
			
		||||
    event.id = rawEvent[0][0]; // id
 | 
			
		||||
    event.timestamp = parseInt(rawEvent[0][1], 10); // timestamp - it comes as a string over d-bus (yuck)
 | 
			
		||||
    return event;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Event.toPlain = function(event) {
 | 
			
		||||
    let rawEvent = [];
 | 
			
		||||
    rawEvent[0] = [];
 | 
			
		||||
    rawEvent[0][0] = event.id.toString();
 | 
			
		||||
    rawEvent[0][1] = event.timestamp.toString();
 | 
			
		||||
    rawEvent[0][2] = event.interpretation;
 | 
			
		||||
    rawEvent[0][3] = event.manifestation;
 | 
			
		||||
    rawEvent[0][4] = event.actor;
 | 
			
		||||
    rawEvent[1] = event.subjects.map(Subject.toPlain);
 | 
			
		||||
    rawEvent[2] = event.payload;
 | 
			
		||||
    return rawEvent;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Zeitgeist D-Bus interface definitions. Note that most of these are
 | 
			
		||||
// incomplete, and only cover the methods/properties/signals that
 | 
			
		||||
// we're currently using.
 | 
			
		||||
 | 
			
		||||
/* Zeitgeist D-Bus Interface */
 | 
			
		||||
 | 
			
		||||
const LOG_NAME = 'org.gnome.zeitgeist.Engine';
 | 
			
		||||
const LOG_PATH = '/org/gnome/zeitgeist/log/activity';
 | 
			
		||||
const LogIface = {
 | 
			
		||||
    name: 'org.gnome.zeitgeist.Log',
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'GetEvents',
 | 
			
		||||
          inSignature: 'au',
 | 
			
		||||
          outSignature: 'a'+SIG_EVENT },
 | 
			
		||||
        { name: 'FindRelatedUris',
 | 
			
		||||
          inSignature: 'au',
 | 
			
		||||
          outSignature: '(xx)a(' + SIG_EVENT + ')a'+ SIG_EVENT + 'uuu' },
 | 
			
		||||
        { name: 'FindEventIds',
 | 
			
		||||
          inSignature: '(xx)a' + SIG_EVENT + 'uuu',
 | 
			
		||||
          outSignature: 'au' },
 | 
			
		||||
        { name: 'FindEvents',
 | 
			
		||||
          inSignature: '(xx)a' + SIG_EVENT + 'uuu',
 | 
			
		||||
          outSignature: 'a' + SIG_EVENT },
 | 
			
		||||
        { name: 'InsertEvents',
 | 
			
		||||
          inSignature: 'a' + SIG_EVENT,
 | 
			
		||||
          outSignature: 'au' },
 | 
			
		||||
        { name: 'DeleteEvents',
 | 
			
		||||
          inSignature: 'au',
 | 
			
		||||
          outSignature: '(xx)' },
 | 
			
		||||
        { name: 'DeleteLog',
 | 
			
		||||
          inSignature: '',
 | 
			
		||||
          outSignature: '' },
 | 
			
		||||
        { name: 'Quit',
 | 
			
		||||
          inSignature: '',
 | 
			
		||||
          outSignature: '' },
 | 
			
		||||
        // FIXME: Add missing DBus Methods
 | 
			
		||||
        // - InstallMonitor
 | 
			
		||||
        // - RemoveMonitor
 | 
			
		||||
    ],
 | 
			
		||||
    properties: [
 | 
			
		||||
        { name: 'Get',
 | 
			
		||||
          inSignature: 'ss',
 | 
			
		||||
          outSignature: 'v',
 | 
			
		||||
          access: 'read' },
 | 
			
		||||
        { name: 'Set',
 | 
			
		||||
          inSignature: 'ssv',
 | 
			
		||||
          outSignature: '',
 | 
			
		||||
          access: 'read' },
 | 
			
		||||
        { name: 'GetAll',
 | 
			
		||||
          inSignature: 's',
 | 
			
		||||
          outSignature: 'a{sv}',
 | 
			
		||||
          access: 'read' },
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Log = DBus.makeProxyClass(LogIface);
 | 
			
		||||
const _log = new Log(DBus.session, LOG_NAME, LOG_PATH);
 | 
			
		||||
 | 
			
		||||
function findEvents(timeRange, eventTemplates, storageState, numEvents, resultType, callback) {
 | 
			
		||||
    function handler(results, error) {
 | 
			
		||||
        if (error != null)
 | 
			
		||||
            log("Error querying Zeitgeist for events: "+error);
 | 
			
		||||
        else
 | 
			
		||||
            callback(results.map(Event.fromPlain));
 | 
			
		||||
    }
 | 
			
		||||
    _log.FindEventsRemote(timeRange, eventTemplates.map(Event.toPlain),
 | 
			
		||||
                          storageState, numEvents, resultType, handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Zeitgeist Full-Text-Search Interface */
 | 
			
		||||
 | 
			
		||||
const INDEX_NAME = 'org.gnome.zeitgeist.Engine';
 | 
			
		||||
const INDEX_PATH = '/org/gnome/zeitgeist/index/activity';
 | 
			
		||||
const IndexIface = {
 | 
			
		||||
    name: 'org.gnome.zeitgeist.Index',
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'Search',
 | 
			
		||||
          inSignature: 's(xx)a'+SIG_EVENT+'uuu',
 | 
			
		||||
          outSignature: 'a'+SIG_EVENT+'u' },
 | 
			
		||||
    ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Index = DBus.makeProxyClass(IndexIface);
 | 
			
		||||
const _index = new Index(DBus.session, INDEX_NAME, INDEX_PATH);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * fullTextSearch:
 | 
			
		||||
 *
 | 
			
		||||
 * Asynchronously search Zeitgeist's index for events relating to the query.
 | 
			
		||||
 *
 | 
			
		||||
 * @param query The query string, using asterisks for wildcards. Wildcards must
 | 
			
		||||
 *        be used at the start and/or end of a string to get relevant information.
 | 
			
		||||
 * @param eventTemplates Zeitgeist event templates, see
 | 
			
		||||
 *        http://zeitgeist-project.com/docs/0.6/datamodel.html#event for more
 | 
			
		||||
 *        information
 | 
			
		||||
 * @param callback The callback, takes a list containing Zeitgeist.Event
 | 
			
		||||
 *        objects
 | 
			
		||||
 */
 | 
			
		||||
function fullTextSearch(query, eventTemplates, callback) {
 | 
			
		||||
    function handler(results, error) {
 | 
			
		||||
        if (error != null)
 | 
			
		||||
            log("Error searching with Zeitgeist FTS: "+error);
 | 
			
		||||
        else
 | 
			
		||||
            callback(results[0].map(Event.fromPlain));
 | 
			
		||||
    }
 | 
			
		||||
    _index.SearchRemote(query, [0, MAX_TIMESTAMP],
 | 
			
		||||
                        eventTemplates.map(Event.toPlain),
 | 
			
		||||
                        0, // offset into the search results
 | 
			
		||||
                        MAX_RESULTS,
 | 
			
		||||
                        ResultType.MOST_POPULAR_SUBJECTS, handler);
 | 
			
		||||
}
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
@@ -13,6 +14,7 @@ const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const AppFavorites = imports.ui.appFavorites;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const DocInfo = imports.misc.docInfo;
 | 
			
		||||
const IconGrid = imports.ui.iconGrid;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Overview = imports.ui.overview;
 | 
			
		||||
@@ -21,6 +23,7 @@ const Search = imports.ui.search;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const Workspace = imports.ui.workspace;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Zeitgeist = imports.misc.zeitgeist;
 | 
			
		||||
 | 
			
		||||
const MENU_POPUP_TIMEOUT = 600;
 | 
			
		||||
const SCROLL_TIME = 0.1;
 | 
			
		||||
@@ -582,6 +585,7 @@ AppIconMenu.prototype = {
 | 
			
		||||
        this.blockSourceEvents = true;
 | 
			
		||||
 | 
			
		||||
        this._source = source;
 | 
			
		||||
        this._eventTemplate = new Zeitgeist.Event('', '', "application://" + this._source.app.get_id(), [], []);
 | 
			
		||||
 | 
			
		||||
        this.connect('activate', Lang.bind(this, this._onActivate));
 | 
			
		||||
        this.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
 | 
			
		||||
@@ -628,6 +632,63 @@ AppIconMenu.prototype = {
 | 
			
		||||
        this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
 | 
			
		||||
                                                                    : _("Add to Favorites"));
 | 
			
		||||
 | 
			
		||||
        Zeitgeist.findEvents([new Date().getTime() - 86400000*90, Zeitgeist.MAX_TIMESTAMP],
 | 
			
		||||
                             [this._eventTemplate],
 | 
			
		||||
                             Zeitgeist.StorageState.ANY,
 | 
			
		||||
                             100,
 | 
			
		||||
                             Zeitgeist.ResultType.MOST_RECENT_SUBJECTS,
 | 
			
		||||
                             Lang.bind(this, this._appendJumplist));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _appendJumplist: function (events) {
 | 
			
		||||
        let fetchedUris = [];
 | 
			
		||||
        let hasJumplist = false;
 | 
			
		||||
 | 
			
		||||
        function appendEvents(events2, count, type) {
 | 
			
		||||
            if (count == null) {
 | 
			
		||||
                count = 3;
 | 
			
		||||
            }
 | 
			
		||||
            if (type == null) {
 | 
			
		||||
                type = "emblem-favorite";
 | 
			
		||||
            }
 | 
			
		||||
            let j = 0;
 | 
			
		||||
            if (events.length > 0) {
 | 
			
		||||
                for (let i in events) {
 | 
			
		||||
                    let uri = events[i].subjects[0].uri.replace('file://', '');
 | 
			
		||||
                    uri = uri.replace(/\%20/g, ' '); // FIXME: properly unescape, or get the display name otherwise
 | 
			
		||||
                    if (fetchedUris.indexOf(uri) == -1 &&
 | 
			
		||||
                        (GLib.file_test(uri, GLib.FileTest.EXISTS) || this._source.app.get_id() == "tomboy.desktop")) {
 | 
			
		||||
                        if (!hasJumplist) {
 | 
			
		||||
                            this._appendSeparator();
 | 
			
		||||
                            hasJumplist = true;
 | 
			
		||||
                        }
 | 
			
		||||
                        this._appendJumplistItem(events[i], type);
 | 
			
		||||
                        fetchedUris.push(uri);
 | 
			
		||||
                        j++;
 | 
			
		||||
                        if (j >= count)
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        appendEvents.call(this, events, 4, "document-open-recent");
 | 
			
		||||
        Zeitgeist.findEvents([new Date().getTime() - 86400000*90, Zeitgeist.MAX_TIMESTAMP],
 | 
			
		||||
                             [this._eventTemplate],
 | 
			
		||||
                             Zeitgeist.StorageState.ANY,
 | 
			
		||||
                             100,
 | 
			
		||||
                             Zeitgeist.ResultType.MOST_POPULAR_SUBJECTS,
 | 
			
		||||
                             Lang.bind(this, appendEvents));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _appendJumplistItem: function (event, type) {
 | 
			
		||||
        let info = new DocInfo.ZeitgeistItemInfo(event);
 | 
			
		||||
        let item = new PopupMenu.PopupImageMenuItem(info.name, type);
 | 
			
		||||
        this.addMenuItem(item);
 | 
			
		||||
        item.connect('activate', Lang.bind(this, function () {
 | 
			
		||||
            let app = new Gio.DesktopAppInfo.new(this._source.app.get_id());
 | 
			
		||||
            app.launch_uris([info.uri], null);
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _appendSeparator: function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,50 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const DocInfo = imports.misc.docInfo;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function DocSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocSearchProvider.prototype = {
 | 
			
		||||
    __proto__: Search.SearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(name) {
 | 
			
		||||
        Search.SearchProvider.prototype._init.call(this, _("RECENT ITEMS"));
 | 
			
		||||
        this._docManager = DocInfo.getDocManager();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMeta: function(resultId) {
 | 
			
		||||
        let docInfo = this._docManager.lookupByUri(resultId);
 | 
			
		||||
        if (!docInfo)
 | 
			
		||||
            return null;
 | 
			
		||||
        return { 'id': resultId,
 | 
			
		||||
                 'name': docInfo.name,
 | 
			
		||||
                 'createIcon': function(size) {
 | 
			
		||||
                                   return docInfo.createIcon(size);
 | 
			
		||||
                               }
 | 
			
		||||
               };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateResult: function(id, params) {
 | 
			
		||||
        params = Params.parse(params, { workspace: null,
 | 
			
		||||
                                        timestamp: null });
 | 
			
		||||
 | 
			
		||||
        let docInfo = this._docManager.lookupByUri(id);
 | 
			
		||||
        docInfo.launch(params.workspace ? params.workspace.index() : -1);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
        return this._docManager.initialSearch(terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, terms) {
 | 
			
		||||
        return this._docManager.subsearch(previousResults, terms);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -26,11 +26,13 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
 | 
			
		||||
                    'const Gtk = imports.gi.Gtk; ' +
 | 
			
		||||
                    'const Mainloop = imports.mainloop; ' +
 | 
			
		||||
                    'const Meta = imports.gi.Meta; ' +
 | 
			
		||||
                    'const Semantic = imports.misc.semantic' +
 | 
			
		||||
                    'const Shell = imports.gi.Shell; ' +
 | 
			
		||||
                    'const Tp = imports.gi.TelepathyGLib; ' +
 | 
			
		||||
                    'const Main = imports.ui.main; ' +
 | 
			
		||||
                    'const Lang = imports.lang; ' +
 | 
			
		||||
                    'const Tweener = imports.ui.tweener; ' +
 | 
			
		||||
                    'const Zeitgeist = imports.misc.zeitgeist; ' +
 | 
			
		||||
                    /* Utility functions...we should probably be able to use these
 | 
			
		||||
                     * in the shell core code too. */
 | 
			
		||||
                    'const stage = global.stage; ' +
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@ const Gdk = imports.gi.Gdk;
 | 
			
		||||
const AppDisplay = imports.ui.appDisplay;
 | 
			
		||||
const Dash = imports.ui.dash;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const DocDisplay = imports.ui.docDisplay;
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
@@ -25,6 +24,7 @@ const Tweener = imports.ui.tweener;
 | 
			
		||||
const ViewSelector = imports.ui.viewSelector;
 | 
			
		||||
const WorkspacesView = imports.ui.workspacesView;
 | 
			
		||||
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
 | 
			
		||||
const ZeitgeistSearch = imports.ui.zeitgeistSearch;
 | 
			
		||||
 | 
			
		||||
// Time for initial animation going into Overview mode
 | 
			
		||||
const ANIMATION_TIME = 0.25;
 | 
			
		||||
@@ -192,7 +192,11 @@ Overview.prototype = {
 | 
			
		||||
        this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new ZeitgeistSearch.DocumentsAsyncSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new ZeitgeistSearch.VideosAsyncSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new ZeitgeistSearch.MusicAsyncSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new ZeitgeistSearch.PicturesAsyncSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new ZeitgeistSearch.OtherAsyncSearchProvider());
 | 
			
		||||
 | 
			
		||||
        // TODO - recalculate everything when desktop size changes
 | 
			
		||||
        this.dash = new Dash.Dash();
 | 
			
		||||
 
 | 
			
		||||
@@ -115,6 +115,43 @@ function SearchProvider(title) {
 | 
			
		||||
SearchProvider.prototype = {
 | 
			
		||||
    _init: function(title) {
 | 
			
		||||
        this.title = title;
 | 
			
		||||
        this.searchSystem = null;
 | 
			
		||||
        this.searchAsync  = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _asyncCancelled: function() {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startAsync: function() {
 | 
			
		||||
        this.searchAsync = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    tryCancelAsync: function() {
 | 
			
		||||
        if (!this.searchAsync)
 | 
			
		||||
            return;
 | 
			
		||||
        this._asyncCancelled();
 | 
			
		||||
        this.searchAsync = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * addItems:
 | 
			
		||||
     * @items: an array of result identifier strings representing
 | 
			
		||||
     * items which match the last given search terms.
 | 
			
		||||
     *
 | 
			
		||||
     * This should be used for something that requires a bit more
 | 
			
		||||
     * logic; it's designed to be an asyncronous way to add a result
 | 
			
		||||
     * to the current search.
 | 
			
		||||
     */
 | 
			
		||||
    addItems: function( items) {
 | 
			
		||||
        if (!this.searchSystem)
 | 
			
		||||
            throw new Error('Search provider not registered');
 | 
			
		||||
 | 
			
		||||
        if (!items.length)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.tryCancelAsync();
 | 
			
		||||
 | 
			
		||||
        this.searchSystem.addProviderItems(this, items);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -212,6 +249,7 @@ SearchProvider.prototype = {
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(SearchProvider.prototype);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function OpenSearchSystem() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
@@ -324,6 +362,7 @@ SearchSystem.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    registerProvider: function (provider) {
 | 
			
		||||
        provider.searchSystem = this;
 | 
			
		||||
        this._providers.push(provider);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -340,30 +379,50 @@ SearchSystem.prototype = {
 | 
			
		||||
        this._previousResults = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addProviderItems: function(provider, items) {
 | 
			
		||||
        let index = this._providers.indexOf(provider);
 | 
			
		||||
        let [provider2, results] = this._previousResults[index];
 | 
			
		||||
        if (provider !== provider2)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        results.push.apply(results, items);
 | 
			
		||||
        this.emit('results-updated', this._previousResults);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSearch: function(searchString) {
 | 
			
		||||
        searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
 | 
			
		||||
        if (searchString == '')
 | 
			
		||||
            return [];
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        this.updateSearchResults(terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSearchResults: function(terms) {
 | 
			
		||||
        let isSubSearch = false;
 | 
			
		||||
 | 
			
		||||
        if (terms) {
 | 
			
		||||
            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;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            terms = this._previousTerms;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let results = [];
 | 
			
		||||
        if (isSubSearch) {
 | 
			
		||||
            for (let i = 0; i < this._previousResults.length; i++) {
 | 
			
		||||
            for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
                let [provider, previousResults] = this._previousResults[i];
 | 
			
		||||
                provider.tryCancelAsync();
 | 
			
		||||
                try {
 | 
			
		||||
                    let providerResults = provider.getSubsearchResultSet(previousResults, terms);
 | 
			
		||||
                    if (providerResults.length > 0)
 | 
			
		||||
                        results.push([provider, providerResults]);
 | 
			
		||||
                    results.push([provider, providerResults]);
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
 | 
			
		||||
                }
 | 
			
		||||
@@ -371,10 +430,10 @@ SearchSystem.prototype = {
 | 
			
		||||
        } else {
 | 
			
		||||
            for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
                let provider = this._providers[i];
 | 
			
		||||
                provider.tryCancelAsync();
 | 
			
		||||
                try {
 | 
			
		||||
                    let providerResults = provider.getInitialResultSet(terms);
 | 
			
		||||
                    if (providerResults.length > 0)
 | 
			
		||||
                        results.push([provider, providerResults]);
 | 
			
		||||
                    results.push([provider, providerResults]);
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
 | 
			
		||||
                }
 | 
			
		||||
@@ -383,8 +442,7 @@ SearchSystem.prototype = {
 | 
			
		||||
 | 
			
		||||
        this._previousTerms = terms;
 | 
			
		||||
        this._previousResults = results;
 | 
			
		||||
 | 
			
		||||
        return results;
 | 
			
		||||
    }
 | 
			
		||||
        this.emit('results-updated', results);
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(SearchSystem.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -182,6 +182,7 @@ function SearchResults(searchSystem, openSearchSystem) {
 | 
			
		||||
SearchResults.prototype = {
 | 
			
		||||
    _init: function(searchSystem, openSearchSystem) {
 | 
			
		||||
        this._searchSystem = searchSystem;
 | 
			
		||||
        this._searchSystem.connect('results-updated', Lang.bind(this, this._updateResults));
 | 
			
		||||
        this._openSearchSystem = openSearchSystem;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ name: 'searchResults',
 | 
			
		||||
@@ -216,9 +217,11 @@ SearchResults.prototype = {
 | 
			
		||||
        this._selectedProvider = -1;
 | 
			
		||||
        this._providers = this._searchSystem.getProviders();
 | 
			
		||||
        this._providerMeta = [];
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++)
 | 
			
		||||
        this._providerMetaResults = {};
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
            this.createProviderMeta(this._providers[i]);
 | 
			
		||||
 | 
			
		||||
            this._providerMetaResults[this.providers[i].title] = [];
 | 
			
		||||
        }
 | 
			
		||||
        this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
 | 
			
		||||
        this.actor.add(this._searchProvidersBox);
 | 
			
		||||
 | 
			
		||||
@@ -296,6 +299,12 @@ SearchResults.prototype = {
 | 
			
		||||
            meta.actor.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    
 | 
			
		||||
    _clearDisplayForProvider: function(index) {
 | 
			
		||||
        let meta = this._providerMeta[index];
 | 
			
		||||
        meta.resultDisplay.clear();
 | 
			
		||||
        meta.actor.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reset: function() {
 | 
			
		||||
        this._searchSystem.reset();
 | 
			
		||||
@@ -311,15 +320,15 @@ SearchResults.prototype = {
 | 
			
		||||
        this._statusText.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    doSearch: function (searchString) {
 | 
			
		||||
        this._searchSystem.updateSearch(searchString);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _metaForProvider: function(provider) {
 | 
			
		||||
        return this._providerMeta[this._providers.indexOf(provider)];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSearch: function (searchString) {
 | 
			
		||||
        let results = this._searchSystem.updateSearch(searchString);
 | 
			
		||||
 | 
			
		||||
        this._clearDisplay();
 | 
			
		||||
 | 
			
		||||
    _updateResults: function(searchSystem, results) {
 | 
			
		||||
        if (results.length == 0) {
 | 
			
		||||
            this._statusText.set_text(_("No matching results."));
 | 
			
		||||
            this._statusText.show();
 | 
			
		||||
@@ -329,14 +338,22 @@ SearchResults.prototype = {
 | 
			
		||||
            this._statusText.hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let terms = this._searchSystem.getTerms();
 | 
			
		||||
        let terms = searchSystem.getTerms();
 | 
			
		||||
        this._openSearchSystem.setSearchTerms(terms);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < results.length; i++) {
 | 
			
		||||
            let [provider, providerResults] = results[i];
 | 
			
		||||
            let meta = this._metaForProvider(provider);
 | 
			
		||||
            meta.actor.show();
 | 
			
		||||
            meta.resultDisplay.renderResults(providerResults, terms);
 | 
			
		||||
            if (providerResults.length == 0)
 | 
			
		||||
                this._clearDisplayForProvider(i)
 | 
			
		||||
            else {
 | 
			
		||||
                if (this._providerMetaResults[provider.title] != providerResults) {
 | 
			
		||||
                    this._providerMetaResults[provider.title] = providerResults;
 | 
			
		||||
                    this._clearDisplayForProvider(i);
 | 
			
		||||
                    let meta = this._metaForProvider(provider);
 | 
			
		||||
                    meta.actor.show();
 | 
			
		||||
                    meta.resultDisplay.renderResults(providerResults, terms);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._selectedOpenSearchButton == -1)
 | 
			
		||||
 
 | 
			
		||||
@@ -290,7 +290,7 @@ SearchTab.prototype = {
 | 
			
		||||
    _doSearch: function () {
 | 
			
		||||
        this._searchTimeoutId = 0;
 | 
			
		||||
        let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
 | 
			
		||||
        this._searchResults.updateSearch(text);
 | 
			
		||||
        this._searchResults.doSearch(text);
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										202
									
								
								js/ui/zeitgeistSearch.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								js/ui/zeitgeistSearch.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2010 Seif Lotfy <seif@lotfy.com>
 | 
			
		||||
 * Copyright (C) 2011 Siegfried-Angel Gevatter Pujals <siegfried@gevatter.com>
 | 
			
		||||
 * Copyright (C) 2010-2011 Collabora Ltd.
 | 
			
		||||
 *     Authored by: Seif Lotfy <seif@lotfy.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio
 | 
			
		||||
const Semantic = imports.misc.semantic;
 | 
			
		||||
const Zeitgeist = imports.misc.zeitgeist;
 | 
			
		||||
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const DocInfo = imports.misc.docInfo;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
// FIXME: The subject cache is never being emptied.
 | 
			
		||||
let ZeitgeistSubjectCache = {};
 | 
			
		||||
 | 
			
		||||
function ZeitgeistAsyncSearchProvider(title, interpretations) {
 | 
			
		||||
    this._init(title, interpretations);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ZeitgeistAsyncSearchProvider.prototype = {
 | 
			
		||||
    __proto__: Search.SearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(title, interpretations) {
 | 
			
		||||
        Search.SearchProvider.prototype._init.call(this, title);
 | 
			
		||||
        this._buildTemplates(interpretations);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildTemplates: function(interpretations) {
 | 
			
		||||
        this.templates = [];
 | 
			
		||||
        for (let i = 0; i < interpretations.length; i++) {
 | 
			
		||||
            let subject = new Zeitgeist.Subject('', interpretations[i], '', '', '', '', '');
 | 
			
		||||
            let event = new Zeitgeist.Event('', '', '', [subject], []);
 | 
			
		||||
            this.templates.push(event);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _search: function(terms) {
 | 
			
		||||
        this._search_terms = terms;
 | 
			
		||||
        Zeitgeist.fullTextSearch(terms[0]+'*',
 | 
			
		||||
                                 this.templates,
 | 
			
		||||
                                 Lang.bind(this, function(events) {
 | 
			
		||||
                                     if (terms == this._search_terms)
 | 
			
		||||
                                         this._asyncCallback(events);
 | 
			
		||||
                                 }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _asyncCancelled: function() {
 | 
			
		||||
        this._search_terms = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
        this._search(terms);
 | 
			
		||||
        return [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, terms) {
 | 
			
		||||
        this.tryCancelAsync();
 | 
			
		||||
        return this.getInitialResultSet(terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMeta: function(resultId) {
 | 
			
		||||
        return { 'id': ZeitgeistSubjectCache[resultId].uri,
 | 
			
		||||
                 'name': ZeitgeistSubjectCache[resultId].name,
 | 
			
		||||
                 'createIcon': function (size) {
 | 
			
		||||
                                   return ZeitgeistSubjectCache[resultId].createIcon(size);
 | 
			
		||||
                               },
 | 
			
		||||
               };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateResult: function(resultId) {
 | 
			
		||||
        Gio.app_info_launch_default_for_uri(resultId,
 | 
			
		||||
                                            global.create_app_launch_context());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _asyncCallback: function(events) {
 | 
			
		||||
        let items = [];
 | 
			
		||||
        for (let i = 0; i < events.length; i++) {
 | 
			
		||||
            let event = events[i];
 | 
			
		||||
            let subject = event.subjects[0];
 | 
			
		||||
            let uri = subject.uri.replace('file://', '');
 | 
			
		||||
            uri = GLib.uri_unescape_string(uri, '');
 | 
			
		||||
            if (GLib.file_test(uri, GLib.FileTest.EXISTS)) {
 | 
			
		||||
                if (!ZeitgeistSubjectCache.hasOwnProperty(subject.uri)) {
 | 
			
		||||
                    let info = new DocInfo.ZeitgeistItemInfo(event);
 | 
			
		||||
                    ZeitgeistSubjectCache[info.uri] = info;
 | 
			
		||||
                }
 | 
			
		||||
                items.push(subject.uri);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.addItems(items);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function DocumentsAsyncSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocumentsAsyncSearchProvider.prototype = {
 | 
			
		||||
    __proto__: ZeitgeistAsyncSearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        let interpretations = [Semantic.NFO_DOCUMENT];
 | 
			
		||||
        ZeitgeistAsyncSearchProvider.prototype._init.call(this, _("DOCUMENTS"), interpretations);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function VideosAsyncSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VideosAsyncSearchProvider.prototype = {
 | 
			
		||||
    __proto__: ZeitgeistAsyncSearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        let interpretations = [Semantic.NFO_VIDEO];
 | 
			
		||||
        ZeitgeistAsyncSearchProvider.prototype._init.call(this, _("VIDEOS"), interpretations);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function MusicAsyncSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MusicAsyncSearchProvider.prototype = {
 | 
			
		||||
    __proto__: ZeitgeistAsyncSearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        let interpretations = [
 | 
			
		||||
            Semantic.NFO_AUDIO,
 | 
			
		||||
            Semantic.NMM_MUSIC_PIECE];
 | 
			
		||||
        ZeitgeistAsyncSearchProvider.prototype._init.call(this, _("MUSIC"), interpretations);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function PicturesAsyncSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PicturesAsyncSearchProvider.prototype = {
 | 
			
		||||
    __proto__: ZeitgeistAsyncSearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        let interpretations = [Semantic.NFO_IMAGE];
 | 
			
		||||
        ZeitgeistAsyncSearchProvider.prototype._init.call(this, _("PICTURES"), interpretations);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function OtherAsyncSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OtherAsyncSearchProvider.prototype = {
 | 
			
		||||
    __proto__: ZeitgeistAsyncSearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        let interpretations = [
 | 
			
		||||
            '!' + Semantic.NFO_IMAGE,
 | 
			
		||||
            '!' + Semantic.NFO_DOCUMENT,
 | 
			
		||||
            '!' + Semantic.NFO_VIDEO,
 | 
			
		||||
            '!' + Semantic.NFO_AUDIO,
 | 
			
		||||
            '!' + Semantic.NMM_MUSIC_PIECE];
 | 
			
		||||
        ZeitgeistAsyncSearchProvider.prototype._init.call(this, _("OTHER"), interpretations);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildTemplates: function(interpretations) {
 | 
			
		||||
        // Here we want to get everything matching all of the templates, and
 | 
			
		||||
        // not just any of them. Therefore we need to AND the interpretations
 | 
			
		||||
        // instead of OR'ing them; this is done by having an Event with
 | 
			
		||||
        // different Subjects.
 | 
			
		||||
        this.templates = [];
 | 
			
		||||
        let subjects = [];
 | 
			
		||||
        for (let i = 0; i < interpretations.length; i++) {
 | 
			
		||||
            let subject = new Zeitgeist.Subject('', interpretations[i], '', '', '', '', '');
 | 
			
		||||
            subjects.push(subject);
 | 
			
		||||
        }
 | 
			
		||||
        let event = new Zeitgeist.Event('', '', '', subjects, []);
 | 
			
		||||
        this.templates.push(event);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user