2008-12-20 04:27:57 +00:00
|
|
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
|
|
|
2009-08-17 02:23:44 +00:00
|
|
|
const Big = imports.gi.Big;
|
2008-12-20 04:27:57 +00:00
|
|
|
const Clutter = imports.gi.Clutter;
|
|
|
|
const Gio = imports.gi.Gio;
|
|
|
|
const Gtk = imports.gi.Gtk;
|
2009-02-10 19:12:13 +00:00
|
|
|
const Lang = imports.lang;
|
2009-08-17 02:23:44 +00:00
|
|
|
const Pango = imports.gi.Pango;
|
2008-12-20 04:27:57 +00:00
|
|
|
const Shell = imports.gi.Shell;
|
2009-02-02 23:02:16 +00:00
|
|
|
const Signals = imports.signals;
|
2009-11-06 21:08:07 +00:00
|
|
|
const St = imports.gi.St;
|
2009-07-22 16:46:05 +00:00
|
|
|
const Mainloop = imports.mainloop;
|
2008-12-20 04:27:57 +00:00
|
|
|
|
2009-06-16 16:20:12 +00:00
|
|
|
const DocInfo = imports.misc.docInfo;
|
2009-08-17 02:23:44 +00:00
|
|
|
const DND = imports.ui.dnd;
|
2008-12-20 04:27:57 +00:00
|
|
|
const GenericDisplay = imports.ui.genericDisplay;
|
2009-05-01 18:13:51 +00:00
|
|
|
const Main = imports.ui.main;
|
2008-12-20 04:27:57 +00:00
|
|
|
|
2009-12-08 02:48:31 +00:00
|
|
|
const MAX_DASH_DOCS = 50;
|
2009-08-17 02:23:44 +00:00
|
|
|
const DASH_DOCS_ICON_SIZE = 16;
|
|
|
|
|
|
|
|
const DEFAULT_SPACING = 4;
|
|
|
|
|
2008-12-20 04:27:57 +00:00
|
|
|
/* This class represents a single display item containing information about a document.
|
2009-07-22 22:57:05 +00:00
|
|
|
* We take the current number of seconds in the constructor to avoid looking up the current
|
|
|
|
* time for every item when they are created in a batch.
|
2008-12-20 04:27:57 +00:00
|
|
|
*
|
2009-06-16 16:20:12 +00:00
|
|
|
* docInfo - DocInfo object containing information about the document
|
2009-07-20 21:56:47 +00:00
|
|
|
* currentSeconds - current number of seconds since the epoch
|
2008-12-20 04:27:57 +00:00
|
|
|
*/
|
Rewrite Dash, remove hardcoded width/height from GenericDisplay
This patch is a near-total rewrite of the Dash. First, the dash
code moves into a separate file, dash.js.
Inside dash.js, the components are more broken up into separate
classes; in particular there's now a Pane class and a MoreLink
class. Instead of each section of the dash, when activated,
attempting to close all N-1 other sections, instead there
is the concept of a single "active pane", and when e.g. activating
the More link for documents, if we know there's an active pane
which happens to be the apps, close it.
Many redundant containers were removed from the dash, and all
manual width, height and x/y offsets are entirely gone. We move
the visual apperance closer to the design by using the view-more.svg,
etc.
To complete the removal of height/width calculations from the dash,
we also had to do the same for GenericDisplay. Also clean up
the positioning inside overlay.js so calculation of children's
positioning is inside a single function that flows from screen.width
and screen.height, so in the future we can stop passing the width
into the Dash constructor and call this once and work on screen
resizing.
2009-08-01 02:12:01 +00:00
|
|
|
function DocDisplayItem(docInfo, currentSecs) {
|
|
|
|
this._init(docInfo, currentSecs);
|
2008-12-20 04:27:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DocDisplayItem.prototype = {
|
|
|
|
__proto__: GenericDisplay.GenericDisplayItem.prototype,
|
|
|
|
|
Rewrite Dash, remove hardcoded width/height from GenericDisplay
This patch is a near-total rewrite of the Dash. First, the dash
code moves into a separate file, dash.js.
Inside dash.js, the components are more broken up into separate
classes; in particular there's now a Pane class and a MoreLink
class. Instead of each section of the dash, when activated,
attempting to close all N-1 other sections, instead there
is the concept of a single "active pane", and when e.g. activating
the More link for documents, if we know there's an active pane
which happens to be the apps, close it.
Many redundant containers were removed from the dash, and all
manual width, height and x/y offsets are entirely gone. We move
the visual apperance closer to the design by using the view-more.svg,
etc.
To complete the removal of height/width calculations from the dash,
we also had to do the same for GenericDisplay. Also clean up
the positioning inside overlay.js so calculation of children's
positioning is inside a single function that flows from screen.width
and screen.height, so in the future we can stop passing the width
into the Dash constructor and call this once and work on screen
resizing.
2009-08-01 02:12:01 +00:00
|
|
|
_init : function(docInfo, currentSecs) {
|
|
|
|
GenericDisplay.GenericDisplayItem.prototype._init.call(this);
|
2008-12-20 04:27:57 +00:00
|
|
|
this._docInfo = docInfo;
|
Rewrite Dash, remove hardcoded width/height from GenericDisplay
This patch is a near-total rewrite of the Dash. First, the dash
code moves into a separate file, dash.js.
Inside dash.js, the components are more broken up into separate
classes; in particular there's now a Pane class and a MoreLink
class. Instead of each section of the dash, when activated,
attempting to close all N-1 other sections, instead there
is the concept of a single "active pane", and when e.g. activating
the More link for documents, if we know there's an active pane
which happens to be the apps, close it.
Many redundant containers were removed from the dash, and all
manual width, height and x/y offsets are entirely gone. We move
the visual apperance closer to the design by using the view-more.svg,
etc.
To complete the removal of height/width calculations from the dash,
we also had to do the same for GenericDisplay. Also clean up
the positioning inside overlay.js so calculation of children's
positioning is inside a single function that flows from screen.width
and screen.height, so in the future we can stop passing the width
into the Dash constructor and call this once and work on screen
resizing.
2009-08-01 02:12:01 +00:00
|
|
|
|
2009-06-29 19:08:48 +00:00
|
|
|
this._setItemInfo(docInfo.name, "");
|
2009-07-22 22:57:05 +00:00
|
|
|
|
|
|
|
this._timeoutTime = -1;
|
2009-07-20 21:56:47 +00:00
|
|
|
this._resetTimeDisplay(currentSecs);
|
2008-12-20 04:27:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
//// Public methods ////
|
|
|
|
|
2009-07-22 22:57:05 +00:00
|
|
|
getUpdateTimeoutTime: function() {
|
|
|
|
return this._timeoutTime;
|
2009-07-20 21:56:47 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Update any relative-time based displays for this item.
|
|
|
|
redisplay: function(currentSecs) {
|
|
|
|
this._resetTimeDisplay(currentSecs);
|
|
|
|
},
|
|
|
|
|
2008-12-20 04:27:57 +00:00
|
|
|
//// Public method overrides ////
|
|
|
|
|
|
|
|
// Opens a document represented by this display item.
|
|
|
|
launch : function() {
|
2009-06-16 16:20:12 +00:00
|
|
|
this._docInfo.launch();
|
2009-03-20 16:06:34 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
//// Protected method overrides ////
|
2008-12-20 04:27:57 +00:00
|
|
|
|
2009-06-29 19:08:48 +00:00
|
|
|
// Returns an icon for the item.
|
|
|
|
_createIcon : function() {
|
|
|
|
return this._docInfo.createIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
|
|
|
|
},
|
|
|
|
|
2009-07-29 21:47:50 +00:00
|
|
|
// Returns a preview icon for the item.
|
|
|
|
_createPreviewIcon : function() {
|
|
|
|
return this._docInfo.createIcon(GenericDisplay.PREVIEW_ICON_SIZE);
|
2009-04-28 19:35:36 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Creates and returns a large preview icon, but only if this._docInfo is an image file
|
|
|
|
// and we were able to generate a pixbuf from it successfully.
|
2009-08-09 23:48:54 +00:00
|
|
|
_createLargePreviewIcon : function() {
|
2009-06-16 16:20:12 +00:00
|
|
|
if (this._docInfo.mimeType == null || this._docInfo.mimeType.indexOf("image/") != 0)
|
2009-04-28 19:35:36 +00:00
|
|
|
return null;
|
|
|
|
|
2009-08-11 23:30:58 +00:00
|
|
|
try {
|
|
|
|
return Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.NONE,
|
2009-08-09 23:48:54 +00:00
|
|
|
this._docInfo.uri, -1, -1);
|
2009-08-11 23:30:58 +00:00
|
|
|
} catch (e) {
|
|
|
|
// An exception will be raised when the image format isn't know
|
|
|
|
/* FIXME: http://bugzilla.gnome.org/show_bug.cgi?id=591480: should
|
|
|
|
* only ignore GDK_PIXBUF_ERROR_UNKNOWN_TYPE. */
|
|
|
|
return null;
|
|
|
|
}
|
2009-07-20 21:56:47 +00:00
|
|
|
},
|
|
|
|
|
2009-08-18 00:29:54 +00:00
|
|
|
//// Drag and Drop ////
|
|
|
|
|
|
|
|
shellWorkspaceLaunch: function() {
|
|
|
|
this.launch();
|
|
|
|
},
|
|
|
|
|
2009-07-20 21:56:47 +00:00
|
|
|
//// Private Methods ////
|
|
|
|
|
2009-08-18 00:29:54 +00:00
|
|
|
// Updates the last visited time displayed in the description text for the item.
|
2009-07-20 21:56:47 +00:00
|
|
|
_resetTimeDisplay: function(currentSecs) {
|
2009-07-25 15:00:37 +00:00
|
|
|
let lastSecs = this._docInfo.timestamp;
|
2009-07-20 21:56:47 +00:00
|
|
|
let timeDelta = currentSecs - lastSecs;
|
2009-09-11 21:23:23 +00:00
|
|
|
let [text, nextUpdate] = global.format_time_relative_pretty(timeDelta);
|
2009-07-22 22:57:05 +00:00
|
|
|
this._timeoutTime = currentSecs + nextUpdate;
|
2009-07-20 21:56:47 +00:00
|
|
|
this._setDescriptionText(text);
|
2009-03-20 16:06:34 +00:00
|
|
|
}
|
2008-12-20 04:27:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* This class represents a display containing a collection of document items.
|
|
|
|
* The documents are sorted by how recently they were last visited.
|
|
|
|
*/
|
2009-11-03 23:36:44 +00:00
|
|
|
function DocDisplay(flags) {
|
|
|
|
this._init(flags);
|
2009-07-04 02:32:23 +00:00
|
|
|
}
|
2008-12-20 04:27:57 +00:00
|
|
|
|
|
|
|
DocDisplay.prototype = {
|
|
|
|
__proto__: GenericDisplay.GenericDisplay.prototype,
|
|
|
|
|
2009-11-03 23:36:44 +00:00
|
|
|
_init : function(flags) {
|
|
|
|
GenericDisplay.GenericDisplay.prototype._init.call(this, flags);
|
2009-07-22 22:57:05 +00:00
|
|
|
// We keep a single timeout callback for updating last visited times
|
|
|
|
// for all the items in the display. This avoids creating individual
|
|
|
|
// callbacks for each item in the display. So proper time updates
|
2009-11-03 23:36:44 +00:00
|
|
|
// for individual items and item details depend on the item being
|
2009-07-22 22:57:05 +00:00
|
|
|
// associated with one of the displays.
|
|
|
|
this._updateTimeoutTargetTime = -1;
|
2009-07-20 21:56:47 +00:00
|
|
|
this._updateTimeoutId = 0;
|
|
|
|
|
2009-08-17 02:23:44 +00:00
|
|
|
this._docManager = DocInfo.getDocManager();
|
2008-12-20 04:27:57 +00:00
|
|
|
this._docsStale = true;
|
2009-09-15 17:09:51 +00:00
|
|
|
this._docManager.connect('changed', Lang.bind(this, function(mgr, userData) {
|
|
|
|
this._docsStale = true;
|
2009-08-11 11:46:10 +00:00
|
|
|
// Changes in local recent files should not happen when we are in the Overview mode,
|
2008-12-20 04:27:57 +00:00
|
|
|
// but redisplaying right away is cool when we use Zephyr.
|
|
|
|
// Also, we might be displaying remote documents, like Google Docs, in the future
|
|
|
|
// which might be edited by someone else.
|
2009-10-01 21:41:17 +00:00
|
|
|
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
|
2009-09-15 17:09:51 +00:00
|
|
|
}));
|
2009-07-20 21:56:47 +00:00
|
|
|
|
|
|
|
this.connect('destroy', Lang.bind(this, function (o) {
|
|
|
|
if (this._updateTimeoutId > 0)
|
|
|
|
Mainloop.source_remove(this._updateTimeoutId);
|
|
|
|
}));
|
2008-12-20 04:27:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
//// Protected method overrides ////
|
|
|
|
|
|
|
|
// Gets the list of recent items from the recent items manager.
|
|
|
|
_refreshCache : function() {
|
|
|
|
if (!this._docsStale)
|
2009-09-24 22:36:36 +00:00
|
|
|
return true;
|
2009-11-29 22:43:25 +00:00
|
|
|
this._allItems = {};
|
|
|
|
Lang.copyProperties(this._docManager.getInfosByUri(), this._allItems);
|
2008-12-20 04:27:57 +00:00
|
|
|
this._docsStale = false;
|
2009-09-24 22:36:36 +00:00
|
|
|
return false;
|
2008-12-20 04:27:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Sets the list of the displayed items based on how recently they were last visited.
|
|
|
|
_setDefaultList : function() {
|
|
|
|
// It seems to be an implementation detail of the Mozilla JavaScript that object
|
|
|
|
// properties are returned during the iteration in the same order in which they were
|
|
|
|
// defined, but it is not a guarantee according to this
|
|
|
|
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Statements/for...in
|
2009-01-08 23:31:23 +00:00
|
|
|
// While this._allItems associative array seems to always be ordered by last added,
|
2008-12-20 04:27:57 +00:00
|
|
|
// as the results of this._recentManager.get_items() based on which it is constructed are,
|
2009-01-08 23:31:23 +00:00
|
|
|
// we should do the sorting manually because we want the order to be based on last visited.
|
2009-01-09 01:09:35 +00:00
|
|
|
//
|
|
|
|
// This function is called each time the search string is set back to '' or we display
|
2009-08-11 11:46:10 +00:00
|
|
|
// the Overview, so we are doing the sorting over the same items multiple times if the list
|
2009-01-09 01:09:35 +00:00
|
|
|
// of recent items didn't change. We could store an additional array of doc ids and sort
|
|
|
|
// them once when they are returned by this._recentManager.get_items() to avoid having to do
|
|
|
|
// this sorting each time, but the sorting seems to be very fast anyway, so there is no need
|
|
|
|
// to introduce an additional class variable.
|
2009-09-24 22:36:36 +00:00
|
|
|
this._matchedItems = {};
|
|
|
|
this._matchedItemKeys = [];
|
2009-03-09 20:52:11 +00:00
|
|
|
let docIdsToRemove = [];
|
2008-12-20 04:27:57 +00:00
|
|
|
for (docId in this._allItems) {
|
2009-03-09 20:52:11 +00:00
|
|
|
// this._allItems[docId].exists() checks if the resource still exists
|
2009-09-24 22:36:36 +00:00
|
|
|
if (this._allItems[docId].exists()) {
|
|
|
|
this._matchedItems[docId] = 1;
|
|
|
|
this._matchedItemKeys.push(docId);
|
|
|
|
} else {
|
2009-03-09 20:52:11 +00:00
|
|
|
docIdsToRemove.push(docId);
|
2009-09-24 22:36:36 +00:00
|
|
|
}
|
2008-12-20 04:27:57 +00:00
|
|
|
}
|
2009-03-09 20:52:11 +00:00
|
|
|
|
|
|
|
for (docId in docIdsToRemove) {
|
|
|
|
delete this._allItems[docId];
|
2008-12-20 04:27:57 +00:00
|
|
|
}
|
2009-03-09 20:52:11 +00:00
|
|
|
|
2009-09-24 22:36:36 +00:00
|
|
|
this._matchedItemKeys.sort(Lang.bind(this, this._compareItems));
|
2008-12-20 04:27:57 +00:00
|
|
|
},
|
|
|
|
|
2009-02-10 19:12:13 +00:00
|
|
|
// Compares items associated with the item ids based on how recently the items
|
|
|
|
// were last visited.
|
|
|
|
// Returns an integer value indicating the result of the comparison.
|
|
|
|
_compareItems : function(itemIdA, itemIdB) {
|
|
|
|
let docA = this._allItems[itemIdA];
|
|
|
|
let docB = this._allItems[itemIdB];
|
2009-06-16 16:20:12 +00:00
|
|
|
|
2009-07-25 15:00:37 +00:00
|
|
|
return docB.timestamp - docA.timestamp;
|
2008-12-20 04:27:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Checks if the item info can be a match for the search string by checking
|
|
|
|
// the name of the document. Item info is expected to be GtkRecentInfo.
|
|
|
|
// Returns a boolean flag indicating if itemInfo is a match.
|
|
|
|
_isInfoMatching : function(itemInfo, search) {
|
2009-01-08 23:31:23 +00:00
|
|
|
if (!itemInfo.exists())
|
|
|
|
return false;
|
|
|
|
|
2008-12-20 04:27:57 +00:00
|
|
|
if (search == null || search == '')
|
|
|
|
return true;
|
2009-01-08 23:31:23 +00:00
|
|
|
|
2009-06-16 16:20:12 +00:00
|
|
|
let name = itemInfo.name.toLowerCase();
|
2008-12-20 04:27:57 +00:00
|
|
|
if (name.indexOf(search) >= 0)
|
|
|
|
return true;
|
|
|
|
// TODO: we can also check doc URIs, so that
|
|
|
|
// if you search for a directory name, we display recent files from it
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
2009-06-16 16:20:12 +00:00
|
|
|
// Creates a DocDisplayItem based on itemInfo, which is expected to be a DocInfo object.
|
Rewrite Dash, remove hardcoded width/height from GenericDisplay
This patch is a near-total rewrite of the Dash. First, the dash
code moves into a separate file, dash.js.
Inside dash.js, the components are more broken up into separate
classes; in particular there's now a Pane class and a MoreLink
class. Instead of each section of the dash, when activated,
attempting to close all N-1 other sections, instead there
is the concept of a single "active pane", and when e.g. activating
the More link for documents, if we know there's an active pane
which happens to be the apps, close it.
Many redundant containers were removed from the dash, and all
manual width, height and x/y offsets are entirely gone. We move
the visual apperance closer to the design by using the view-more.svg,
etc.
To complete the removal of height/width calculations from the dash,
we also had to do the same for GenericDisplay. Also clean up
the positioning inside overlay.js so calculation of children's
positioning is inside a single function that flows from screen.width
and screen.height, so in the future we can stop passing the width
into the Dash constructor and call this once and work on screen
resizing.
2009-08-01 02:12:01 +00:00
|
|
|
_createDisplayItem: function(itemInfo) {
|
2009-07-20 21:56:47 +00:00
|
|
|
let currentSecs = new Date().getTime() / 1000;
|
Rewrite Dash, remove hardcoded width/height from GenericDisplay
This patch is a near-total rewrite of the Dash. First, the dash
code moves into a separate file, dash.js.
Inside dash.js, the components are more broken up into separate
classes; in particular there's now a Pane class and a MoreLink
class. Instead of each section of the dash, when activated,
attempting to close all N-1 other sections, instead there
is the concept of a single "active pane", and when e.g. activating
the More link for documents, if we know there's an active pane
which happens to be the apps, close it.
Many redundant containers were removed from the dash, and all
manual width, height and x/y offsets are entirely gone. We move
the visual apperance closer to the design by using the view-more.svg,
etc.
To complete the removal of height/width calculations from the dash,
we also had to do the same for GenericDisplay. Also clean up
the positioning inside overlay.js so calculation of children's
positioning is inside a single function that flows from screen.width
and screen.height, so in the future we can stop passing the width
into the Dash constructor and call this once and work on screen
resizing.
2009-08-01 02:12:01 +00:00
|
|
|
let docDisplayItem = new DocDisplayItem(itemInfo, currentSecs);
|
2009-07-22 22:57:05 +00:00
|
|
|
this._updateTimeoutCallback(docDisplayItem, currentSecs);
|
|
|
|
return docDisplayItem;
|
2009-07-20 21:56:47 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
//// Private Methods ////
|
2009-07-22 22:57:05 +00:00
|
|
|
|
|
|
|
// A callback function that redisplays the items, updating their descriptions,
|
|
|
|
// and sets up a new timeout callback.
|
2009-07-20 21:56:47 +00:00
|
|
|
_docTimeout: function () {
|
|
|
|
let currentSecs = new Date().getTime() / 1000;
|
2009-07-22 22:57:05 +00:00
|
|
|
this._updateTimeoutId = 0;
|
|
|
|
this._updateTimeoutTargetTime = -1;
|
2009-07-22 16:46:05 +00:00
|
|
|
for (let docId in this._displayedItems) {
|
2009-07-22 22:57:05 +00:00
|
|
|
let docDisplayItem = this._displayedItems[docId];
|
|
|
|
docDisplayItem.redisplay(currentSecs);
|
|
|
|
this._updateTimeoutCallback(docDisplayItem, currentSecs);
|
2009-07-20 21:56:47 +00:00
|
|
|
}
|
2009-07-24 16:58:53 +00:00
|
|
|
return false;
|
2009-07-22 22:57:05 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Updates the timeout callback if the timeout time for the docDisplayItem
|
|
|
|
// is earlier than the target time for the current timeout callback.
|
|
|
|
_updateTimeoutCallback: function (docDisplayItem, currentSecs) {
|
|
|
|
let timeoutTime = docDisplayItem.getUpdateTimeoutTime();
|
|
|
|
if (this._updateTimeoutTargetTime < 0 || timeoutTime < this._updateTimeoutTargetTime) {
|
|
|
|
if (this._updateTimeoutId > 0)
|
|
|
|
Mainloop.source_remove(this._updateTimeoutId);
|
|
|
|
this._updateTimeoutId = Mainloop.timeout_add_seconds(timeoutTime - currentSecs, Lang.bind(this, this._docTimeout));
|
|
|
|
this._updateTimeoutTargetTime = timeoutTime;
|
|
|
|
}
|
2009-07-04 19:30:12 +00:00
|
|
|
}
|
2008-12-20 04:27:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Signals.addSignalMethods(DocDisplay.prototype);
|
2009-08-17 02:23:44 +00:00
|
|
|
|
|
|
|
function DashDocDisplayItem(docInfo) {
|
|
|
|
this._init(docInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
DashDocDisplayItem.prototype = {
|
|
|
|
_init: function(docInfo) {
|
|
|
|
this._info = docInfo;
|
|
|
|
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
|
|
|
spacing: DEFAULT_SPACING,
|
|
|
|
reactive: true });
|
|
|
|
this.actor.connect('button-release-event', Lang.bind(this, function () {
|
|
|
|
docInfo.launch();
|
|
|
|
Main.overview.hide();
|
|
|
|
}));
|
|
|
|
|
2009-11-29 22:43:25 +00:00
|
|
|
this.actor._delegate = this;
|
|
|
|
|
2009-08-17 02:23:44 +00:00
|
|
|
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);
|
2009-11-29 22:43:25 +00:00
|
|
|
let name = new St.Label({ style_class: 'dash-recent-docs-item',
|
2009-11-06 21:08:07 +00:00
|
|
|
text: docInfo.name });
|
2009-08-17 02:23:44 +00:00
|
|
|
this.actor.append(name, Big.BoxPackFlags.EXPAND);
|
|
|
|
|
|
|
|
let draggable = DND.makeDraggable(this.actor);
|
2009-11-29 22:43:25 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
getUri: function() {
|
|
|
|
return this._info.uri;
|
2009-08-17 02:23:44 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
getDragActorSource: function() {
|
|
|
|
return this._icon;
|
|
|
|
},
|
|
|
|
|
|
|
|
getDragActor: function(stageX, stageY) {
|
|
|
|
this.dragActor = this._info.createIcon(DASH_DOCS_ICON_SIZE);
|
|
|
|
return this.dragActor;
|
|
|
|
},
|
|
|
|
|
|
|
|
//// Drag and drop functions ////
|
|
|
|
|
|
|
|
shellWorkspaceLaunch: function () {
|
|
|
|
this._info.launch();
|
|
|
|
}
|
2009-09-25 18:54:40 +00:00
|
|
|
};
|
2009-08-17 02:23:44 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Class used to display two column recent documents in the dash
|
|
|
|
*/
|
|
|
|
function DashDocDisplay() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
DashDocDisplay.prototype = {
|
|
|
|
_init: function() {
|
|
|
|
this.actor = new Shell.GenericContainer();
|
|
|
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
|
|
|
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
|
|
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
2009-11-29 22:43:25 +00:00
|
|
|
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
|
|
|
|
|
|
|
this._actorsByUri = {};
|
2009-08-17 02:23:44 +00:00
|
|
|
|
|
|
|
this._docManager = DocInfo.getDocManager();
|
2009-11-29 22:43:25 +00:00
|
|
|
this._docManager.connect('changed', Lang.bind(this, this._onDocsChanged));
|
|
|
|
this._pendingDocsChange = true;
|
|
|
|
this._checkDocExistence = false;
|
2009-08-17 02:23:44 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
|
|
|
let children = actor.get_children();
|
|
|
|
|
|
|
|
// We use two columns maximum. Just take the min and natural size of the
|
|
|
|
// first two items, even though strictly speaking it's not correct; we'd
|
|
|
|
// need to calculate how many items we could fit for the height, then
|
|
|
|
// take the biggest preferred width for each column.
|
|
|
|
// In practice the dash gets a fixed width anyways.
|
|
|
|
|
|
|
|
// If we have one child, add its minimum and natural size
|
|
|
|
if (children.length > 0) {
|
|
|
|
let [minSize, naturalSize] = children[0].get_preferred_width(forHeight);
|
|
|
|
alloc.min_size += minSize;
|
|
|
|
alloc.natural_size += naturalSize;
|
|
|
|
}
|
|
|
|
// If we have two, add its size, plus DEFAULT_SPACING
|
|
|
|
if (children.length > 1) {
|
|
|
|
let [minSize, naturalSize] = children[1].get_preferred_width(forHeight);
|
|
|
|
alloc.min_size += DEFAULT_SPACING + minSize;
|
|
|
|
alloc.natural_size += DEFAULT_SPACING + naturalSize;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_getPreferredHeight: function(actor, forWidth, alloc) {
|
|
|
|
let children = actor.get_children();
|
|
|
|
|
|
|
|
// Two columns, where we go vertically down first. So just take
|
|
|
|
// the height of half of the children as our preferred height.
|
|
|
|
|
2009-10-01 13:18:05 +00:00
|
|
|
let firstColumnChildren = Math.ceil(children.length / 2);
|
2009-08-17 02:23:44 +00:00
|
|
|
|
2009-11-29 22:43:25 +00:00
|
|
|
let natural = 0;
|
2009-08-17 02:23:44 +00:00
|
|
|
for (let i = 0; i < firstColumnChildren; i++) {
|
|
|
|
let child = children[i];
|
2009-11-29 22:43:25 +00:00
|
|
|
let [minSize, naturalSize] = child.get_preferred_height(-1);
|
|
|
|
natural += naturalSize;
|
2009-08-17 02:23:44 +00:00
|
|
|
|
|
|
|
if (i > 0 && i < children.length - 1) {
|
2009-11-29 22:43:25 +00:00
|
|
|
natural += DEFAULT_SPACING;
|
2009-08-17 02:23:44 +00:00
|
|
|
}
|
|
|
|
}
|
2009-11-29 22:43:25 +00:00
|
|
|
alloc.natural_size = natural;
|
2009-08-17 02:23:44 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_allocate: function(actor, box, flags) {
|
|
|
|
let width = box.x2 - box.x1;
|
|
|
|
let height = box.y2 - box.y1;
|
|
|
|
|
|
|
|
let children = actor.get_children();
|
|
|
|
|
|
|
|
// The width of an item is our allocated width, minus spacing, divided in half.
|
|
|
|
let itemWidth = Math.floor((width - DEFAULT_SPACING) / 2);
|
|
|
|
let x = box.x1;
|
|
|
|
let y = box.y1;
|
|
|
|
let columnIndex = 0;
|
|
|
|
let i = 0;
|
|
|
|
// Loop over the children, going vertically down first. When we run
|
|
|
|
// out of vertical space (our y variable is bigger than box.y2), switch
|
|
|
|
// to the second column.
|
2009-10-01 13:18:05 +00:00
|
|
|
while (i < children.length) {
|
2009-08-17 02:23:44 +00:00
|
|
|
let child = children[i];
|
|
|
|
|
|
|
|
let [minSize, naturalSize] = child.get_preferred_height(-1);
|
|
|
|
|
|
|
|
if (y + naturalSize > box.y2) {
|
2009-10-01 13:18:05 +00:00
|
|
|
// Is this the second column, or we're in
|
|
|
|
// the first column and can't even fit one
|
|
|
|
// item? In that case, break.
|
|
|
|
if (columnIndex == 1 || i == 0) {
|
2009-08-17 02:23:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Set x to the halfway point.
|
|
|
|
columnIndex += 1;
|
|
|
|
x = x + itemWidth + DEFAULT_SPACING;
|
|
|
|
// And y is back to the top.
|
|
|
|
y = box.y1;
|
2009-10-01 13:18:05 +00:00
|
|
|
// Retry this same item, now that we're in the second column.
|
|
|
|
// By looping back to the top here, we re-test the size
|
|
|
|
// again for the second column.
|
|
|
|
continue;
|
2009-08-17 02:23:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let childBox = new Clutter.ActorBox();
|
|
|
|
childBox.x1 = x;
|
|
|
|
childBox.y1 = y;
|
|
|
|
childBox.x2 = childBox.x1 + itemWidth;
|
|
|
|
childBox.y2 = y + naturalSize;
|
|
|
|
|
|
|
|
y = childBox.y2 + DEFAULT_SPACING;
|
|
|
|
|
|
|
|
child.show();
|
|
|
|
child.allocate(childBox, flags);
|
2009-10-01 13:18:05 +00:00
|
|
|
|
|
|
|
i++;
|
2009-08-17 02:23:44 +00:00
|
|
|
}
|
|
|
|
|
2009-11-29 22:43:25 +00:00
|
|
|
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;
|
2009-08-17 02:23:44 +00:00
|
|
|
}
|
2009-11-29 22:43:25 +00:00
|
|
|
|
|
|
|
let skipPaint = [];
|
|
|
|
for (; i < children.length; i++)
|
|
|
|
this.actor.set_skip_paint(children[i], true);
|
|
|
|
},
|
|
|
|
|
|
|
|
_onDocsChanged: function() {
|
|
|
|
this._checkDocExistence = true;
|
|
|
|
Main.queueDeferredWork(this._workId);
|
2009-08-17 02:23:44 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_redisplay: function() {
|
2009-11-29 22:43:25 +00:00
|
|
|
// Should be kept alive by the _actorsByUri
|
2009-08-17 02:23:44 +00:00
|
|
|
this.actor.remove_all();
|
2009-11-29 22:43:25 +00:00
|
|
|
let docs = this._docManager.getTimestampOrderedInfos();
|
2009-12-08 02:48:31 +00:00
|
|
|
for (let i = 0; i < docs.length && i < MAX_DASH_DOCS; i++) {
|
2009-11-29 22:43:25 +00:00
|
|
|
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;
|
|
|
|
}
|
2009-08-17 02:23:44 +00:00
|
|
|
}
|
2009-11-29 22:43:25 +00:00
|
|
|
// 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];
|
|
|
|
}
|
2009-08-17 02:23:44 +00:00
|
|
|
}
|
2009-09-25 18:54:40 +00:00
|
|
|
this.emit('changed');
|
2009-08-17 02:23:44 +00:00
|
|
|
}
|
2009-09-25 18:54:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Signals.addSignalMethods(DashDocDisplay.prototype);
|
|
|
|
|