Compare commits
103 Commits
shell-tool
...
2.28.0
Author | SHA1 | Date | |
---|---|---|---|
a26b0b60d1 | |||
60dbb19c2d | |||
3703a86354 | |||
992b43f914 | |||
ff39edd1ee | |||
a81a16801d | |||
4c6f770dea | |||
c0b01c0210 | |||
400326e549 | |||
d7af6d40e3 | |||
90ebcd32e3 | |||
53d0581377 | |||
439daf828f | |||
d120d03de9 | |||
53fbabe2ca | |||
4bdd40911f | |||
ff4ac0d02e | |||
45dd342cc0 | |||
e5efecd2bd | |||
3bf88b8988 | |||
4ddc1118bb | |||
fd1e7b2a0f | |||
aed1e67add | |||
dc99e8ffcd | |||
6a8b50cb00 | |||
edb50d5dc7 | |||
2f6c951997 | |||
64cd51667d | |||
caa08f27fa | |||
26015ef16d | |||
4798ad5107 | |||
9b05304c2d | |||
795feca393 | |||
31663dcd83 | |||
2f2df61093 | |||
3b8d53060d | |||
c5ce405859 | |||
b3a5fc72fb | |||
8a2cc11cc0 | |||
cbb3a3aec8 | |||
e382da9708 | |||
ab1fbbde92 | |||
5f5266ca60 | |||
68e8b14b8b | |||
abdd8b330c | |||
eb8176deeb | |||
956f89f377 | |||
1c69380923 | |||
d2bc7b200e | |||
afb3b1e718 | |||
061a2cfbfb | |||
243824ab80 | |||
90ddad7ba1 | |||
20b29ff48f | |||
96f4d318c5 | |||
6f7da264ba | |||
640e45c12a | |||
04e28cd7c4 | |||
2cc41c6726 | |||
03a45b665c | |||
5a42179a96 | |||
af3ec56ca1 | |||
289b19aa31 | |||
91eb613d69 | |||
1c7c53d19f | |||
d4304495c6 | |||
b77b205d37 | |||
a15205e6c4 | |||
28dbf7a06e | |||
1ec8e9eb6b | |||
fa09f7a6da | |||
d67e54d3ee | |||
d263c12e2e | |||
4d55ccff39 | |||
2a0adc0fc8 | |||
076e902b2c | |||
8c72623da3 | |||
1fd25573e5 | |||
6527dbc8b7 | |||
3c646ec516 | |||
a9fd350396 | |||
e91e8e993d | |||
276d9a9302 | |||
6b95864076 | |||
d4c577a299 | |||
b90fc1e194 | |||
55497899dd | |||
e37790fdf9 | |||
83402957bb | |||
58325fca76 | |||
8b6962f3bf | |||
c1f91def74 | |||
529f74c0e5 | |||
459a3b18f2 | |||
ac2be7f0d1 | |||
f6b80d5ed4 | |||
d291e568fd | |||
2b78d5bd5d | |||
c9d9846759 | |||
6baafaa530 | |||
c2af05f753 | |||
97df305a6d | |||
fee385ba35 |
@ -1,4 +1,4 @@
|
||||
AC_INIT(gnome-shell, 2.27.3)
|
||||
AC_INIT(gnome-shell, 2.28.0)
|
||||
|
||||
AC_CONFIG_AUX_DIR(config)
|
||||
|
||||
@ -57,7 +57,7 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugin
|
||||
gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0
|
||||
gobject-introspection-1.0 >= 0.6.5)
|
||||
PKG_CHECK_MODULES(TIDY, clutter-1.0)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-2.0 clutter-imcontext-0.1 libcroco-0.6)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-2.0 libcroco-0.6)
|
||||
PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
|
||||
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
|
||||
|
@ -61,6 +61,19 @@ StScrollBar StButton#vhandle:hover
|
||||
border-image: url("scroll-vhandle.png") 5;
|
||||
}
|
||||
|
||||
/* Panel */
|
||||
|
||||
.panel-button {
|
||||
padding: 4px 12px 3px;
|
||||
border-radius: 5px;
|
||||
font: 16px sans-serif;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.panel-button:active, .panel-button:checked {
|
||||
background-color: #314a6c;
|
||||
}
|
||||
|
||||
/* LookingGlass */
|
||||
|
||||
#LookingGlassDialog
|
||||
@ -95,3 +108,69 @@ StScrollBar StButton#vhandle:hover
|
||||
padding: 4px;
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
/* Calendar popup */
|
||||
|
||||
#calendarPopup {
|
||||
border-radius: 5px;
|
||||
background: rgba(0,0,0,0.9);
|
||||
border: 1px solid rgba(128,128,128,0.45);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#calendarPopup .calendar {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
spacing-rows: 5px;
|
||||
spacing-columns: 3px;
|
||||
}
|
||||
|
||||
.calendar-change-month {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.calendar-change-month:hover {
|
||||
background: #314a6c;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.calendar-change-month:active {
|
||||
background: #213050;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
padding: 1px 2px;
|
||||
}
|
||||
|
||||
.calendar-today {
|
||||
font-weight: bold;
|
||||
background: #ffffff;
|
||||
color: black;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.calendar-other-month-day {
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
/* App Switcher */
|
||||
.switcher-list {
|
||||
background: rgba(0,0,0,0.8);
|
||||
border: 1px solid rgba(128,128,128,0.40);
|
||||
border-radius: 8px;
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.switcher-list .item-box {
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.switcher-list .selected-item-box {
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background: rgba(255,255,255,0.33);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
jsmiscdir = $(pkgdatadir)/js/misc
|
||||
|
||||
dist_jsmisc_DATA = \
|
||||
docInfo.js
|
||||
docInfo.js \
|
||||
format.js
|
||||
|
44
js/misc/format.js
Normal file
44
js/misc/format.js
Normal file
@ -0,0 +1,44 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
* This function is intended to extend the String object and provide
|
||||
* an String.format API for string formatting.
|
||||
* It has to be set up using String.prototype.format = Format.format;
|
||||
* Usage:
|
||||
* "somestring %s %d".format('hello', 5);
|
||||
* It supports %s, %d and %f, for %f it also support precisions like
|
||||
* "%.2f".format(1.526)
|
||||
*/
|
||||
|
||||
function format() {
|
||||
let str = this;
|
||||
let i = 0;
|
||||
let args = arguments;
|
||||
|
||||
return str.replace(/%(?:\.([0-9]+))?(.)/g, function (str, precisionGroup, genericGroup) {
|
||||
|
||||
if (precisionGroup != '' && genericGroup != 'f')
|
||||
throw new Error("Precision can only be specified for 'f'");
|
||||
|
||||
switch (genericGroup) {
|
||||
case '%':
|
||||
return '%';
|
||||
break;
|
||||
case 's':
|
||||
return args[i++].toString();
|
||||
break;
|
||||
case 'd':
|
||||
return parseInt(args[i++]);
|
||||
break;
|
||||
case 'f':
|
||||
if (precisionGroup == '')
|
||||
return parseFloat(args[i++]);
|
||||
else
|
||||
return parseFloat(args[i++]).toFixed(parseInt(precisionGroup));
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unsupported conversion character %' + genericGroup);
|
||||
}
|
||||
return ""; // Suppress warning
|
||||
});
|
||||
}
|
@ -5,6 +5,7 @@ dist_jsui_DATA = \
|
||||
appDisplay.js \
|
||||
appIcon.js \
|
||||
button.js \
|
||||
calendar.js \
|
||||
chrome.js \
|
||||
dash.js \
|
||||
dnd.js \
|
||||
|
794
js/ui/altTab.js
794
js/ui/altTab.js
@ -4,84 +4,61 @@ const Big = imports.gi.Big;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const AppIcon = imports.ui.appIcon;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const POPUP_BG_COLOR = new Clutter.Color();
|
||||
POPUP_BG_COLOR.from_pixel(0x00000080);
|
||||
const POPUP_APPICON_BORDER_COLOR = new Clutter.Color();
|
||||
POPUP_APPICON_BORDER_COLOR.from_pixel(0xffffffff);
|
||||
const POPUP_ARROW_COLOR = new Clutter.Color();
|
||||
POPUP_ARROW_COLOR.from_pixel(0xffffffff);
|
||||
const TRANSPARENT_COLOR = new Clutter.Color();
|
||||
TRANSPARENT_COLOR.from_pixel(0x00000000);
|
||||
const POPUP_SEPARATOR_COLOR = new Clutter.Color();
|
||||
POPUP_SEPARATOR_COLOR.from_pixel(0x80808066);
|
||||
|
||||
const POPUP_GRID_SPACING = 8;
|
||||
const POPUP_ICON_SIZE = 48;
|
||||
const POPUP_NUM_COLUMNS = 5;
|
||||
const POPUP_APPICON_SIZE = 96;
|
||||
const POPUP_LIST_SPACING = 8;
|
||||
|
||||
const POPUP_POINTER_SELECTION_THRESHOLD = 3;
|
||||
|
||||
const THUMBNAIL_SIZE = 256;
|
||||
const THUMBNAIL_POPUP_TIME = 1000; // milliseconds
|
||||
|
||||
const HOVER_TIME = 500; // milliseconds
|
||||
|
||||
function mod(a, b) {
|
||||
return (a + b) % b;
|
||||
}
|
||||
|
||||
function AltTabPopup() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
AltTabPopup.prototype = {
|
||||
_init : function() {
|
||||
this.actor = new Big.Box({ background_color : POPUP_BG_COLOR,
|
||||
corner_radius: POPUP_GRID_SPACING,
|
||||
padding: POPUP_GRID_SPACING,
|
||||
spacing: POPUP_GRID_SPACING,
|
||||
orientation: Big.BoxOrientation.VERTICAL,
|
||||
reactive: true });
|
||||
this.actor = new Clutter.Group({ reactive: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height });
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
// Icon grid. TODO: Investigate Nbtk.Grid once that lands. Currently
|
||||
// just implemented using a chain of Big.Box.
|
||||
this._grid = new Big.Box({ spacing: POPUP_GRID_SPACING,
|
||||
orientation: Big.BoxOrientation.VERTICAL });
|
||||
let gcenterbox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
x_align: Big.BoxAlignment.CENTER });
|
||||
gcenterbox.append(this._grid, Big.BoxPackFlags.NONE);
|
||||
this.actor.append(gcenterbox, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._icons = [];
|
||||
this._currentWindows = [];
|
||||
this._haveModal = false;
|
||||
this._selected = 0;
|
||||
this._highlightedWindow = null;
|
||||
this._toplevels = global.window_group.get_children();
|
||||
|
||||
this._currentApp = 0;
|
||||
this._currentWindows = [];
|
||||
this._thumbnailTimeoutId = 0;
|
||||
|
||||
global.stage.add_actor(this.actor);
|
||||
},
|
||||
|
||||
_addIcon : function(appIcon) {
|
||||
appIcon.connect('activate', Lang.bind(this, this._appClicked));
|
||||
appIcon.connect('activate-window', Lang.bind(this, this._windowClicked));
|
||||
appIcon.connect('highlight-window', Lang.bind(this, this._windowHovered));
|
||||
appIcon.connect('menu-popped-up', Lang.bind(this, this._menuPoppedUp));
|
||||
appIcon.connect('menu-popped-down', Lang.bind(this, this._menuPoppedDown));
|
||||
|
||||
appIcon.actor.connect('enter-event', Lang.bind(this, this._iconEntered));
|
||||
|
||||
// FIXME?
|
||||
appIcon.actor.border = 2;
|
||||
appIcon.highlight_border_color = POPUP_APPICON_BORDER_COLOR;
|
||||
|
||||
this._icons.push(appIcon);
|
||||
this._currentWindows.push(appIcon.windows[0]);
|
||||
|
||||
// Add it to the grid
|
||||
if (!this._gridRow || this._gridRow.get_children().length == POPUP_NUM_COLUMNS) {
|
||||
this._gridRow = new Big.Box({ spacing: POPUP_GRID_SPACING,
|
||||
orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
this._grid.append(this._gridRow, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
this._gridRow.append(appIcon.actor, Big.BoxPackFlags.NONE);
|
||||
},
|
||||
|
||||
show : function(initialSelection) {
|
||||
show : function(backward) {
|
||||
let appMonitor = Shell.AppMonitor.get_default();
|
||||
let apps = appMonitor.get_running_apps ("");
|
||||
|
||||
@ -99,32 +76,52 @@ AltTabPopup.prototype = {
|
||||
this._mouseActive = false;
|
||||
this._mouseMovement = 0;
|
||||
|
||||
// Contruct the AppIcons, sort by time, add to the popup
|
||||
let icons = [];
|
||||
for (let i = 0; i < apps.length; i++)
|
||||
icons.push(new AppIcon.AppIcon(apps[i], AppIcon.MenuType.BELOW));
|
||||
icons.sort(Lang.bind(this, this._sortAppIcon));
|
||||
for (let i = 0; i < icons.length; i++)
|
||||
this._addIcon(icons[i]);
|
||||
this._appSwitcher = new AppSwitcher(apps);
|
||||
this.actor.add_actor(this._appSwitcher.actor);
|
||||
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
|
||||
this._appSwitcher.connect('item-hovered', Lang.bind(this, this._appHovered));
|
||||
|
||||
// Need to specify explicit width and height because the
|
||||
// window_group may not actually cover the whole screen
|
||||
this._lightbox = new Lightbox.Lightbox(global.window_group,
|
||||
global.screen_width,
|
||||
global.screen_height);
|
||||
let primary = global.get_primary_monitor();
|
||||
this._appSwitcher.actor.x = primary.x + Math.floor((primary.width - this._appSwitcher.actor.width) / 2);
|
||||
this._appSwitcher.actor.y = primary.y + Math.floor((primary.height - this._appSwitcher.actor.height) / 2);
|
||||
|
||||
this.actor.show_all();
|
||||
this.actor.x = Math.floor((global.screen_width - this.actor.width) / 2);
|
||||
this.actor.y = Math.floor((global.screen_height - this.actor.height) / 2);
|
||||
this._appIcons = this._appSwitcher.icons;
|
||||
|
||||
this._updateSelection(initialSelection);
|
||||
// _currentWindows give the index of the selected window for
|
||||
// each app; they all start at 0.
|
||||
this._currentWindows = this._appIcons.map(function (app) { return 0; });
|
||||
|
||||
// Make the initial selection
|
||||
if (this._appIcons.length == 1) {
|
||||
if (!backward && this._appIcons[0].windows.length > 1) {
|
||||
// For compatibility with the multi-app case below
|
||||
this._select(0, 1);
|
||||
} else
|
||||
this._select(0);
|
||||
} else if (backward) {
|
||||
this._select(this._appIcons.length - 1);
|
||||
} else {
|
||||
if (this._appIcons[0].windows.length > 1) {
|
||||
let curAppNextWindow = this._appIcons[0].windows[1];
|
||||
let nextAppWindow = this._appIcons[1].windows[0];
|
||||
|
||||
// If the next window of the current app is more-recently-used
|
||||
// than the first window of the next app, then select it.
|
||||
if (curAppNextWindow.get_workspace() == global.screen.get_active_workspace() &&
|
||||
curAppNextWindow.get_user_time() > nextAppWindow.get_user_time())
|
||||
this._select(0, 1);
|
||||
else
|
||||
this._select(1);
|
||||
} else
|
||||
this._select(1);
|
||||
}
|
||||
|
||||
// There's a race condition; if the user released Alt before
|
||||
// we got the grab, then we won't be notified. (See
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
|
||||
// details.) So we check now. (Have to do this after calling
|
||||
// _updateSelection.)
|
||||
let [screen, x, y, mods] = Gdk.Display.get_default().get_pointer();
|
||||
// details.) So we check now. (Have to do this after updating
|
||||
// selection.)
|
||||
let mods = global.get_modifier_keys();
|
||||
if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
|
||||
this._finish();
|
||||
return false;
|
||||
@ -133,6 +130,502 @@ AltTabPopup.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
_nextApp : function() {
|
||||
return mod(this._currentApp + 1, this._appIcons.length);
|
||||
},
|
||||
_previousApp : function() {
|
||||
return mod(this._currentApp - 1, this._appIcons.length);
|
||||
},
|
||||
|
||||
_nextWindow : function() {
|
||||
return mod(this._currentWindows[this._currentApp] + 1,
|
||||
this._appIcons[this._currentApp].windows.length);
|
||||
},
|
||||
_previousWindow : function() {
|
||||
return mod(this._currentWindows[this._currentApp] - 1,
|
||||
this._appIcons[this._currentApp].windows.length);
|
||||
},
|
||||
|
||||
_keyPressEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
|
||||
|
||||
// The WASD stuff is for debugging in Xephyr, where the arrow
|
||||
// keys aren't mapped correctly
|
||||
|
||||
if (keysym == Clutter.Tab)
|
||||
this._select(shift ? this._previousApp() : this._nextApp());
|
||||
else if (keysym == Clutter.grave)
|
||||
this._select(this._currentApp, shift ? this._previousWindow() : this._nextWindow());
|
||||
else if (keysym == Clutter.Escape)
|
||||
this.destroy();
|
||||
else if (this._thumbnails) {
|
||||
if (keysym == Clutter.Left || keysym == Clutter.a)
|
||||
this._select(this._currentApp, this._previousWindow());
|
||||
else if (keysym == Clutter.Right || keysym == Clutter.d)
|
||||
this._select(this._currentApp, this._nextWindow());
|
||||
else if (keysym == Clutter.Up || keysym == Clutter.w)
|
||||
this._select(this._currentApp, null, true);
|
||||
} else {
|
||||
if (keysym == Clutter.Left || keysym == Clutter.a)
|
||||
this._select(this._previousApp());
|
||||
else if (keysym == Clutter.Right || keysym == Clutter.d)
|
||||
this._select(this._nextApp());
|
||||
else if (keysym == Clutter.Down || keysym == Clutter.s)
|
||||
this._select(this._currentApp, this._currentWindows[this._currentApp]);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_keyReleaseEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
|
||||
if (keysym == Clutter.Alt_L || keysym == Clutter.Alt_R)
|
||||
this._finish();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_appActivated : function(appSwitcher, n) {
|
||||
Main.activateWindow(this._appIcons[n].windows[this._currentWindows[n]]);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_appHovered : function(appSwitcher, n) {
|
||||
if (!this._mouseActive)
|
||||
return;
|
||||
|
||||
this._select(n, this._currentWindows[n]);
|
||||
},
|
||||
|
||||
_windowActivated : function(thumbnailList, n) {
|
||||
Main.activateWindow(this._appIcons[this._currentApp].windows[n]);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_windowHovered : function(thumbnailList, n) {
|
||||
if (!this._mouseActive)
|
||||
return;
|
||||
|
||||
this._select(this._currentApp, n);
|
||||
},
|
||||
|
||||
_mouseMoved : function(actor, event) {
|
||||
if (++this._mouseMovement < POPUP_POINTER_SELECTION_THRESHOLD)
|
||||
return;
|
||||
|
||||
this.actor.disconnect(this._motionEventId);
|
||||
this._mouseActive = true;
|
||||
|
||||
this._appSwitcher.checkHover();
|
||||
if (this._thumbnails)
|
||||
this._thumbnails.checkHover();
|
||||
},
|
||||
|
||||
_finish : function() {
|
||||
let app = this._appIcons[this._currentApp];
|
||||
let window = app.windows[this._currentWindows[this._currentApp]];
|
||||
Main.activateWindow(window);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
if (this._haveModal)
|
||||
Main.popModal(this.actor);
|
||||
|
||||
if (this._keyPressEventId)
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
if (this._keyReleaseEventId)
|
||||
global.stage.disconnect(this._keyReleaseEventId);
|
||||
|
||||
if (this._thumbnailTimeoutId != 0)
|
||||
Mainloop.source_remove(this._thumbnailTimeoutId);
|
||||
},
|
||||
|
||||
_select : function(app, window, noTimeout) {
|
||||
if ((app != this._currentApp || !window) && this._thumbnails) {
|
||||
this._thumbnails.actor.destroy();
|
||||
this._thumbnails = null;
|
||||
this._appSwitcher.showArrow(-1);
|
||||
}
|
||||
|
||||
if (this._thumbnailTimeoutId != 0) {
|
||||
Mainloop.source_remove(this._thumbnailTimeoutId);
|
||||
this._thumbnailTimeoutId = 0;
|
||||
}
|
||||
|
||||
this._currentApp = app;
|
||||
if (window != null) {
|
||||
this._appSwitcher.highlight(-1);
|
||||
this._appSwitcher.showArrow(app);
|
||||
} else {
|
||||
this._appSwitcher.highlight(app);
|
||||
if (this._appIcons[this._currentApp].windows.length > 1)
|
||||
this._appSwitcher.showArrow(app);
|
||||
}
|
||||
|
||||
if (window != null) {
|
||||
if (!this._thumbnails)
|
||||
this._createThumbnails();
|
||||
this._currentWindows[this._currentApp] = window;
|
||||
this._thumbnails.highlight(window);
|
||||
} else if (this._appIcons[this._currentApp].windows.length > 1 &&
|
||||
!noTimeout) {
|
||||
this._thumbnailTimeoutId = Mainloop.timeout_add (
|
||||
THUMBNAIL_POPUP_TIME,
|
||||
Lang.bind(this, function () {
|
||||
this._select(this._currentApp,
|
||||
this._currentWindows[this._currentApp]);
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_createThumbnails : function() {
|
||||
this._thumbnails = new ThumbnailList (this._appIcons[this._currentApp].windows);
|
||||
this._thumbnails.connect('item-activated', Lang.bind(this, this._windowActivated));
|
||||
this._thumbnails.connect('item-hovered', Lang.bind(this, this._windowHovered));
|
||||
|
||||
this.actor.add_actor(this._thumbnails.actor);
|
||||
|
||||
let thumbnailCenter;
|
||||
if (this._thumbnails.actor.width < this._appSwitcher.actor.width) {
|
||||
// Center the thumbnails under the corresponding AppIcon.
|
||||
// If this is being called when the switcher is first
|
||||
// being brought up, then nothing will have been assigned
|
||||
// an allocation yet, and the get_transformed_position()
|
||||
// call will return 0,0.
|
||||
// (http://bugzilla.openedhand.com/show_bug.cgi?id=1115).
|
||||
// Calling clutter_actor_get_allocation_box() would force
|
||||
// it to properly allocate itself, but we can't call that
|
||||
// because it has an out-caller-allocates arg. So we use
|
||||
// clutter_stage_get_actor_at_pos(), which will force a
|
||||
// reallocation as a side effect.
|
||||
global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, 0, 0);
|
||||
|
||||
let icon = this._appIcons[this._currentApp].actor;
|
||||
let [stageX, stageY] = icon.get_transformed_position();
|
||||
thumbnailCenter = stageX + icon.width / 2;
|
||||
} else {
|
||||
// Center the thumbnails on the monitor
|
||||
let primary = global.get_primary_monitor();
|
||||
thumbnailCenter = primary.x + primary.width / 2;
|
||||
}
|
||||
|
||||
this._thumbnails.actor.x = Math.floor(thumbnailCenter - this._thumbnails.actor.width / 2);
|
||||
this._thumbnails.actor.y = this._appSwitcher.actor.y + this._appSwitcher.actor.height + POPUP_LIST_SPACING;
|
||||
}
|
||||
};
|
||||
|
||||
function SwitcherList(squareItems) {
|
||||
this._init(squareItems);
|
||||
}
|
||||
|
||||
SwitcherList.prototype = {
|
||||
_init : function(squareItems) {
|
||||
this.actor = new St.Bin({ style_class: 'switcher-list' });
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
// Here we use a GenericContainer so that we can force all the
|
||||
// children except the separator to have the same width.
|
||||
this._list = new Shell.GenericContainer();
|
||||
this._list.spacing = POPUP_LIST_SPACING;
|
||||
|
||||
this._list.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this._list.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this._list.connect('allocate', Lang.bind(this, this._allocate));
|
||||
|
||||
this.actor.add_actor(this._list);
|
||||
|
||||
this._items = [];
|
||||
this._highlighted = -1;
|
||||
this._separator = null;
|
||||
this._squareItems = squareItems;
|
||||
|
||||
this._hoverTimeout = 0;
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
if (this._hoverTimeout != 0) {
|
||||
Mainloop.source_remove(this._hoverTimeout);
|
||||
this._hoverTimeout = 0;
|
||||
}
|
||||
},
|
||||
|
||||
addItem : function(item) {
|
||||
let box = new St.Bin({ style_class: 'item-box' });
|
||||
let bbox;
|
||||
|
||||
if (item instanceof Shell.ButtonBox)
|
||||
bbox = item;
|
||||
else {
|
||||
bbox = new Shell.ButtonBox({ reactive: true });
|
||||
bbox.append(item, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
box.add_actor(bbox);
|
||||
this._list.add_actor(box);
|
||||
|
||||
let n = this._items.length;
|
||||
bbox.connect('activate', Lang.bind(this, function () {
|
||||
this._itemActivated(n);
|
||||
}));
|
||||
bbox.connect('notify::hover', Lang.bind(this, function () {
|
||||
this._hoverChanged(bbox, n);
|
||||
}));
|
||||
|
||||
this._items.push(box);
|
||||
},
|
||||
|
||||
addSeparator: function () {
|
||||
// FIXME: make this work with StWidgets and CSS
|
||||
let box = new Big.Box({ padding_top: 2, padding_bottom: 2 });
|
||||
box.append(new Clutter.Rectangle({ width: 1,
|
||||
color: POPUP_SEPARATOR_COLOR }),
|
||||
Big.BoxPackFlags.EXPAND);
|
||||
this._separator = box;
|
||||
this._list.add_actor(box);
|
||||
},
|
||||
|
||||
highlight: function(index) {
|
||||
if (this._highlighted != -1)
|
||||
this._items[this._highlighted].style_class = 'item-box';
|
||||
|
||||
this._highlighted = index;
|
||||
|
||||
if (this._highlighted != -1)
|
||||
this._items[this._highlighted].style_class = 'selected-item-box';
|
||||
},
|
||||
|
||||
// Used after the mouse movement exceeds the threshold, to check
|
||||
// if it's already hovering over an icon
|
||||
checkHover: function() {
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
if (this._items[i].get_child().hover) {
|
||||
this._hoverChanged(this._items[i].get_child(), i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_itemActivated: function(n) {
|
||||
this.emit('item-activated', n);
|
||||
},
|
||||
|
||||
_hoverChanged: function(box, n) {
|
||||
if (this._hoverTimeout != 0) {
|
||||
Mainloop.source_remove(this._hoverTimeout);
|
||||
this._hoverTimeout = 0;
|
||||
}
|
||||
|
||||
if (box.hover) {
|
||||
this._hoverTimeout = Mainloop.timeout_add(
|
||||
HOVER_TIME,
|
||||
Lang.bind (this, function () {
|
||||
this._itemHovered(n);
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_itemHovered: function(n) {
|
||||
this.emit('item-hovered', n);
|
||||
},
|
||||
|
||||
_maxChildWidth: function (forHeight) {
|
||||
let maxChildMin = 0;
|
||||
let maxChildNat = 0;
|
||||
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
let [childMin, childNat] = this._items[i].get_preferred_width(forHeight);
|
||||
maxChildMin = Math.max(childMin, maxChildMin);
|
||||
maxChildNat = Math.max(childNat, maxChildNat);
|
||||
|
||||
if (this._squareItems) {
|
||||
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
|
||||
maxChildMin = Math.max(childMin, maxChildMin);
|
||||
maxChildNat = Math.max(childNat, maxChildNat);
|
||||
}
|
||||
}
|
||||
|
||||
return [maxChildMin, maxChildNat];
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||
let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight);
|
||||
|
||||
let separatorWidth = 0;
|
||||
if (this._separator) {
|
||||
let [sepMin, sepNat] = this._separator.get_preferred_width(forHeight);
|
||||
separatorWidth = sepNat + this._list.spacing;
|
||||
}
|
||||
|
||||
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||
alloc.min_size = this._items.length * maxChildMin + separatorWidth + totalSpacing;
|
||||
alloc.nat_size = this._items.length * maxChildNat + separatorWidth + totalSpacing;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||
let maxChildMin = 0;
|
||||
let maxChildNat = 0;
|
||||
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
|
||||
maxChildMin = Math.max(childMin, maxChildMin);
|
||||
maxChildNat = Math.max(childNat, maxChildNat);
|
||||
}
|
||||
|
||||
if (this._squareItems) {
|
||||
let [childMin, childNat] = this._maxChildWidth(-1);
|
||||
maxChildMin = Math.max(childMin, maxChildMin);
|
||||
maxChildNat = Math.max(childNat, maxChildNat);
|
||||
}
|
||||
|
||||
alloc.min_size = maxChildMin;
|
||||
alloc.nat_size = maxChildNat;
|
||||
},
|
||||
|
||||
_allocate: function (actor, box, flags) {
|
||||
let childHeight = box.y2 - box.y1;
|
||||
|
||||
let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
|
||||
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||
|
||||
let separatorWidth = 0;
|
||||
if (this._separator) {
|
||||
let [sepMin, sepNat] = this._separator.get_preferred_width(childHeight);
|
||||
separatorWidth = sepNat;
|
||||
totalSpacing += this._list.spacing;
|
||||
}
|
||||
|
||||
let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing - separatorWidth) / this._items.length);
|
||||
|
||||
let x = 0;
|
||||
let children = this._list.get_children();
|
||||
let childBox = new Clutter.ActorBox();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (this._items.indexOf(children[i]) != -1) {
|
||||
let [childMin, childNat] = children[i].get_preferred_height(childWidth);
|
||||
let vSpacing = (childHeight - childNat) / 2;
|
||||
childBox.x1 = x;
|
||||
childBox.y1 = vSpacing;
|
||||
childBox.x2 = x + childWidth;
|
||||
childBox.y2 = childBox.y1 + childNat;
|
||||
children[i].allocate(childBox, flags);
|
||||
|
||||
x += this._list.spacing + childWidth;
|
||||
} else if (children[i] == this._separator) {
|
||||
// We want the separator to be more compact than the rest.
|
||||
childBox.x1 = x;
|
||||
childBox.y1 = 0;
|
||||
childBox.x2 = x + separatorWidth;
|
||||
childBox.y2 = childHeight;
|
||||
children[i].allocate(childBox, flags);
|
||||
x += this._list.spacing + separatorWidth;
|
||||
} else {
|
||||
// Something else, eg, AppSwitcher's arrows;
|
||||
// we don't allocate it.
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(SwitcherList.prototype);
|
||||
|
||||
function AppSwitcher(apps) {
|
||||
this._init(apps);
|
||||
}
|
||||
|
||||
AppSwitcher.prototype = {
|
||||
__proto__ : SwitcherList.prototype,
|
||||
|
||||
_init : function(apps) {
|
||||
SwitcherList.prototype._init.call(this, true);
|
||||
|
||||
// Construct the AppIcons, sort by time, add to the popup
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
let workspaceIcons = [];
|
||||
let otherIcons = [];
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let appIcon = new AppIcon.AppIcon({ appInfo: apps[i],
|
||||
size: POPUP_APPICON_SIZE });
|
||||
if (this._hasWindowsOnWorkspace(appIcon, activeWorkspace))
|
||||
workspaceIcons.push(appIcon);
|
||||
else
|
||||
otherIcons.push(appIcon);
|
||||
}
|
||||
|
||||
workspaceIcons.sort(Lang.bind(this, this._sortAppIcon));
|
||||
otherIcons.sort(Lang.bind(this, this._sortAppIcon));
|
||||
|
||||
this.icons = [];
|
||||
this._arrows = [];
|
||||
for (let i = 0; i < workspaceIcons.length; i++)
|
||||
this._addIcon(workspaceIcons[i]);
|
||||
if (workspaceIcons.length > 0 && otherIcons.length > 0)
|
||||
this.addSeparator();
|
||||
for (let i = 0; i < otherIcons.length; i++)
|
||||
this._addIcon(otherIcons[i]);
|
||||
|
||||
this._shownArrow = -1;
|
||||
},
|
||||
|
||||
_allocate: function (actor, box, flags) {
|
||||
// Allocate the main list items
|
||||
SwitcherList.prototype._allocate.call(this, actor, box, flags);
|
||||
|
||||
let arrowHeight = Math.floor(this.actor.get_theme_node().get_padding(St.Side.BOTTOM) / 3);
|
||||
let arrowWidth = arrowHeight * 2;
|
||||
|
||||
// Now allocate each arrow underneath its item
|
||||
let childBox = new Clutter.ActorBox();
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
let itemBox = this._items[i].allocation;
|
||||
childBox.x1 = Math.floor(itemBox.x1 + (itemBox.x2 - itemBox.x1 - arrowWidth) / 2);
|
||||
childBox.x2 = childBox.x1 + arrowWidth;
|
||||
childBox.y1 = itemBox.y2 + arrowHeight;
|
||||
childBox.y2 = childBox.y1 + arrowHeight;
|
||||
this._arrows[i].allocate(childBox, flags);
|
||||
}
|
||||
},
|
||||
|
||||
showArrow : function(n) {
|
||||
if (this._shownArrow != -1)
|
||||
this._arrows[this._shownArrow].hide();
|
||||
|
||||
this._shownArrow = n;
|
||||
|
||||
if (this._shownArrow != -1)
|
||||
this._arrows[this._shownArrow].show();
|
||||
},
|
||||
|
||||
_addIcon : function(appIcon) {
|
||||
this.icons.push(appIcon);
|
||||
this.addItem(appIcon.actor);
|
||||
|
||||
let arrow = new Shell.DrawingArea();
|
||||
arrow.connect('redraw', Lang.bind(this,
|
||||
function (area, texture) {
|
||||
Shell.draw_box_pointer(texture, Shell.PointerDirection.DOWN,
|
||||
TRANSPARENT_COLOR,
|
||||
POPUP_ARROW_COLOR);
|
||||
}));
|
||||
this._list.add_actor(arrow);
|
||||
this._arrows.push(arrow);
|
||||
arrow.hide();
|
||||
},
|
||||
|
||||
_hasWindowsOnWorkspace: function(appIcon, workspace) {
|
||||
for (let i = 0; i < appIcon.windows.length; i++) {
|
||||
if (appIcon.windows[i].get_workspace() == workspace)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_hasVisibleWindows : function(appIcon) {
|
||||
for (let i = 0; i < appIcon.windows.length; i++) {
|
||||
if (appIcon.windows[i].showing_on_its_workspace())
|
||||
@ -155,133 +648,32 @@ AltTabPopup.prototype = {
|
||||
return (appIcon2.windows[0].get_user_time() -
|
||||
appIcon1.windows[0].get_user_time());
|
||||
}
|
||||
},
|
||||
|
||||
_keyPressEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
let backwards = (event.get_state() & Clutter.ModifierType.SHIFT_MASK);
|
||||
|
||||
if (keysym == Clutter.Tab)
|
||||
this._updateSelection(backwards ? -1 : 1);
|
||||
else if (keysym == Clutter.grave)
|
||||
this._updateWindowSelection(backwards ? -1 : 1);
|
||||
else if (keysym == Clutter.Escape)
|
||||
this.destroy();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_keyReleaseEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
|
||||
if (keysym == Clutter.Alt_L || keysym == Clutter.Alt_R)
|
||||
this._finish();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_appClicked : function(icon) {
|
||||
Main.activateWindow(icon.windows[0]);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_windowClicked : function(icon, window) {
|
||||
if (window)
|
||||
Main.activateWindow(window);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_windowHovered : function(icon, window) {
|
||||
if (window)
|
||||
this._highlightWindow(window);
|
||||
},
|
||||
|
||||
_mouseMoved : function(actor, event) {
|
||||
if (++this._mouseMovement < POPUP_POINTER_SELECTION_THRESHOLD)
|
||||
return;
|
||||
|
||||
this.actor.disconnect(this._motionEventId);
|
||||
this._mouseActive = true;
|
||||
|
||||
actor = event.get_source();
|
||||
while (actor) {
|
||||
if (actor._delegate instanceof AppIcon.AppIcon) {
|
||||
this._iconEntered(actor, event);
|
||||
return;
|
||||
}
|
||||
actor = actor.get_parent();
|
||||
}
|
||||
},
|
||||
|
||||
_iconEntered : function(actor, event) {
|
||||
let index = this._icons.indexOf(actor._delegate);
|
||||
if (this._mouseActive)
|
||||
this._updateSelection(index - this._selected);
|
||||
},
|
||||
|
||||
_finish : function() {
|
||||
if (this._highlightedWindow)
|
||||
Main.activateWindow(this._highlightedWindow);
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
if (this._haveModal)
|
||||
Main.popModal(this.actor);
|
||||
|
||||
if (this._lightbox)
|
||||
this._lightbox.destroy();
|
||||
|
||||
if (this._keyPressEventId)
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
if (this._keyReleaseEventId)
|
||||
global.stage.disconnect(this._keyReleaseEventId);
|
||||
},
|
||||
|
||||
_updateSelection : function(delta) {
|
||||
this._icons[this._selected].setHighlight(false);
|
||||
if (delta != 0 && this._selectedMenu)
|
||||
this._selectedMenu.popdown();
|
||||
|
||||
this._selected = (this._selected + this._icons.length + delta) % this._icons.length;
|
||||
this._icons[this._selected].setHighlight(true);
|
||||
|
||||
this._highlightWindow(this._currentWindows[this._selected]);
|
||||
},
|
||||
|
||||
_menuPoppedUp : function(icon, menu) {
|
||||
this._selectedMenu = menu;
|
||||
},
|
||||
|
||||
_menuPoppedDown : function(icon, menu) {
|
||||
this._selectedMenu = null;
|
||||
},
|
||||
|
||||
_updateWindowSelection : function(delta) {
|
||||
let icon = this._icons[this._selected];
|
||||
|
||||
if (!this._selectedMenu)
|
||||
icon.popupMenu();
|
||||
if (!this._selectedMenu)
|
||||
return;
|
||||
|
||||
let next = 0;
|
||||
for (let i = 0; i < icon.windows.length; i++) {
|
||||
if (icon.windows[i] == this._highlightedWindow) {
|
||||
next = (i + icon.windows.length + delta) % icon.windows.length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._selectedMenu.selectWindow(icon.windows[next]);
|
||||
},
|
||||
|
||||
_highlightWindow : function(metaWin) {
|
||||
this._highlightedWindow = metaWin;
|
||||
this._currentWindows[this._selected] = metaWin;
|
||||
this._lightbox.highlight(this._highlightedWindow.get_compositor_private());
|
||||
}
|
||||
};
|
||||
|
||||
function ThumbnailList(windows) {
|
||||
this._init(windows);
|
||||
}
|
||||
|
||||
ThumbnailList.prototype = {
|
||||
__proto__ : SwitcherList.prototype,
|
||||
|
||||
_init : function(windows) {
|
||||
SwitcherList.prototype._init.call(this);
|
||||
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let mutterWindow = windows[i].get_compositor_private();
|
||||
let windowTexture = mutterWindow.get_texture ();
|
||||
let [width, height] = windowTexture.get_size();
|
||||
let scale = Math.min(1.0, THUMBNAIL_SIZE / width, THUMBNAIL_SIZE / height);
|
||||
|
||||
let clone = new Clutter.Clone ({ source: windowTexture,
|
||||
reactive: true,
|
||||
width: width * scale,
|
||||
height: height * scale });
|
||||
let box = new Big.Box({ padding: AppIcon.APPICON_BORDER_WIDTH + AppIcon.APPICON_PADDING });
|
||||
box.append(clone, Big.BoxPackFlags.NONE);
|
||||
this.addItem(box);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -177,31 +177,21 @@ AppDisplay.prototype = {
|
||||
|
||||
this._menus = [];
|
||||
this._menuDisplays = [];
|
||||
|
||||
// map<itemId, array of category names>
|
||||
this._appCategories = {};
|
||||
// map<search term, map<appId, true>>
|
||||
// We use a map of appIds instead of an array to ensure that we don't have duplicates and for easier lookup.
|
||||
this._menuSearchAppMatches = {};
|
||||
|
||||
this._appMonitor = Shell.AppMonitor.get_default();
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this._appsStale = true;
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, function(appSys) {
|
||||
this._appsStale = true;
|
||||
this._redisplay(0);
|
||||
this._redisplayMenus();
|
||||
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
|
||||
}));
|
||||
this._appSystem.connect('favorites-changed', Lang.bind(this, function(appSys) {
|
||||
this._redisplay(0);
|
||||
this._appsStale = true;
|
||||
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
|
||||
}));
|
||||
this._appMonitor.connect('app-added', Lang.bind(this, function(monitor) {
|
||||
this._redisplay(0);
|
||||
}));
|
||||
this._appMonitor.connect('app-removed', Lang.bind(this, function(monitor) {
|
||||
this._redisplay(0);
|
||||
}));
|
||||
|
||||
// Load the apps now so it doesn't slow down the first
|
||||
// transition into the Overview
|
||||
this._refreshCache();
|
||||
|
||||
this._focusInMenus = true;
|
||||
this._activeMenuIndex = -1;
|
||||
@ -210,7 +200,6 @@ AppDisplay.prototype = {
|
||||
this._menuDisplay = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: MENU_SPACING
|
||||
});
|
||||
this._redisplayMenus();
|
||||
|
||||
this.connect('expanded', Lang.bind(this, function (self) {
|
||||
this._filterReset();
|
||||
@ -253,6 +242,39 @@ AppDisplay.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
setSearch: function(text) {
|
||||
let lowertext = text.toLowerCase();
|
||||
if (lowertext == this._search)
|
||||
return;
|
||||
|
||||
// We prepare menu matches up-front, so that we don't
|
||||
// need to go over all menu items for each application
|
||||
// and then get all applications for a matching menu
|
||||
// to see if a particular application passed to
|
||||
// _isInfoMatching() is a match.
|
||||
let terms = lowertext.split(/\s+/);
|
||||
this._menuSearchAppMatches = {};
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
let term = terms[i];
|
||||
this._menuSearchAppMatches[term] = {};
|
||||
for (let j = 0; j < this._menus.length; j++) {
|
||||
let menuItem = this._menus[j];
|
||||
// Match only on the beginning of the words in category names,
|
||||
// because otherwise it introduces unnecessary noise in the results.
|
||||
if (menuItem.name.toLowerCase().indexOf(term) == 0 ||
|
||||
menuItem.name.toLowerCase().indexOf(" " + term) > 0) {
|
||||
let menuApps = this._appSystem.get_applications_for_menu(menuItem.id);
|
||||
for (let k = 0; k < menuApps.length; k++) {
|
||||
let menuApp = menuApps[k];
|
||||
this._menuSearchAppMatches[term][menuApp.get_id()] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GenericDisplay.GenericDisplay.prototype.setSearch.call(this, text);
|
||||
},
|
||||
|
||||
// Protected overrides
|
||||
|
||||
_filterActive: function() {
|
||||
@ -303,7 +325,7 @@ AppDisplay.prototype = {
|
||||
this._activeMenuApps = this._appSystem.get_applications_for_menu(id);
|
||||
}
|
||||
}
|
||||
this._redisplay(true);
|
||||
this._redisplay(GenericDisplay.RedisplayFlags.FULL);
|
||||
}));
|
||||
this._menuDisplay.append(display.actor, 0);
|
||||
},
|
||||
@ -333,7 +355,6 @@ AppDisplay.prototype = {
|
||||
if (!this._appsStale)
|
||||
return true;
|
||||
this._allItems = {};
|
||||
this._appCategories = {};
|
||||
|
||||
if (this._showPrefs) {
|
||||
// Get the desktop file ids for settings/preferences.
|
||||
@ -361,6 +382,7 @@ AppDisplay.prototype = {
|
||||
this._addApp(app);
|
||||
}
|
||||
}
|
||||
this._redisplayMenus();
|
||||
}
|
||||
|
||||
this._appsStale = false;
|
||||
@ -383,7 +405,7 @@ AppDisplay.prototype = {
|
||||
},
|
||||
|
||||
// Checks if the item info can be a match for the search string by checking
|
||||
// the name, description, execution command, and categories for the application.
|
||||
// the name, description, execution command, and category for the application.
|
||||
// Item info is expected to be Shell.AppInfo.
|
||||
// Returns a boolean flag indicating if itemInfo is a match.
|
||||
_isInfoMatching : function(itemInfo, search) {
|
||||
@ -395,10 +417,10 @@ AppDisplay.prototype = {
|
||||
if (this._activeMenu == null || search != "")
|
||||
return this._isInfoMatchingSearch(itemInfo, search);
|
||||
else
|
||||
return this._isInfoMatchingMenu(itemInfo, search);
|
||||
return this._isInfoMatchingMenu(itemInfo);
|
||||
},
|
||||
|
||||
_isInfoMatchingMenu : function(itemInfo, search) {
|
||||
_isInfoMatchingMenu: function(itemInfo) {
|
||||
let id = itemInfo.get_id();
|
||||
for (let i = 0; i < this._activeMenuApps.length; i++) {
|
||||
let activeApp = this._activeMenuApps[i];
|
||||
@ -436,11 +458,11 @@ AppDisplay.prototype = {
|
||||
return true;
|
||||
}
|
||||
|
||||
let categories = itemInfo.get_categories();
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
let category = fold(categories[i]);
|
||||
if (category.indexOf(search) >= 0)
|
||||
if (this._menuSearchAppMatches[search]) {
|
||||
if (this._menuSearchAppMatches[search].hasOwnProperty(itemInfo.get_id()))
|
||||
return true;
|
||||
} else {
|
||||
log("Missing an entry for search term " + search + " in this._menuSearchAppMatches");
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -461,8 +483,10 @@ function BaseWellItem(appInfo, isFavorite, hasMenu) {
|
||||
BaseWellItem.prototype = {
|
||||
__proto__: AppIcon.AppIcon.prototype,
|
||||
|
||||
_init: function(appInfo, isFavorite, hasMenu) {
|
||||
AppIcon.AppIcon.prototype._init.call(this, appInfo, hasMenu ? AppIcon.MenuType.ON_RIGHT : AppIcon.MenuType.NONE);
|
||||
_init: function(appInfo, isFavorite) {
|
||||
AppIcon.AppIcon.prototype._init.call(this, { appInfo: appInfo,
|
||||
menuType: AppIcon.MenuType.ON_RIGHT,
|
||||
glow: true });
|
||||
|
||||
this.isFavorite = isFavorite;
|
||||
|
||||
@ -521,7 +545,7 @@ RunningWellItem.prototype = {
|
||||
__proto__: BaseWellItem.prototype,
|
||||
|
||||
_init: function(appInfo, isFavorite) {
|
||||
BaseWellItem.prototype._init.call(this, appInfo, isFavorite, true);
|
||||
BaseWellItem.prototype._init.call(this, appInfo, isFavorite);
|
||||
|
||||
this._dragStartX = 0;
|
||||
this._dragStartY = 0;
|
||||
@ -530,7 +554,7 @@ RunningWellItem.prototype = {
|
||||
},
|
||||
|
||||
_onActivate: function (actor, event) {
|
||||
let modifiers = event.get_state();
|
||||
let modifiers = Shell.get_event_state(event);
|
||||
|
||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
|
||||
this.appInfo.launch();
|
||||
@ -594,6 +618,12 @@ InactiveWellItem.prototype = {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
menuPoppedUp: function() {
|
||||
},
|
||||
|
||||
menuPoppedDown: function() {
|
||||
}
|
||||
};
|
||||
|
||||
@ -661,10 +691,8 @@ WellGrid.prototype = {
|
||||
childBox.y2 = childBox.y1 + itemHeight;
|
||||
children[i].allocate(childBox, flags);
|
||||
|
||||
let atSeparator = (i == this._separatorIndex - 1);
|
||||
|
||||
columnIndex++;
|
||||
if (columnIndex == columns || atSeparator) {
|
||||
if (columnIndex == columns) {
|
||||
columnIndex = 0;
|
||||
}
|
||||
|
||||
@ -674,27 +702,7 @@ WellGrid.prototype = {
|
||||
} else {
|
||||
x += itemWidth;
|
||||
}
|
||||
|
||||
if (atSeparator) {
|
||||
y += separatorNatural + WELL_ITEM_VSPACING;
|
||||
}
|
||||
}
|
||||
|
||||
let separatorRowIndex = Math.ceil(this._separatorIndex / columns);
|
||||
|
||||
/* Allocate the separator */
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.x1 = box.x1;
|
||||
childBox.y1 = (itemHeight + WELL_ITEM_VSPACING) * separatorRowIndex;
|
||||
this._cachedSeparatorY = childBox.y1;
|
||||
childBox.x2 = box.x2;
|
||||
childBox.y2 = childBox.y1+separatorNatural;
|
||||
this._separator.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
setSeparatorIndex: function (index) {
|
||||
this._separatorIndex = index;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
removeAll: function () {
|
||||
@ -702,11 +710,6 @@ WellGrid.prototype = {
|
||||
for (let i = 0; i < itemChildren.length; i++) {
|
||||
itemChildren[i].destroy();
|
||||
}
|
||||
this._separatorIndex = 0;
|
||||
},
|
||||
|
||||
isBeforeSeparator: function(x, y) {
|
||||
return y < this._cachedSeparatorY;
|
||||
},
|
||||
|
||||
_getItemChildren: function () {
|
||||
@ -724,7 +727,10 @@ WellGrid.prototype = {
|
||||
return [0, WELL_DEFAULT_COLUMNS, 0, 0];
|
||||
let nColumns = 0;
|
||||
let usedWidth = 0;
|
||||
if (forWidth < 0) {
|
||||
// Big.Box will allocate us at 0x0 if we are not visible; this is probably a
|
||||
// Big.Box bug but it can't be fixed because if children are skipped in allocate()
|
||||
// Clutter gets confused (see http://bugzilla.openedhand.com/show_bug.cgi?id=1831)
|
||||
if (forWidth <= 0) {
|
||||
nColumns = WELL_DEFAULT_COLUMNS;
|
||||
} else {
|
||||
while (nColumns < WELL_DEFAULT_COLUMNS &&
|
||||
@ -746,11 +752,10 @@ WellGrid.prototype = {
|
||||
let minWidth = itemMinWidth * nColumns;
|
||||
|
||||
let lastColumnIndex = nColumns - 1;
|
||||
let separatorColumns = lastColumnIndex - ((lastColumnIndex + this._separatorIndex) % nColumns);
|
||||
let rows = Math.ceil((children.length + separatorColumns) / nColumns);
|
||||
let rows = Math.ceil(children.length / nColumns);
|
||||
|
||||
let itemWidth;
|
||||
if (forWidth < 0) {
|
||||
if (forWidth <= 0) {
|
||||
itemWidth = itemNaturalWidth;
|
||||
} else {
|
||||
itemWidth = Math.floor(forWidth / nColumns);
|
||||
@ -794,6 +799,9 @@ 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._grid = new WellGrid();
|
||||
this.actor.append(this._grid.actor, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
@ -833,7 +841,20 @@ AppWell.prototype = {
|
||||
values[id] = index; return values; }, {});
|
||||
},
|
||||
|
||||
_onMappedNotify: function() {
|
||||
let mapped = this.actor.mapped;
|
||||
if (mapped && this._pendingRedisplay)
|
||||
this._redisplay();
|
||||
},
|
||||
|
||||
_redisplay: function () {
|
||||
let mapped = this.actor.mapped;
|
||||
if (!mapped) {
|
||||
this._pendingRedisplay = true;
|
||||
return;
|
||||
}
|
||||
this._pendingRedisplay = false;
|
||||
|
||||
this._grid.removeAll();
|
||||
|
||||
let favoriteIds = this._appSystem.get_favorites();
|
||||
@ -849,7 +870,6 @@ AppWell.prototype = {
|
||||
|
||||
let displays = []
|
||||
this._addApps(favorites, true);
|
||||
this._grid.setSeparatorIndex(favorites.length);
|
||||
this._addApps(running, false);
|
||||
this._displays = displays;
|
||||
},
|
||||
@ -872,9 +892,7 @@ AppWell.prototype = {
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
|
||||
let app = null;
|
||||
if (source instanceof BaseWellItem) {
|
||||
app = source.appInfo;
|
||||
} else if (source instanceof AppDisplayItem) {
|
||||
if (source instanceof AppDisplayItem) {
|
||||
app = appSystem.lookup_cached_app(source.getId());
|
||||
} else if (source instanceof Workspaces.WindowClone) {
|
||||
let appMonitor = Shell.AppMonitor.get_default();
|
||||
@ -891,22 +909,15 @@ AppWell.prototype = {
|
||||
let favoriteIds = this._appSystem.get_favorites();
|
||||
let favoriteIdsObject = this._arrayValues(favoriteIds);
|
||||
|
||||
let dropIsFavorite = this._grid.isBeforeSeparator(x - this._grid.actor.x,
|
||||
y - this._grid.actor.y);
|
||||
let srcIsFavorite = (id in favoriteIdsObject);
|
||||
|
||||
if (srcIsFavorite && (!dropIsFavorite)) {
|
||||
Mainloop.idle_add(function () {
|
||||
appSystem.remove_favorite(id);
|
||||
return false;
|
||||
});
|
||||
} else if ((!srcIsFavorite) && dropIsFavorite) {
|
||||
if (srcIsFavorite) {
|
||||
return false;
|
||||
} else {
|
||||
Mainloop.idle_add(function () {
|
||||
appSystem.add_favorite(id);
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
198
js/ui/appIcon.js
198
js/ui/appIcon.js
@ -20,7 +20,7 @@ GLOW_COLOR.from_pixel(0x4f6ba4ff);
|
||||
const GLOW_PADDING_HORIZONTAL = 3;
|
||||
const GLOW_PADDING_VERTICAL = 3;
|
||||
|
||||
const APPICON_ICON_SIZE = 48;
|
||||
const APPICON_DEFAULT_ICON_SIZE = 48;
|
||||
|
||||
const APPICON_PADDING = 1;
|
||||
const APPICON_BORDER_WIDTH = 1;
|
||||
@ -49,14 +49,19 @@ TRANSPARENT_COLOR.from_pixel(0x00000000);
|
||||
|
||||
const MenuType = { NONE: 0, ON_RIGHT: 1, BELOW: 2 };
|
||||
|
||||
function AppIcon(appInfo, menuType) {
|
||||
this._init(appInfo, menuType || MenuType.NONE);
|
||||
function AppIcon(params) {
|
||||
this._init(params);
|
||||
}
|
||||
|
||||
AppIcon.prototype = {
|
||||
_init : function(appInfo, menuType) {
|
||||
this.appInfo = appInfo;
|
||||
this._menuType = menuType;
|
||||
_init : function(params) {
|
||||
this.appInfo = params.appInfo;
|
||||
if (!this.appInfo)
|
||||
throw new Error('AppIcon constructor requires "appInfo" param');
|
||||
|
||||
this._menuType = ('menuType' in params) ? params.menuType : MenuType.NONE;
|
||||
this._iconSize = ('size' in params) ? params.size : APPICON_DEFAULT_ICON_SIZE;
|
||||
let showGlow = ('glow' in params) ? params.glow : false;
|
||||
|
||||
this.actor = new Shell.ButtonBox({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
border: APPICON_BORDER_WIDTH,
|
||||
@ -65,29 +70,36 @@ AppIcon.prototype = {
|
||||
reactive: true });
|
||||
this.actor._delegate = this;
|
||||
this.highlight_border_color = APPICON_DEFAULT_BORDER_COLOR;
|
||||
this._signalIds = [];
|
||||
|
||||
if (menuType != MenuType.NONE) {
|
||||
this.windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.get_id());
|
||||
for (let i = 0; i < this.windows.length; i++) {
|
||||
this.windows[i].connect('notify::user-time', Lang.bind(this, this._resortWindows));
|
||||
}
|
||||
this._resortWindows();
|
||||
// Note, we don't presently update the window list dynamically here; this actor
|
||||
// gets destroyed and recreated by AppWell, and in alt-tab the whole thing is
|
||||
// created each time
|
||||
this.windows = Shell.AppMonitor.get_default().get_windows_for_app(this.appInfo.get_id());
|
||||
for (let i = 0; i < this.windows.length; i++) {
|
||||
let sigId = this.windows[i].connect('notify::user-time', Lang.bind(this, this._resortWindows));
|
||||
this._signalIds.push([this.windows[i], sigId]);
|
||||
}
|
||||
this._resortWindows();
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._destroy));
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedChanged));
|
||||
|
||||
if (this._menuType != MenuType.NONE) {
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._updateMenuOnButtonPress));
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this._updateMenuOnHoverChanged));
|
||||
this.actor.connect('activate', Lang.bind(this, this._updateMenuOnActivate));
|
||||
|
||||
this._menuTimeoutId = 0;
|
||||
this._menu = null;
|
||||
} else
|
||||
this.windows = [];
|
||||
}
|
||||
|
||||
let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
x_align: Big.BoxAlignment.CENTER,
|
||||
y_align: Big.BoxAlignment.CENTER,
|
||||
width: APPICON_ICON_SIZE,
|
||||
height: APPICON_ICON_SIZE });
|
||||
this.icon = appInfo.create_icon_texture(APPICON_ICON_SIZE);
|
||||
width: this._iconSize,
|
||||
height: this._iconSize });
|
||||
this.icon = this.appInfo.create_icon_texture(this._iconSize);
|
||||
iconBox.append(this.icon, Big.BoxPackFlags.NONE);
|
||||
|
||||
this.actor.append(iconBox, Big.BoxPackFlags.EXPAND);
|
||||
@ -102,18 +114,23 @@ AppIcon.prototype = {
|
||||
font_name: "Sans 12px",
|
||||
line_alignment: Pango.Alignment.CENTER,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
text: appInfo.get_name() });
|
||||
text: this.appInfo.get_name() });
|
||||
nameBox.add_actor(this._name);
|
||||
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
|
||||
for (let i = 0; i < this.windows.length && i < 3; i++) {
|
||||
let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
||||
glowPath, -1, -1);
|
||||
glow.keep_aspect_ratio = false;
|
||||
this._glowBox.append(glow, Big.BoxPackFlags.EXPAND);
|
||||
if (showGlow) {
|
||||
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
||||
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
|
||||
for (let i = 0; i < this.windows.length && i < 3; i++) {
|
||||
let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
||||
glowPath, -1, -1);
|
||||
glow.keep_aspect_ratio = false;
|
||||
this._glowBox.append(glow, Big.BoxPackFlags.EXPAND);
|
||||
}
|
||||
this._nameBox.add_actor(this._glowBox);
|
||||
this._glowBox.lower(this._name);
|
||||
}
|
||||
this._nameBox.add_actor(this._glowBox);
|
||||
this._glowBox.lower(this._name);
|
||||
else
|
||||
this._glowBox = null;
|
||||
|
||||
this.actor.append(nameBox, Big.BoxPackFlags.NONE);
|
||||
},
|
||||
|
||||
@ -158,8 +175,36 @@ AppIcon.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_destroy: function() {
|
||||
for (let i = 0; i < this._signalIds.length; i++) {
|
||||
let [obj, sigId] = this._signalIds[i];
|
||||
obj.disconnect(sigId);
|
||||
}
|
||||
},
|
||||
|
||||
_onMappedChanged: function() {
|
||||
let mapped = this.actor.mapped;
|
||||
if (mapped && this._windowSortStale)
|
||||
this._resortWindows();
|
||||
},
|
||||
|
||||
_resortWindows: function() {
|
||||
let mapped = this.actor.mapped;
|
||||
if (!mapped) {
|
||||
this._windowSortStale = true;
|
||||
return;
|
||||
}
|
||||
this._windowSortStale = false;
|
||||
this.windows.sort(function (a, b) {
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
let wsA = a.get_workspace() == activeWorkspace;
|
||||
let wsB = b.get_workspace() == activeWorkspace;
|
||||
|
||||
if (wsA && !wsB)
|
||||
return -1;
|
||||
else if (wsB && !wsA)
|
||||
return 1;
|
||||
|
||||
let visA = a.showing_on_its_workspace();
|
||||
let visB = b.showing_on_its_workspace();
|
||||
|
||||
@ -176,7 +221,7 @@ AppIcon.prototype = {
|
||||
// a subclass of it draggable, you can use this method to create
|
||||
// a drag actor
|
||||
createDragActor: function() {
|
||||
return this.appInfo.create_icon_texture(APPICON_ICON_SIZE);
|
||||
return this.appInfo.create_icon_texture(this._iconSize);
|
||||
},
|
||||
|
||||
setHighlight: function(highlight) {
|
||||
@ -205,14 +250,19 @@ AppIcon.prototype = {
|
||||
},
|
||||
|
||||
_updateMenuOnButtonPress: function(actor, event) {
|
||||
if (this._menuTimeoutId != 0)
|
||||
Mainloop.source_remove(this._menuTimeoutId);
|
||||
this._menuTimeoutId = Mainloop.timeout_add(APPICON_MENU_POPUP_TIMEOUT_MS,
|
||||
Lang.bind(this, this.popupMenu));
|
||||
let button = event.get_button();
|
||||
if (button == 1) {
|
||||
if (this._menuTimeoutId != 0)
|
||||
Mainloop.source_remove(this._menuTimeoutId);
|
||||
this._menuTimeoutId = Mainloop.timeout_add(APPICON_MENU_POPUP_TIMEOUT_MS,
|
||||
Lang.bind(this, function () { this.popupMenu(button); }));
|
||||
} else if (button == 3) {
|
||||
this.popupMenu(button);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
popupMenu: function() {
|
||||
popupMenu: function(activatingButton) {
|
||||
if (this._menuTimeoutId != 0) {
|
||||
Mainloop.source_remove(this._menuTimeoutId);
|
||||
this._menuTimeoutId = 0;
|
||||
@ -236,7 +286,7 @@ AppIcon.prototype = {
|
||||
}));
|
||||
}
|
||||
|
||||
this._menu.popup();
|
||||
this._menu.popup(activatingButton);
|
||||
|
||||
return false;
|
||||
},
|
||||
@ -299,7 +349,7 @@ AppIconMenu.prototype = {
|
||||
this._arrow = new Shell.DrawingArea();
|
||||
this._arrow.connect('redraw', Lang.bind(this, function (area, texture) {
|
||||
Shell.draw_box_pointer(texture,
|
||||
this._type == MenuType.ON_RIGHT ? Clutter.Gravity.WEST : Clutter.Gravity.NORTH,
|
||||
this._type == MenuType.ON_RIGHT ? Shell.PointerDirection.LEFT : Shell.PointerDirection.UP,
|
||||
source.highlight_border_color,
|
||||
APPICON_MENU_BACKGROUND_COLOR);
|
||||
}));
|
||||
@ -377,32 +427,53 @@ AppIconMenu.prototype = {
|
||||
|
||||
let iconsDiffer = false;
|
||||
let texCache = Shell.TextureCache.get_default();
|
||||
let firstIcon = windows[0].mini_icon;
|
||||
for (let i = 1; i < windows.length; i++) {
|
||||
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
|
||||
iconsDiffer = true;
|
||||
break;
|
||||
if (windows.length > 0) {
|
||||
let firstIcon = windows[0].mini_icon;
|
||||
for (let i = 1; i < windows.length; i++) {
|
||||
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
|
||||
iconsDiffer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display the app windows menu items and the separator between windows
|
||||
// of the current desktop and other windows.
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
|
||||
|
||||
let currentWorkspaceWindows = windows.filter(function (w) {
|
||||
return w.get_workspace() == activeWorkspace;
|
||||
});
|
||||
let otherWorkspaceWindows = windows.filter(function (w) {
|
||||
return w.get_workspace() != activeWorkspace;
|
||||
});
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
|
||||
this._appendSeparator();
|
||||
separatorShown = true;
|
||||
}
|
||||
|
||||
this._appendWindows(currentWorkspaceWindows, iconsDiffer);
|
||||
if (currentWorkspaceWindows.length > 0 && otherWorkspaceWindows.length > 0) {
|
||||
this._appendSeparator();
|
||||
let icon = null;
|
||||
if (iconsDiffer)
|
||||
icon = Shell.TextureCache.get_default().bind_pixbuf_property(windows[i], "mini-icon");
|
||||
|
||||
let box = this._appendMenuItem(icon, windows[i].title);
|
||||
box._window = windows[i];
|
||||
}
|
||||
this._appendWindows(otherWorkspaceWindows, iconsDiffer);
|
||||
|
||||
this._appendSeparator();
|
||||
if (windows.length > 0)
|
||||
this._appendSeparator();
|
||||
|
||||
this._newWindowMenuItem = this._appendMenuItem(null, _("New Window"));
|
||||
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(null, _("New Window")) : null;
|
||||
|
||||
let favorites = Shell.AppSystem.get_default().get_favorites();
|
||||
let id = this._source.appInfo.get_id();
|
||||
this._isFavorite = false;
|
||||
for (let i = 0; i < favorites.length; i++) {
|
||||
if (id == favorites[i]) {
|
||||
this._isFavorite = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (windows.length > 0)
|
||||
this._appendSeparator();
|
||||
this._toggleFavoriteMenuItem = this._appendMenuItem(null, this._isFavorite ? _("Remove from favorites")
|
||||
: _("Add to favorites"));
|
||||
|
||||
this._highlightedItem = null;
|
||||
},
|
||||
@ -441,26 +512,13 @@ AppIconMenu.prototype = {
|
||||
return box;
|
||||
},
|
||||
|
||||
_appendWindows: function (windows, iconsDiffer) {
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let metaWindow = windows[i];
|
||||
|
||||
let icon = null;
|
||||
if (iconsDiffer) {
|
||||
icon = Shell.TextureCache.get_default().bind_pixbuf_property(metaWindow, "mini-icon");
|
||||
}
|
||||
let box = this._appendMenuItem(icon, metaWindow.title);
|
||||
box._window = metaWindow;
|
||||
}
|
||||
},
|
||||
|
||||
popup: function() {
|
||||
popup: function(activatingButton) {
|
||||
let [stageX, stageY] = this._source.actor.get_transformed_position();
|
||||
let [stageWidth, stageHeight] = this._source.actor.get_transformed_size();
|
||||
|
||||
this._redisplay();
|
||||
|
||||
this._windowContainer.popup(0, Main.currentTime());
|
||||
this._windowContainer.popup(activatingButton, Main.currentTime());
|
||||
|
||||
this.emit('popup', true);
|
||||
|
||||
@ -559,6 +617,12 @@ AppIconMenu.prototype = {
|
||||
} else if (child == this._newWindowMenuItem) {
|
||||
this._source.appInfo.launch();
|
||||
this.emit('activate-window', null);
|
||||
} else if (child == this._toggleFavoriteMenuItem) {
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
if (this._isFavorite)
|
||||
appSys.remove_favorite(this._source.appInfo.get_id());
|
||||
else
|
||||
appSys.add_favorite(this._source.appInfo.get_id());
|
||||
}
|
||||
this.popdown();
|
||||
},
|
||||
|
177
js/ui/calendar.js
Normal file
177
js/ui/calendar.js
Normal file
@ -0,0 +1,177 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Gettext_gtk20 = imports.gettext.domain('gtk20');
|
||||
|
||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||
|
||||
function _sameDay(dateA, dateB) {
|
||||
return (dateA.getDate() == dateB.getDate() &&
|
||||
dateA.getMonth() == dateB.getMonth() &&
|
||||
dateA.getYear() == dateB.getYear());
|
||||
}
|
||||
|
||||
function Calendar() {
|
||||
this._init();
|
||||
};
|
||||
|
||||
Calendar.prototype = {
|
||||
_init: function() {
|
||||
// FIXME: This is actually the fallback method for GTK+ for the week start;
|
||||
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
|
||||
// should add a C function so we can do the full handling.
|
||||
this._weekStart = NaN;
|
||||
let weekStartString = Gettext_gtk20.gettext("calendar:week_start:0");
|
||||
if (weekStartString.indexOf("calendar:week_start:") == 0) {
|
||||
this._weekStart = parseInt(weekStartString.substring(20));
|
||||
}
|
||||
|
||||
if (isNaN(this._weekStart) || this._weekStart < 0 || this._weekStart > 6) {
|
||||
log("Translation of 'calendar:week_start:0' in GTK+ is not correct");
|
||||
this.weekStart = 0;
|
||||
}
|
||||
|
||||
// Find the ordering for month/year in the calendar heading
|
||||
switch (Gettext_gtk20.gettext("calendar:MY")) {
|
||||
case "calendar:MY":
|
||||
this._headerFormat = "%B %Y";
|
||||
break;
|
||||
case "calendar:YM":
|
||||
this._headerFormat = "%Y %B";
|
||||
break;
|
||||
default:
|
||||
log("Translation of 'calendar:MY' in GTK+ is not correct");
|
||||
this._headerFormat = "%B %Y";
|
||||
break;
|
||||
}
|
||||
|
||||
// Start off with the current date
|
||||
this.date = new Date();
|
||||
|
||||
this.actor = new St.Table({ homogeneous: false,
|
||||
style_class: "calendar",
|
||||
reactive: true });
|
||||
|
||||
this.actor.connect('scroll-event',
|
||||
Lang.bind(this, this._onScroll));
|
||||
|
||||
// Top line of the calendar '<| September 2009 |>'
|
||||
this._topBox = new St.BoxLayout();
|
||||
this.actor.add(this._topBox,
|
||||
{ row: 0, col: 0, col_span: 7 });
|
||||
|
||||
let back = new St.Button({ label: "<", style_class: 'calendar-change-month' });
|
||||
this._topBox.add(back);
|
||||
back.connect("clicked", Lang.bind(this, this._prevMonth));
|
||||
|
||||
this._dateLabel = new St.Label();
|
||||
this._topBox.add(this._dateLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||
|
||||
let forward = new St.Button({ label: ">", style_class: 'calendar-change-month' });
|
||||
this._topBox.add(forward);
|
||||
forward.connect("clicked", Lang.bind(this, this._nextMonth));
|
||||
|
||||
// We need to figure out the abbreviated localized names for the days of the week;
|
||||
// we do this by just getting the next 7 days starting from right now and then putting
|
||||
// them in the right cell in the table. It doesn't matter if we add them in order
|
||||
let iter = new Date(this.date);
|
||||
iter.setSeconds(0); // Leap second protection. Hah!
|
||||
iter.setHours(12);
|
||||
for (let i = 0; i < 7; i++) {
|
||||
this.actor.add(new St.Label({ text: iter.toLocaleFormat("%a") }),
|
||||
{ row: 1,
|
||||
col: (7 + iter.getDay() - this._weekStart) % 7,
|
||||
x_fill: false, x_align: 1.0 });
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
}
|
||||
|
||||
// All the children after this are days, and get removed when we update the calendar
|
||||
this._firstDayIndex = this.actor.get_children().length;
|
||||
|
||||
this._update();
|
||||
},
|
||||
|
||||
// Sets the calendar to show a specific date
|
||||
setDate: function(date) {
|
||||
if (!_sameDay(date, this.date)) {
|
||||
this.date = date;
|
||||
this._update();
|
||||
}
|
||||
},
|
||||
|
||||
_onScroll : function(actor, event) {
|
||||
switch (event.get_scroll_direction()) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
case Clutter.ScrollDirection.LEFT:
|
||||
this._prevMonth();
|
||||
break;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
case Clutter.ScrollDirection.RIGHT:
|
||||
this._nextMonth();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_prevMonth: function() {
|
||||
if (this.date.getMonth() == 0) {
|
||||
this.date.setMonth(11);
|
||||
this.date.setFullYear(this.date.getFullYear() - 1);
|
||||
} else {
|
||||
this.date.setMonth(this.date.getMonth() - 1);
|
||||
}
|
||||
this._update();
|
||||
},
|
||||
|
||||
_nextMonth: function() {
|
||||
if (this.date.getMonth() == 11) {
|
||||
this.date.setMonth(0);
|
||||
this.date.setFullYear(this.date.getFullYear() + 1);
|
||||
} else {
|
||||
this.date.setMonth(this.date.getMonth() + 1);
|
||||
}
|
||||
this._update();
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat);
|
||||
|
||||
// Remove everything but the topBox and the weekday labels
|
||||
let children = this.actor.get_children();
|
||||
for (let i = this._firstDayIndex; i < children.length; i++)
|
||||
children[i].destroy();
|
||||
|
||||
// Start at the beginning of the week before the start of the month
|
||||
let iter = new Date(this.date);
|
||||
iter.setDate(1);
|
||||
iter.setSeconds(0);
|
||||
iter.setHours(12);
|
||||
iter.setTime(iter.getTime() - (iter.getDay() - this._weekStart) * MSECS_IN_DAY);
|
||||
|
||||
let now = new Date();
|
||||
|
||||
let row = 2;
|
||||
while (true) {
|
||||
let label = new St.Label({ text: iter.getDate().toString() });
|
||||
if (_sameDay(now, iter))
|
||||
label.style_class = "calendar-day calendar-today";
|
||||
else if (iter.getMonth() != this.date.getMonth())
|
||||
label.style_class = "calendar-day calendar-other-month-day";
|
||||
else
|
||||
label.style_class = "calendar-day";
|
||||
this.actor.add(label,
|
||||
{ row: row, col: (7 + iter.getDay() - this._weekStart) % 7,
|
||||
x_fill: false, x_align: 1.0 });
|
||||
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
if (iter.getDay() == this._weekStart) {
|
||||
// We stop on the first "first day of the week" after the month we are displaying
|
||||
if (iter.getMonth() > this.date.getMonth() || iter.getYear() > this.date.getYear())
|
||||
break;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
@ -889,6 +889,9 @@ Dash.prototype = {
|
||||
_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();
|
||||
@ -897,13 +900,7 @@ Dash.prototype = {
|
||||
this._appsSection.actor.hide();
|
||||
this._placesSection.actor.hide();
|
||||
this._docsSection.actor.hide();
|
||||
} else if (this._searchActive) {
|
||||
for (let i = 0; i < this._searchSections.length; i++) {
|
||||
let section = this._searchSections[i];
|
||||
section.header.actor.show();
|
||||
section.resultArea.actor.show();
|
||||
}
|
||||
} else {
|
||||
} else if (!this._searchActive) {
|
||||
this._showAllSearchSections();
|
||||
this._searchResultsSection.actor.hide();
|
||||
this._appsSection.actor.show();
|
||||
|
@ -136,7 +136,7 @@ DocDisplay.prototype = {
|
||||
// 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.
|
||||
this._redisplay(false);
|
||||
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
|
||||
}));
|
||||
|
||||
this.connect('destroy', Lang.bind(this, function (o) {
|
||||
|
@ -4,6 +4,8 @@ const St = imports.gi.St;
|
||||
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const Format = imports.misc.format;
|
||||
|
||||
// "monkey patch" in some varargs ClutterContainer methods; we need
|
||||
// to do this per-container class since there is no representation
|
||||
// of interfaces in Javascript
|
||||
@ -30,4 +32,5 @@ _patchContainerClass(St.Table);
|
||||
|
||||
function init() {
|
||||
Tweener.init();
|
||||
String.prototype.format = Format.format;
|
||||
}
|
||||
|
@ -370,7 +370,11 @@ GenericDisplay.prototype = {
|
||||
return;
|
||||
let flags = RedisplayFlags.RESET_CONTROLS;
|
||||
if (this._search != '') {
|
||||
if (lowertext.indexOf(this._search) == 0)
|
||||
// Because we combine search terms with OR, we have to be sure that no new term
|
||||
// was introduced before deciding that the new search results will be a subset of
|
||||
// the existing search results.
|
||||
if (lowertext.indexOf(this._search) == 0 &&
|
||||
lowertext.split(/\s+/).length == this._search.split(/\s+/).length)
|
||||
flags |= RedisplayFlags.SUBSEARCH;
|
||||
}
|
||||
this._search = lowertext;
|
||||
@ -486,15 +490,16 @@ GenericDisplay.prototype = {
|
||||
|
||||
//// Protected methods ////
|
||||
|
||||
_redisplayFull: function() {
|
||||
_recreateDisplayItems: function() {
|
||||
this._removeAllDisplayItems();
|
||||
this._setDefaultList();
|
||||
for (let itemId in this._allItems) {
|
||||
this._addDisplayItem(itemId);
|
||||
}
|
||||
},
|
||||
|
||||
// Creates a display item based on the information associated with itemId
|
||||
// and adds it to the displayed items.
|
||||
// and adds it to the list of displayed items, but does not yet display it.
|
||||
_addDisplayItem : function(itemId) {
|
||||
if (this._displayedItems.hasOwnProperty(itemId)) {
|
||||
log("Tried adding a display item for " + itemId + ", but an item with this item id is already among displayed items.");
|
||||
@ -525,7 +530,6 @@ GenericDisplay.prototype = {
|
||||
this.emit('show-details', index);
|
||||
}
|
||||
}));
|
||||
this._list.add_actor(displayItem.actor);
|
||||
this._displayedItems[itemId] = displayItem;
|
||||
},
|
||||
|
||||
@ -635,6 +639,7 @@ GenericDisplay.prototype = {
|
||||
* their own while the user was browsing through the result pages.
|
||||
* SUBSEARCH - Indicates that the current _search is a superstring of the previous
|
||||
* one, which implies we only need to re-search through previous results.
|
||||
* FULL - Indicates that we need refresh all displayed items.
|
||||
*/
|
||||
_redisplay: function(flags) {
|
||||
let resetPage = (flags & RedisplayFlags.RESET_CONTROLS) > 0;
|
||||
@ -642,13 +647,20 @@ GenericDisplay.prototype = {
|
||||
let fullReload = (flags & RedisplayFlags.FULL) > 0;
|
||||
|
||||
let hadSelected = this.hasSelected();
|
||||
this.unsetSelected();
|
||||
|
||||
if (!this._initialLoadComplete || !this._refreshCache())
|
||||
if (!this._initialLoadComplete)
|
||||
fullReload = true;
|
||||
|
||||
if (!this._refreshCache())
|
||||
fullReload = true;
|
||||
|
||||
if (fullReload) {
|
||||
this._recreateDisplayItems();
|
||||
this._initialLoadComplete = true;
|
||||
this._redisplayFull();
|
||||
} if (isSubSearch) {
|
||||
}
|
||||
|
||||
if (isSubSearch) {
|
||||
this._redisplaySubSearch();
|
||||
} else {
|
||||
this._redisplayReordering();
|
||||
|
@ -231,12 +231,13 @@ function Inspector() {
|
||||
Inspector.prototype = {
|
||||
_init: function() {
|
||||
let width = 150;
|
||||
let primary = global.get_primary_monitor();
|
||||
let eventHandler = new St.BoxLayout({ name: "LookingGlassDialog",
|
||||
vertical: false,
|
||||
y: Math.floor(global.stage.height/2),
|
||||
y: primary.y + Math.floor(primary.height / 2),
|
||||
reactive: true });
|
||||
eventHandler.connect('notify::allocation', Lang.bind(this, function () {
|
||||
eventHandler.x = Math.floor((global.stage.width)/2 - (eventHandler.width)/2);
|
||||
eventHandler.x = primary.x + Math.floor((primary.width - eventHandler.width) / 2);
|
||||
}));
|
||||
global.stage.add_actor(eventHandler);
|
||||
let displayText = new St.Label();
|
||||
@ -501,11 +502,11 @@ LookingGlass.prototype = {
|
||||
},
|
||||
|
||||
_resizeTo: function(actor) {
|
||||
let stage = global.stage;
|
||||
let myWidth = stage.width * 0.7;
|
||||
let myHeight = stage.height * 0.7;
|
||||
let primary = global.get_primary_monitor();
|
||||
let myWidth = primary.width * 0.7;
|
||||
let myHeight = primary.height * 0.7;
|
||||
let [srcX, srcY] = actor.get_transformed_position();
|
||||
this.actor.x = srcX + (stage.width-myWidth)/2;
|
||||
this.actor.x = srcX + (primary.width - myWidth) / 2;
|
||||
this._hiddenY = srcY + actor.height - myHeight - 4; // -4 to hide the top corners
|
||||
this._targetY = this._hiddenY + myHeight;
|
||||
this.actor.y = this._hiddenY;
|
||||
|
@ -127,7 +127,9 @@ function start() {
|
||||
}
|
||||
|
||||
function _relayout() {
|
||||
panel.actor.set_size(global.screen_width, Panel.PANEL_HEIGHT);
|
||||
let primary = global.get_primary_monitor();
|
||||
panel.actor.set_position(primary.x, primary.y);
|
||||
panel.actor.set_size(primary.width, Panel.PANEL_HEIGHT);
|
||||
overview.relayout();
|
||||
}
|
||||
|
||||
@ -199,7 +201,7 @@ function _globalKeyPressHandler(actor, event) {
|
||||
overview.hide();
|
||||
|
||||
return true;
|
||||
} else if (symbol == Clutter.F2 && (event.get_state() & Clutter.ModifierType.MOD1_MASK)) {
|
||||
} else if (symbol == Clutter.F2 && (Shell.get_event_state(event) & Clutter.ModifierType.MOD1_MASK)) {
|
||||
getRunDialog().open();
|
||||
}
|
||||
}
|
||||
|
@ -142,42 +142,44 @@ Overview.prototype = {
|
||||
},
|
||||
|
||||
_recalculateGridSizes: function () {
|
||||
wideScreen = (global.screen_width/global.screen_height > WIDE_SCREEN_CUT_OFF_RATIO) &&
|
||||
(global.screen_height >= WIDE_SCREEN_MINIMUM_HEIGHT);
|
||||
let primary = global.get_primary_monitor();
|
||||
wideScreen = (primary.width/primary.height > WIDE_SCREEN_CUT_OFF_RATIO) &&
|
||||
(primary.height >= WIDE_SCREEN_MINIMUM_HEIGHT);
|
||||
|
||||
// We divide the screen into an imaginary grid which helps us determine the layout of
|
||||
// different visual components.
|
||||
if (wideScreen) {
|
||||
displayGridColumnWidth = global.screen_width / COLUMNS_WIDE_SCREEN;
|
||||
displayGridRowHeight = global.screen_height / ROWS_WIDE_SCREEN;
|
||||
displayGridColumnWidth = primary.width / COLUMNS_WIDE_SCREEN;
|
||||
displayGridRowHeight = primary.height / ROWS_WIDE_SCREEN;
|
||||
} else {
|
||||
displayGridColumnWidth = global.screen_width / COLUMNS_REGULAR_SCREEN;
|
||||
displayGridRowHeight = global.screen_height / ROWS_REGULAR_SCREEN;
|
||||
displayGridColumnWidth = primary.width / COLUMNS_REGULAR_SCREEN;
|
||||
displayGridRowHeight = primary.height / ROWS_REGULAR_SCREEN;
|
||||
}
|
||||
},
|
||||
|
||||
relayout: function () {
|
||||
let screenHeight = global.screen_height;
|
||||
let screenWidth = global.screen_width;
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
this._group.set_position(primary.x, primary.y);
|
||||
|
||||
let contentY = Panel.PANEL_HEIGHT;
|
||||
let contentHeight = screenHeight - contentY;
|
||||
let contentHeight = primary.height - contentY;
|
||||
|
||||
this._coverPane.set_position(0, contentY);
|
||||
this._coverPane.set_size(screenWidth, contentHeight);
|
||||
this._coverPane.set_size(primary.width, contentHeight);
|
||||
|
||||
let workspaceColumnsUsed = wideScreen ? COLUMNS_FOR_WORKSPACES_WIDE_SCREEN : COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN;
|
||||
let workspaceRowsUsed = wideScreen ? ROWS_FOR_WORKSPACES_WIDE_SCREEN : ROWS_FOR_WORKSPACES_REGULAR_SCREEN;
|
||||
|
||||
this._workspacesWidth = displayGridColumnWidth * workspaceColumnsUsed
|
||||
- WORKSPACE_GRID_PADDING * 2;
|
||||
// We scale the vertical padding by (screenHeight / screenWidth)
|
||||
// We scale the vertical padding by (primary.height / primary.width)
|
||||
// so that the workspace preserves its aspect ratio.
|
||||
this._workspacesHeight = displayGridRowHeight * workspaceRowsUsed
|
||||
- WORKSPACE_GRID_PADDING * (screenHeight / screenWidth) * 2;
|
||||
- WORKSPACE_GRID_PADDING * (primary.height / primary.width) * 2;
|
||||
|
||||
this._workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING;
|
||||
this._workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (screenHeight / screenWidth);
|
||||
this._workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (primary.height / primary.width);
|
||||
|
||||
this._dash.actor.set_position(0, contentY);
|
||||
this._dash.actor.set_size(displayGridColumnWidth, contentHeight);
|
||||
@ -187,10 +189,12 @@ Overview.prototype = {
|
||||
// place the 'Add Workspace' button in the bottom row of the grid
|
||||
addRemoveButtonSize = Math.floor(displayGridRowHeight * 3/5);
|
||||
this._addButtonX = this._workspacesX + this._workspacesWidth - addRemoveButtonSize;
|
||||
this._addButtonY = screenHeight - Math.floor(displayGridRowHeight * 4/5);
|
||||
this._addButtonY = primary.height - Math.floor(displayGridRowHeight * 4/5);
|
||||
|
||||
this._backOver.set_position(0, contentY);
|
||||
this._backOver.set_size(global.screen_width, contentHeight);
|
||||
// The parent (this._group) is positioned at the top left of the primary monitor
|
||||
// while this._backOver occupies the entire screen.
|
||||
this._backOver.set_position(- primary.x, - primary.y);
|
||||
this._backOver.set_size(global.screen_width, global.screen_height);
|
||||
|
||||
this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING,
|
||||
contentY);
|
||||
@ -198,7 +202,7 @@ Overview.prototype = {
|
||||
this._paneContainer.height = contentHeight;
|
||||
|
||||
this._transparentBackground.set_position(this._paneContainer.x, this._paneContainer.y);
|
||||
this._transparentBackground.set_size(global.screen_width - this._paneContainer.x,
|
||||
this._transparentBackground.set_size(primary.width - this._paneContainer.x,
|
||||
this._paneContainer.height);
|
||||
|
||||
if (this._activeDisplayPane != null)
|
||||
@ -322,9 +326,10 @@ Overview.prototype = {
|
||||
// The opposite transition is used in hide().
|
||||
this._group.scaleX = this._group.scaleY = this.getZoomedInScale();
|
||||
[this._group.x, this._group.y] = this.getZoomedInPosition();
|
||||
let primary = global.get_primary_monitor();
|
||||
Tweener.addTween(this._group,
|
||||
{ x: 0,
|
||||
y: 0,
|
||||
{ x: primary.x,
|
||||
y: primary.y,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
transition: 'easeOutQuad',
|
||||
@ -355,6 +360,10 @@ Overview.prototype = {
|
||||
this._activeDisplayPane.close();
|
||||
this._workspaces.hide();
|
||||
|
||||
this._addButton.actor.destroy();
|
||||
this._addButton.actor = null;
|
||||
this._addButton = null;
|
||||
|
||||
// Create a zoom in effect by transforming the Overview group so that
|
||||
// the active workspace fills up the whole screen. The opposite
|
||||
// transition is used in show().
|
||||
|
@ -7,12 +7,14 @@ const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Button = imports.ui.button;
|
||||
const Calendar = imports.ui.calendar;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const PANEL_HEIGHT = 26;
|
||||
@ -53,12 +55,13 @@ const TRAY_SPACING_MIN = 8;
|
||||
// Used for the tray icon container with gtk pre-2.16, which doesn't
|
||||
// fully support tray icon transparency
|
||||
const TRAY_BACKGROUND_COLOR = new Clutter.Color();
|
||||
TRAY_BACKGROUND_COLOR.from_pixel(0xefefefff);
|
||||
TRAY_BACKGROUND_COLOR.from_pixel(0x0b0b0bff);
|
||||
const TRAY_BORDER_COLOR = new Clutter.Color();
|
||||
TRAY_BORDER_COLOR.from_pixel(0x00000033);
|
||||
const TRAY_CORNER_RADIUS = 5;
|
||||
const TRAY_BORDER_WIDTH = 0;
|
||||
|
||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||
|
||||
function AppPanelMenu() {
|
||||
this._init();
|
||||
@ -288,6 +291,8 @@ Panel.prototype = {
|
||||
opacity: 0,
|
||||
reactive: true });
|
||||
|
||||
this._hotCornerActivationTime = 0;
|
||||
|
||||
this._hotCornerEnvirons.connect('leave-event',
|
||||
Lang.bind(this, this._onHotCornerEnvironsLeft));
|
||||
// Clicking on the hot corner environs should result in the same bahavior
|
||||
@ -313,10 +318,17 @@ Panel.prototype = {
|
||||
|
||||
/* center */
|
||||
|
||||
let clockButton = new St.Button({ style_class: "panel-button",
|
||||
toggle_mode: true });
|
||||
this._centerBox.append(clockButton, Big.BoxPackFlags.NONE);
|
||||
clockButton.connect('clicked', Lang.bind(this, this._toggleCalendar));
|
||||
|
||||
this._clock = new Clutter.Text({ font_name: DEFAULT_FONT,
|
||||
color: PANEL_FOREGROUND_COLOR,
|
||||
text: "" });
|
||||
this._centerBox.append(this._clock, Big.BoxPackFlags.NONE);
|
||||
clockButton.add_actor(this._clock);
|
||||
|
||||
this._calendarPopup = null;
|
||||
|
||||
/* right */
|
||||
|
||||
@ -395,14 +407,14 @@ Panel.prototype = {
|
||||
// We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
|
||||
// have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
|
||||
// to switch to.
|
||||
this.button.actor.connect('button-press-event', function(b, e) {
|
||||
if (e.get_button() == 1 && e.get_click_count() == 1) {
|
||||
Main.overview.toggle();
|
||||
this.button.actor.connect('button-press-event', Lang.bind(this, function(b, e) {
|
||||
if (e.get_button() == 1 && e.get_click_count() == 1 && !Main.overview.animationInProgress) {
|
||||
this._maybeToggleOverviewOnClick();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}));
|
||||
// In addition to pressing the button, the Overview can be entered and exited by other means, such as
|
||||
// pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered
|
||||
// and to be released when it is exited regardless of how it was triggered.
|
||||
@ -454,10 +466,21 @@ Panel.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
_toggleCalendar: function(clockButton) {
|
||||
if (clockButton.checked) {
|
||||
if (this._calendarPopup == null)
|
||||
this._calendarPopup = new CalendarPopup();
|
||||
this._calendarPopup.show();
|
||||
} else {
|
||||
this._calendarPopup.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_onHotCornerEntered : function() {
|
||||
if (!this._hotCornerEntered) {
|
||||
this._hotCornerEntered = true;
|
||||
if (!Main.overview.animationInProgress) {
|
||||
this._hotCornerActivationTime = Date.now() / 1000;
|
||||
Main.overview.toggle();
|
||||
}
|
||||
}
|
||||
@ -466,7 +489,7 @@ Panel.prototype = {
|
||||
|
||||
_onHotCornerClicked : function() {
|
||||
if (!Main.overview.animationInProgress) {
|
||||
Main.overview.toggle();
|
||||
this._maybeToggleOverviewOnClick();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
@ -483,5 +506,65 @@ Panel.prototype = {
|
||||
this._hotCornerEntered = false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Toggles the overview unless this is the first click on the Activities button within the HOT_CORNER_ACTIVATION_TIMEOUT time
|
||||
// of the hot corner being triggered. This check avoids opening and closing the overview if the user both triggered the hot corner
|
||||
// and clicked the Activities button.
|
||||
_maybeToggleOverviewOnClick: function() {
|
||||
if (this._hotCornerActivationTime == 0 || Date.now() / 1000 - this._hotCornerActivationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
|
||||
Main.overview.toggle();
|
||||
this._hotCornerActivationTime = 0;
|
||||
}
|
||||
};
|
||||
|
||||
function CalendarPopup() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
CalendarPopup.prototype = {
|
||||
_init: function() {
|
||||
let panelActor = Main.panel.actor;
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'calendarPopup' });
|
||||
|
||||
this.calendar = new Calendar.Calendar();
|
||||
this.actor.add(this.calendar.actor);
|
||||
|
||||
// Directly adding the actor to Main.chrome.actor is a hack to
|
||||
// work around the fact that there is no way to add an actor that
|
||||
// affects the input region but not the shape.
|
||||
// See: https://bugzilla.gnome.org/show_bug.cgi?id=597044
|
||||
Main.chrome.actor.add_actor(this.actor);
|
||||
Main.chrome.addInputRegionActor(this.actor);
|
||||
this.actor.y = (panelActor.y + panelActor.height - this.actor.height);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
let panelActor = Main.panel.actor;
|
||||
|
||||
// Reset the calendar to today's date
|
||||
this.calendar.setDate(new Date());
|
||||
|
||||
this.actor.x = Math.round(panelActor.x + (panelActor.width - this.actor.width) / 2);
|
||||
this.actor.lower(panelActor);
|
||||
this.actor.show();
|
||||
Tweener.addTween(this.actor,
|
||||
{ y: panelActor.y + panelActor.height,
|
||||
time: 0.2,
|
||||
transition: "easeOutQuad"
|
||||
});
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
let panelActor = Main.panel.actor;
|
||||
|
||||
Tweener.addTween(this.actor,
|
||||
{ y: panelActor.y + panelActor.height - this.actor.height,
|
||||
time: 0.2,
|
||||
transition: "easeOutQuad",
|
||||
onComplete: function() { this.actor.hide(); },
|
||||
onCompleteScope: this
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -9,11 +9,16 @@ const Shell = imports.gi.Shell;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
|
||||
const NAUTILUS_PREFS_DIR = '/apps/nautilus/preferences';
|
||||
const DESKTOP_IS_HOME_KEY = NAUTILUS_PREFS_DIR + '/desktop_is_home_dir';
|
||||
|
||||
const PLACES_VSPACING = 8;
|
||||
const PLACES_ICON_SIZE = 16;
|
||||
|
||||
@ -75,17 +80,33 @@ function Places() {
|
||||
|
||||
Places.prototype = {
|
||||
_init : function() {
|
||||
// Places is divided semi-arbitrarily into left and right; a grid would
|
||||
// look better in that there would be an even number of items left+right,
|
||||
// but it seems like we want some sort of differentiation between actions
|
||||
// like "Connect to server..." and regular folders
|
||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
spacing: 4 });
|
||||
this._menuBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING });
|
||||
this.actor.append(this._menuBox, Big.BoxPackFlags.EXPAND);
|
||||
this._devBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING });
|
||||
this._leftBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
|
||||
this.actor.append(this._leftBox, Big.BoxPackFlags.EXPAND);
|
||||
this._rightBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
|
||||
this.actor.append(this._rightBox, Big.BoxPackFlags.EXPAND);
|
||||
|
||||
// Subdivide left into actions and devices
|
||||
this._actionsBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING });
|
||||
this._leftBox.append(this._actionsBox, Big.BoxPackFlags.NONE);
|
||||
this._devBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING,
|
||||
padding_top: 6 });
|
||||
this._leftBox.append(this._devBox, Big.BoxPackFlags.NONE);
|
||||
|
||||
// Right is bookmarks
|
||||
this._dirsBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
spacing: PLACES_VSPACING });
|
||||
this.actor.append(this._dirsBox, Big.BoxPackFlags.EXPAND);
|
||||
this._rightBox.append(this._dirsBox, Big.BoxPackFlags.NONE);
|
||||
|
||||
let gconf = Shell.GConf.get_default();
|
||||
gconf.watch_directory(NAUTILUS_PREFS_DIR);
|
||||
|
||||
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
||||
let homeUri = homeFile.get_uri();
|
||||
@ -99,13 +120,27 @@ Places.prototype = {
|
||||
Gio.app_info_launch_default_for_uri(homeUri, Main.createAppLaunchContext());
|
||||
});
|
||||
|
||||
this._menuBox.append(home.actor, Big.BoxPackFlags.NONE);
|
||||
this._actionsBox.append(home.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
||||
let desktopFile = Gio.file_new_for_path (desktopPath);
|
||||
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 PlaceDisplay(desktopLabel,
|
||||
function() {
|
||||
return Shell.TextureCache.get_default().load_gicon(desktopIcon, PLACES_ICON_SIZE);
|
||||
},
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(desktopUri, Main.createAppLaunchContext());
|
||||
});
|
||||
this._actionsBox.append(this._desktopMenu.actor, Big.BoxPackFlags.NONE);
|
||||
this._updateDesktopMenuVisibility();
|
||||
gconf.connect('changed::' + DESKTOP_IS_HOME_KEY, Lang.bind(this, this._updateDesktopMenuVisibility));
|
||||
|
||||
/*
|
||||
* Show devices, code more or less ported from nautilus-places-sidebar.c
|
||||
*/
|
||||
|
||||
this._menuBox.append(this._devBox, Big.BoxPackFlags.NONE);
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
|
||||
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
|
||||
@ -137,17 +172,17 @@ Places.prototype = {
|
||||
function () {
|
||||
networkApp.launch();
|
||||
});
|
||||
this._menuBox.append(network.actor, Big.BoxPackFlags.NONE);
|
||||
this._actionsBox.append(network.actor, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
|
||||
let connect = new PlaceDisplay('Connect to...',
|
||||
let connect = new PlaceDisplay(_("Connect to..."),
|
||||
function () {
|
||||
return Shell.TextureCache.get_default().load_icon_name("applications-internet", PLACES_ICON_SIZE);
|
||||
},
|
||||
function () {
|
||||
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
|
||||
});
|
||||
this._menuBox.append(connect.actor, Big.BoxPackFlags.NONE);
|
||||
this._actionsBox.append(connect.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), ".gtk-bookmarks"]);
|
||||
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
|
||||
@ -272,7 +307,15 @@ Places.prototype = {
|
||||
Gio.app_info_launch_default_for_uri(mountUri, Main.createAppLaunchContext());
|
||||
});
|
||||
this._devBox.append(devItem.actor, Big.BoxPackFlags.NONE);
|
||||
}
|
||||
},
|
||||
|
||||
_updateDesktopMenuVisibility: function() {
|
||||
let gconf = Shell.GConf.get_default();
|
||||
let desktopIsHome = gconf.get_boolean(DESKTOP_IS_HOME_KEY);
|
||||
if (desktopIsHome)
|
||||
this._desktopMenu.actor.hide();
|
||||
else
|
||||
this._desktopMenu.actor.show();
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(Places.prototype);
|
||||
|
@ -60,24 +60,26 @@ RunDialog.prototype = {
|
||||
|
||||
// All actors are inside _group. We create it initially
|
||||
// hidden then show it in show()
|
||||
this._group = new Clutter.Group({ visible: false });
|
||||
this._group = new Clutter.Group({ visible: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height });
|
||||
global.stage.add_actor(this._group);
|
||||
|
||||
this._lightbox = new Lightbox.Lightbox(this._group);
|
||||
let lightbox = new Lightbox.Lightbox(this._group);
|
||||
|
||||
let boxH = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
x_align: Big.BoxAlignment.CENTER,
|
||||
y_align: Big.BoxAlignment.CENTER,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height });
|
||||
this._boxH = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||
x_align: Big.BoxAlignment.CENTER,
|
||||
y_align: Big.BoxAlignment.CENTER });
|
||||
|
||||
this._group.add_actor(boxH);
|
||||
this._lightbox.highlight(boxH);
|
||||
this._group.add_actor(this._boxH);
|
||||
lightbox.highlight(this._boxH);
|
||||
|
||||
let boxV = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
y_align: Big.BoxAlignment.CENTER });
|
||||
|
||||
boxH.append(boxV, Big.BoxPackFlags.NONE);
|
||||
this._boxH.append(boxV, Big.BoxPackFlags.NONE);
|
||||
|
||||
|
||||
let dialogBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||
@ -87,7 +89,7 @@ RunDialog.prototype = {
|
||||
padding: DIALOG_PADDING,
|
||||
width: DIALOG_WIDTH });
|
||||
|
||||
boxH.append(dialogBox, Big.BoxPackFlags.NONE);
|
||||
this._boxH.append(dialogBox, Big.BoxPackFlags.NONE);
|
||||
|
||||
let label = new Clutter.Text({ color: BOX_TEXT_COLOR,
|
||||
font_name: '18px Sans',
|
||||
@ -146,6 +148,7 @@ RunDialog.prototype = {
|
||||
},
|
||||
|
||||
_run : function(command) {
|
||||
this._commandError = false;
|
||||
let f;
|
||||
if (this._enableInternalCommands)
|
||||
f = this._internalCommands[command];
|
||||
@ -155,7 +158,6 @@ RunDialog.prototype = {
|
||||
f();
|
||||
} else if (command) {
|
||||
try {
|
||||
this._commandError = false;
|
||||
let [ok, len, args] = GLib.shell_parse_argv(command);
|
||||
let p = new Shell.Process({'args' : args});
|
||||
p.run();
|
||||
@ -168,7 +170,7 @@ RunDialog.prototype = {
|
||||
* We are only interested in the actual error, so parse that out.
|
||||
*/
|
||||
let m = /.+\((.+)\)/.exec(e);
|
||||
let errorStr = "Execution of '" + command + "' failed:\n" + m[1];
|
||||
let errorStr = _("Execution of '%s' failed:").format(command) + "\n" + m[1];
|
||||
this._errorMessage.set_text(errorStr);
|
||||
this._errorBox.show();
|
||||
}
|
||||
@ -182,6 +184,12 @@ RunDialog.prototype = {
|
||||
if (!Main.pushModal(this._group))
|
||||
return;
|
||||
|
||||
// Position the dialog on the current monitor
|
||||
let monitor = global.get_focus_monitor();
|
||||
|
||||
this._boxH.set_position(monitor.x, monitor.y);
|
||||
this._boxH.set_size(monitor.width, monitor.height);
|
||||
|
||||
this._isOpen = true;
|
||||
this._group.show();
|
||||
|
||||
|
@ -274,7 +274,7 @@ WindowManager.prototype = {
|
||||
_startAppSwitcher : function(shellwm, binding, window, backwards) {
|
||||
let tabPopup = new AltTab.AltTabPopup();
|
||||
|
||||
if (!tabPopup.show(backwards ? -1 : 1))
|
||||
if (!tabPopup.show(backwards))
|
||||
tabPopup.destroy();
|
||||
}
|
||||
};
|
||||
|
@ -111,6 +111,8 @@ WindowClone.prototype = {
|
||||
this.origX = realWindow.x;
|
||||
this.origY = realWindow.y;
|
||||
|
||||
this._stackAbove = null;
|
||||
|
||||
this._title = null;
|
||||
|
||||
this.actor.connect('button-release-event',
|
||||
@ -129,6 +131,8 @@ WindowClone.prototype = {
|
||||
this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
this._inDrag = false;
|
||||
|
||||
this._zooming = false;
|
||||
},
|
||||
|
||||
setVisibleWithChrome: function(visible) {
|
||||
@ -143,6 +147,14 @@ WindowClone.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
setStackAbove: function (actor) {
|
||||
this._stackAbove = actor;
|
||||
if (this._inDrag || this._zooming)
|
||||
// We'll fix up the stack after the drag/zooming
|
||||
return;
|
||||
this.actor.raise(this._stackAbove);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.actor.destroy();
|
||||
if (this._title)
|
||||
@ -157,7 +169,6 @@ WindowClone.prototype = {
|
||||
|
||||
this._havePointer = true;
|
||||
|
||||
actor.raise_top();
|
||||
this._updateTitle();
|
||||
},
|
||||
|
||||
@ -206,6 +217,8 @@ WindowClone.prototype = {
|
||||
},
|
||||
|
||||
_zoomStart : function () {
|
||||
this._zooming = true;
|
||||
|
||||
this._zoomLightbox = new Lightbox.Lightbox(global.stage);
|
||||
|
||||
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
|
||||
@ -232,7 +245,10 @@ WindowClone.prototype = {
|
||||
},
|
||||
|
||||
_zoomEnd : function () {
|
||||
this._zooming = false;
|
||||
|
||||
this.actor.reparent(this._origParent);
|
||||
this.actor.raise(this._stackAbove);
|
||||
|
||||
[this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition();
|
||||
[this.actor.scale_x, this.actor.scale_y] = this._zoomLocalOrig.getScale();
|
||||
@ -272,6 +288,12 @@ WindowClone.prototype = {
|
||||
// mysteriously present
|
||||
this._havePointer = false;
|
||||
|
||||
// We may not have a parent if DnD completed successfully, in
|
||||
// which case our clone will shortly be destroyed and replaced
|
||||
// with a new one on the target workspace.
|
||||
if (this.actor.get_parent() != null)
|
||||
this.actor.raise(this._stackAbove);
|
||||
|
||||
this.emit('drag-end');
|
||||
},
|
||||
|
||||
@ -283,7 +305,6 @@ WindowClone.prototype = {
|
||||
// Called by Tweener
|
||||
onAnimationComplete : function () {
|
||||
this._updateTitle();
|
||||
this.actor.raise(this.stackAbove);
|
||||
},
|
||||
|
||||
_createTitle : function () {
|
||||
@ -618,12 +639,43 @@ Workspace.prototype = {
|
||||
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
|
||||
},
|
||||
|
||||
_isCloneVisible: function(clone) {
|
||||
return this._showOnlyWindows == null || (clone.metaWindow in this._showOnlyWindows);
|
||||
},
|
||||
|
||||
/**
|
||||
* _getVisibleClones:
|
||||
*
|
||||
* Returns a list WindowClone objects where the clone isn't filtered
|
||||
* out by any application filter. The clone for the desktop is excluded.
|
||||
* The returned array will always be newly allocated; it is not in any
|
||||
* defined order, and thus it's convenient to call .sort() with your
|
||||
* choice of sorting function.
|
||||
*/
|
||||
_getVisibleClones: function() {
|
||||
let visible = [];
|
||||
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
|
||||
if (!this._isCloneVisible(clone))
|
||||
continue;
|
||||
|
||||
visible.push(clone);
|
||||
}
|
||||
return visible;
|
||||
},
|
||||
|
||||
_getVisibleWindows: function() {
|
||||
return this._getVisibleClones().map(function (clone) { return clone.metaWindow; });
|
||||
},
|
||||
|
||||
_resetCloneVisibility: function () {
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
let icon = this._windowIcons[i];
|
||||
|
||||
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows)) {
|
||||
if (!this._isCloneVisible(clone)) {
|
||||
clone.setVisibleWithChrome(false);
|
||||
icon.hide();
|
||||
} else {
|
||||
@ -853,21 +905,12 @@ Workspace.prototype = {
|
||||
positionWindows : function(workspaceZooming) {
|
||||
let totalVisible = 0;
|
||||
|
||||
let visibleWindows = [];
|
||||
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
|
||||
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
|
||||
continue;
|
||||
|
||||
visibleWindows.push(clone.metaWindow);
|
||||
}
|
||||
let visibleWindows = this._getVisibleWindows();
|
||||
|
||||
// Start the animations
|
||||
let slots = this._computeAllWindowSlots(visibleWindows.length);
|
||||
visibleWindows = this._orderWindowsByMotionAndStartup(visibleWindows, slots);
|
||||
|
||||
let previousWindow = this._windows[0];
|
||||
for (let i = 0; i < visibleWindows.length; i++) {
|
||||
let slot = slots[i];
|
||||
let metaWindow = visibleWindows[i];
|
||||
@ -875,9 +918,6 @@ Workspace.prototype = {
|
||||
let clone = metaWindow._delegate;
|
||||
let icon = this._windowIcons[mainIndex];
|
||||
|
||||
clone.stackAbove = previousWindow.actor;
|
||||
previousWindow = clone;
|
||||
|
||||
let [x, y, scale] = this._computeWindowRelativeLayout(metaWindow, slot);
|
||||
|
||||
icon.hide();
|
||||
@ -896,6 +936,24 @@ Workspace.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
syncStacking: function(stackIndices) {
|
||||
let desktopClone = this._windows[0];
|
||||
|
||||
let visibleClones = this._getVisibleClones();
|
||||
visibleClones.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; });
|
||||
|
||||
for (let i = 0; i < visibleClones.length; i++) {
|
||||
let clone = visibleClones[i];
|
||||
let metaWindow = clone.metaWindow;
|
||||
if (i == 0) {
|
||||
clone.setStackAbove(desktopClone.actor);
|
||||
} else {
|
||||
let previousClone = visibleClones[i - 1];
|
||||
clone.setStackAbove(previousClone.actor);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_fadeInWindowIcon: function (clone, icon) {
|
||||
icon.opacity = 0;
|
||||
icon.show();
|
||||
@ -1320,10 +1378,12 @@ Workspaces.prototype = {
|
||||
// workspaces have been created. This cannot be done first because
|
||||
// window movement depends on the Workspaces object being accessible
|
||||
// as an Overview member.
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, function() {
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].zoomToOverview();
|
||||
this._overviewShowingId =
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, function() {
|
||||
this._onRestacked();
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].zoomToOverview();
|
||||
}));
|
||||
|
||||
// Track changes to the number of workspaces
|
||||
@ -1333,6 +1393,9 @@ Workspaces.prototype = {
|
||||
this._switchWorkspaceNotifyId =
|
||||
global.window_manager.connect('switch-workspace',
|
||||
Lang.bind(this, this._activeWorkspaceChanged));
|
||||
this._restackedNotifyId =
|
||||
global.screen.connect('restacked',
|
||||
Lang.bind(this, this._onRestacked));
|
||||
},
|
||||
|
||||
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
||||
@ -1422,9 +1485,6 @@ Workspaces.prototype = {
|
||||
this._clearApplicationWindowSelection(false);
|
||||
}
|
||||
|
||||
let clone = this._lookupCloneForMetaWindow (metaWindow);
|
||||
clone.actor.raise_top();
|
||||
|
||||
Main.activateWindow(metaWindow, time);
|
||||
Main.overview.hide();
|
||||
},
|
||||
@ -1448,8 +1508,10 @@ Workspaces.prototype = {
|
||||
this.actor.destroy();
|
||||
this.actor = null;
|
||||
|
||||
Main.overview.disconnect(this._overviewShowingId);
|
||||
global.screen.disconnect(this._nWorkspacesNotifyId);
|
||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||
global.screen.disconnect(this._restackedNotifyId);
|
||||
},
|
||||
|
||||
getScale : function() {
|
||||
@ -1595,6 +1657,19 @@ Workspaces.prototype = {
|
||||
this.actor.add_actor(workspace.actor);
|
||||
},
|
||||
|
||||
_onRestacked: function() {
|
||||
let stack = global.get_windows();
|
||||
let stackIndices = {};
|
||||
|
||||
for (let i = 0; i < stack.length; i++) {
|
||||
// Use the stable sequence for an integer to use as a hash key
|
||||
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._workspaces.length; i++)
|
||||
this._workspaces[i].syncStacking(stackIndices);
|
||||
},
|
||||
|
||||
// Handles a drop onto the (+) button; assumes the new workspace
|
||||
// has already been added
|
||||
acceptNewWorkspaceDrop : function(source, dropActor, x, y, time) {
|
||||
|
41
po/ar.po
41
po/ar.po
@ -6,14 +6,14 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: HEAD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-09-25 04:22+0200\n"
|
||||
"PO-Revision-Date: 2009-09-25 04:21+0300\n"
|
||||
"POT-Creation-Date: 2009-10-03 19:19+0200\n"
|
||||
"PO-Revision-Date: 2009-10-03 19:20+0300\n"
|
||||
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
|
||||
"Language-Team: Arabic <doc@arabeyes.org>\n"
|
||||
"Language: ar\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ar\n"
|
||||
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
|
||||
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
|
||||
"X-Generator: Virtaal 0.4.0\n"
|
||||
@ -27,12 +27,12 @@ msgid "Window management and application launching"
|
||||
msgstr "إدارة النوافذ وإطلاق التطبيقات"
|
||||
|
||||
#. left side
|
||||
#: ../js/ui/panel.js:269
|
||||
#: ../js/ui/panel.js:271
|
||||
msgid "Activities"
|
||||
msgstr "الأنشطة"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:452
|
||||
#: ../js/ui/panel.js:461
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%A %Ol:%OM %p"
|
||||
|
||||
@ -41,48 +41,48 @@ msgid "Find..."
|
||||
msgstr "ابحث..."
|
||||
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "Browse"
|
||||
msgstr "استعرض"
|
||||
msgid "More"
|
||||
msgstr "المزيد"
|
||||
|
||||
#: ../js/ui/dash.js:536
|
||||
#: ../js/ui/dash.js:543
|
||||
msgid "(see all)"
|
||||
msgstr "(انظر الكل)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
|
||||
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "التطبيقات"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:773
|
||||
#: ../js/ui/dash.js:783
|
||||
msgid "PLACES"
|
||||
msgstr "الأماكن"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
|
||||
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "المستندات الحديثة"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
|
||||
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "نتائج البحث"
|
||||
|
||||
#: ../js/ui/dash.js:814
|
||||
#: ../js/ui/dash.js:830
|
||||
msgid "PREFERENCES"
|
||||
msgstr "التفضيلات"
|
||||
|
||||
#: ../js/ui/runDialog.js:95
|
||||
#: ../js/ui/runDialog.js:96
|
||||
msgid "Please enter a command:"
|
||||
msgstr "من فضلك اكتب أمرا:"
|
||||
|
||||
#: ../src/shell-global.c:799
|
||||
#: ../src/shell-global.c:812
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "منذ أقل من دقيقة"
|
||||
|
||||
#: ../src/shell-global.c:802
|
||||
#: ../src/shell-global.c:815
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
@ -93,7 +93,7 @@ msgstr[3] "منذ %d دقائق"
|
||||
msgstr[4] "منذ %d دقيقة"
|
||||
msgstr[5] "منذ %d دقيقة"
|
||||
|
||||
#: ../src/shell-global.c:805
|
||||
#: ../src/shell-global.c:818
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
@ -104,7 +104,7 @@ msgstr[3] "منذ %d ساعات"
|
||||
msgstr[4] "منذ %d ساعة"
|
||||
msgstr[5] "منذ %d ساعة"
|
||||
|
||||
#: ../src/shell-global.c:808
|
||||
#: ../src/shell-global.c:821
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
@ -115,7 +115,7 @@ msgstr[3] "منذ %d أيام"
|
||||
msgstr[4] "منذ %d يوما"
|
||||
msgstr[5] "منذ %d يوم"
|
||||
|
||||
#: ../src/shell-global.c:811
|
||||
#: ../src/shell-global.c:824
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
@ -199,3 +199,6 @@ msgstr "ابحث"
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "Browse"
|
||||
#~ msgstr "استعرض"
|
||||
|
52
po/ca.po
52
po/ca.po
@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: HEAD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-08-30 18:53+0200\n"
|
||||
"PO-Revision-Date: 2009-08-30 18:57+0100\n"
|
||||
"POT-Creation-Date: 2009-10-01 23:16+0200\n"
|
||||
"PO-Revision-Date: 2009-10-01 23:20+0100\n"
|
||||
"Last-Translator: Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -30,76 +30,81 @@ msgid "Activities"
|
||||
msgstr "Activitats"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:454
|
||||
#: ../js/ui/panel.js:461
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %H:%M"
|
||||
|
||||
#: ../js/ui/dash.js:256
|
||||
#: ../js/ui/dash.js:283
|
||||
msgid "Find..."
|
||||
msgstr "Cerca..."
|
||||
|
||||
#: ../js/ui/dash.js:374
|
||||
msgid "Browse"
|
||||
msgstr "Navega"
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "More"
|
||||
msgstr "Més"
|
||||
|
||||
#: ../js/ui/dash.js:451
|
||||
#: ../js/ui/dash.js:543
|
||||
msgid "(see all)"
|
||||
msgstr "(mostra tot)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:633
|
||||
#: ../js/ui/dash.js:681
|
||||
#: ../js/ui/dash.js:763
|
||||
#: ../js/ui/dash.js:825
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "APLICACIONS"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:653
|
||||
#: ../js/ui/dash.js:783
|
||||
msgid "PLACES"
|
||||
msgstr "LLOCS"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:660
|
||||
#: ../js/ui/dash.js:692
|
||||
#: ../js/ui/dash.js:790
|
||||
#: ../js/ui/dash.js:835
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "DOCUMENTS RECENTS"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:679
|
||||
#: ../js/ui/dash.js:815
|
||||
#: ../js/ui/dash.js:958
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "RESULTATS DE LA CERCA"
|
||||
|
||||
#: ../js/ui/runDialog.js:82
|
||||
#: ../js/ui/dash.js:830
|
||||
msgid "PREFERENCES"
|
||||
msgstr "PREFERÈNCIES"
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Introduïu una ordre:"
|
||||
|
||||
#: ../src/shell-global.c:840
|
||||
#: ../src/shell-global.c:812
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Fa menys d'un minut"
|
||||
|
||||
#: ../src/shell-global.c:843
|
||||
#: ../src/shell-global.c:815
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "Fa %d minut"
|
||||
msgstr[1] "Fa %d minuts"
|
||||
|
||||
#: ../src/shell-global.c:846
|
||||
#: ../src/shell-global.c:818
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "Fa %d hora"
|
||||
msgstr[1] "Fa %d hores"
|
||||
|
||||
#: ../src/shell-global.c:849
|
||||
#: ../src/shell-global.c:821
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "Fa %d dia"
|
||||
msgstr[1] "Fa %d dies"
|
||||
|
||||
#: ../src/shell-global.c:852
|
||||
#: ../src/shell-global.c:824
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
@ -180,10 +185,3 @@ msgstr "Cerca"
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "Find apps or documents"
|
||||
#~ msgstr "Cerca aplicacions o documents"
|
||||
#~ msgid "Manager"
|
||||
#~ msgstr "Gestor"
|
||||
#~ msgid "The user manager object this user is controlled by."
|
||||
#~ msgstr "L'objecte gestor d'usuaris que controla a aquest usuari."
|
||||
|
||||
|
89
po/de.po
89
po/de.po
@ -4,18 +4,20 @@
|
||||
#
|
||||
# Hendrik Brandt <heb@gnome-de.org>, 2009.
|
||||
# Hendrik Richter <hendrikr@gnome.org>, 2009.
|
||||
# Christian Kirbach <Christian.Kirbach@googlemail.com>, 2009.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: HEAD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-08-20 16:47+0200\n"
|
||||
"PO-Revision-Date: 2009-08-20 16:49+0200\n"
|
||||
"Last-Translator: Hendrik Richter <hendrikr@gnome.org>\n"
|
||||
"Language-Team: Deutsch <gnome-de@gnome.org>\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&component=general\n"
|
||||
"POT-Creation-Date: 2009-10-01 22:22+0000\n"
|
||||
"PO-Revision-Date: 2009-10-01 14:32+0200\n"
|
||||
"Last-Translator: Christian Kirbach <Christian.Kirbach@googlemail.com>\n"
|
||||
"Language-Team: German <gnome-de@gnome.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||
@ -27,78 +29,83 @@ msgid "Window management and application launching"
|
||||
msgstr "Fenster verwalten und Anwendungen starten"
|
||||
|
||||
#. left side
|
||||
#: ../js/ui/panel.js:266
|
||||
#: ../js/ui/panel.js:271
|
||||
msgid "Activities"
|
||||
msgstr "Aktivitäten"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:433
|
||||
#: ../js/ui/panel.js:461
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %H:%M"
|
||||
|
||||
#: ../js/ui/dash.js:251
|
||||
msgid "Find apps or documents"
|
||||
msgstr "Anwendungen oder Dokumente suchen"
|
||||
#: ../js/ui/dash.js:283
|
||||
msgid "Find..."
|
||||
msgstr "Suchen …"
|
||||
|
||||
#: ../js/ui/dash.js:369
|
||||
msgid "Browse"
|
||||
msgstr "Durchsuchen"
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "More"
|
||||
msgstr "Mehr"
|
||||
|
||||
#: ../js/ui/dash.js:543
|
||||
msgid "(see all)"
|
||||
msgstr "(alle sehen)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:505 ../js/ui/dash.js:578
|
||||
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "ANWENDUNGEN"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:510 ../js/ui/dash.js:605
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "ZULETZT GEÖFFNETE DOKUMENTE"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:598
|
||||
#: ../js/ui/dash.js:783
|
||||
msgid "PLACES"
|
||||
msgstr "ORTE"
|
||||
|
||||
#: ../js/ui/runDialog.js:75
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "ZULETZT GEÖFFNETE DOKUMENTE"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "SUCHERGEBNISSE"
|
||||
|
||||
#: ../js/ui/dash.js:830
|
||||
msgid "PREFERENCES"
|
||||
msgstr "EINSTELLUNGEN"
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Bitte geben Sie einen Befehl ein:"
|
||||
|
||||
#: ../src/gdmuser/gdm-user.c:243
|
||||
msgid "Manager"
|
||||
msgstr "Verwaltung"
|
||||
|
||||
#: ../src/gdmuser/gdm-user.c:244
|
||||
msgid "The user manager object this user is controlled by."
|
||||
msgstr "Das Benutzerverwaltungsobjekt welches diesen Benutzer überwacht."
|
||||
|
||||
#: ../src/shell-global.c:841
|
||||
#: ../src/shell-global.c:812
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Vor weniger als einer Minute"
|
||||
|
||||
#: ../src/shell-global.c:844
|
||||
#: ../src/shell-global.c:815
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "Vor %d Minute"
|
||||
msgstr[1] "Vor %d Minuten"
|
||||
|
||||
#: ../src/shell-global.c:847
|
||||
#: ../src/shell-global.c:818
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "Vor %d Stunde"
|
||||
msgstr[1] "Vor %d Stunden"
|
||||
|
||||
#: ../src/shell-global.c:850
|
||||
#: ../src/shell-global.c:821
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "Vor %d Tag"
|
||||
msgstr[1] "Vor %d Tagen"
|
||||
|
||||
#: ../src/shell-global.c:853
|
||||
#: ../src/shell-global.c:824
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
@ -180,3 +187,15 @@ msgstr "Suchen"
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "Find apps or documents"
|
||||
#~ msgstr "Anwendungen oder Dokumente suchen"
|
||||
|
||||
#~ msgid "Browse"
|
||||
#~ msgstr "Durchsuchen"
|
||||
|
||||
#~ msgid "Manager"
|
||||
#~ msgstr "Verwaltung"
|
||||
|
||||
#~ msgid "The user manager object this user is controlled by."
|
||||
#~ msgstr "Das Benutzerverwaltungsobjekt welches diesen Benutzer überwacht."
|
||||
|
59
po/nb.po
59
po/nb.po
@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnome-shell 0.4\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-08-21 12:36+0200\n"
|
||||
"PO-Revision-Date: 2009-08-21 12:44+0200\n"
|
||||
"POT-Creation-Date: 2009-10-03 10:47+0200\n"
|
||||
"PO-Revision-Date: 2009-10-03 10:48+0200\n"
|
||||
"Last-Translator: Kjartan Maraas <kmaraas@broadpark.no>\n"
|
||||
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -25,70 +25,83 @@ msgid "Window management and application launching"
|
||||
msgstr "Vindushåndtering og oppstart av programmer"
|
||||
|
||||
#. left side
|
||||
#: ../js/ui/panel.js:266
|
||||
#: ../js/ui/panel.js:271
|
||||
msgid "Activities"
|
||||
msgstr "Aktiviteter"
|
||||
|
||||
#. Translators: This is a time format.
|
||||
#: ../js/ui/panel.js:433
|
||||
#: ../js/ui/panel.js:461
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %l:%M"
|
||||
|
||||
#: ../js/ui/dash.js:250
|
||||
msgid "Find apps or documents"
|
||||
msgstr "Finn programmer eller dokumenter"
|
||||
#: ../js/ui/dash.js:283
|
||||
msgid "Find..."
|
||||
msgstr "Finn..."
|
||||
|
||||
#: ../js/ui/dash.js:368
|
||||
msgid "Browse"
|
||||
msgstr "Bla gjennom"
|
||||
#: ../js/ui/dash.js:400
|
||||
msgid "More"
|
||||
msgstr "Mer"
|
||||
|
||||
#: ../js/ui/dash.js:543
|
||||
msgid "(see all)"
|
||||
msgstr "(se alle)"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/dash.js:504 ../js/ui/dash.js:577
|
||||
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "PROGRAMMER"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:509 ../js/ui/dash.js:604
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "SISTE DOKUMENTER"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:597
|
||||
#: ../js/ui/dash.js:783
|
||||
msgid "PLACES"
|
||||
msgstr "STEDER"
|
||||
|
||||
#: ../js/ui/runDialog.js:75
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
|
||||
msgid "RECENT DOCUMENTS"
|
||||
msgstr "SISTE DOKUMENTER"
|
||||
|
||||
#. **** Search Results ****
|
||||
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
|
||||
msgid "SEARCH RESULTS"
|
||||
msgstr "SØKERESULTATER"
|
||||
|
||||
#: ../js/ui/dash.js:830
|
||||
msgid "PREFERENCES"
|
||||
msgstr "BRUKERVALG"
|
||||
|
||||
#: ../js/ui/runDialog.js:96
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Oppgi en kommando:"
|
||||
|
||||
#: ../src/shell-global.c:841
|
||||
#: ../src/shell-global.c:812
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Mindre enn ett minutt siden"
|
||||
|
||||
#: ../src/shell-global.c:844
|
||||
#: ../src/shell-global.c:815
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "%d minutt siden"
|
||||
msgstr[1] "%d minutter siden"
|
||||
|
||||
#: ../src/shell-global.c:847
|
||||
#: ../src/shell-global.c:818
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "%d time siden"
|
||||
msgstr[1] "%d timer siden"
|
||||
|
||||
#: ../src/shell-global.c:850
|
||||
#: ../src/shell-global.c:821
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "%d dag siden"
|
||||
msgstr[1] "%d dager siden"
|
||||
|
||||
#: ../src/shell-global.c:853
|
||||
#: ../src/shell-global.c:824
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
|
@ -18,6 +18,7 @@ st_built_sources = \
|
||||
BUILT_SOURCES += $(st_built_sources)
|
||||
|
||||
EXTRA_DIST += \
|
||||
st/test-theme.css \
|
||||
st/st-marshal.list \
|
||||
st/st-enum-types.h.in \
|
||||
st/st-enum-types.c.in
|
||||
@ -73,18 +74,22 @@ st_source_h = \
|
||||
st/st-button.h \
|
||||
st/st-clipboard.h \
|
||||
st/st-entry.h \
|
||||
st/st-im-text.h \
|
||||
st/st-label.h \
|
||||
st/st-private.h \
|
||||
st/st-scrollable.h \
|
||||
st/st-scroll-bar.h \
|
||||
st/st-scroll-view.h \
|
||||
st/st-subtexture.h \
|
||||
st/st-table.h \
|
||||
st/st-table-child.h \
|
||||
st/st-table-private.h \
|
||||
st/st-texture-cache.h \
|
||||
st/st-texture-frame.h \
|
||||
st/st-theme.h \
|
||||
st/st-theme-context.h \
|
||||
st/st-theme-node.h \
|
||||
st/st-theme-private.h \
|
||||
st/st-tooltip.h \
|
||||
st/st-types.h \
|
||||
st/st-widget.h \
|
||||
@ -105,6 +110,7 @@ st_source_c = \
|
||||
st/st-button.c \
|
||||
st/st-clipboard.c \
|
||||
st/st-entry.c \
|
||||
st/st-im-text.c \
|
||||
st/st-label.c \
|
||||
st/st-private.c \
|
||||
st/st-scrollable.c \
|
||||
|
@ -166,7 +166,7 @@ Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir St-1.0.gir libgnome-shell.l
|
||||
--nsversion=0.1 \
|
||||
--add-include-path=$(MUTTER_LIB_DIR)/mutter/ \
|
||||
--include=Clutter-1.0 \
|
||||
--include=Meta-2.27 \
|
||||
--include=Meta-2.28 \
|
||||
--libtool="$(LIBTOOL)" \
|
||||
--add-include-path=$(builddir) \
|
||||
--include=Big-1.0 \
|
||||
@ -221,7 +221,7 @@ St-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libst-1.0.la Makefile
|
||||
$(addprefix $(srcdir)/,$(st_source_h)) \
|
||||
$(addprefix $(srcdir)/,$(st_source_c)) \
|
||||
$(srcdir)/st-enum-types.h \
|
||||
$(ST_CFLAGS) \
|
||||
$(st_cflags) \
|
||||
-o $@
|
||||
CLEANFILES += St-1.0.gir
|
||||
|
||||
|
@ -45,6 +45,7 @@ struct _ShellAppSystemPrivate {
|
||||
|
||||
GHashTable *app_id_to_app;
|
||||
|
||||
GHashTable *cached_menu_contents; /* <char *id, GSList<ShellAppInfo*>> */
|
||||
GSList *cached_app_menus; /* ShellAppMenuEntry */
|
||||
|
||||
GSList *cached_settings; /* ShellAppInfo */
|
||||
@ -56,6 +57,7 @@ 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);
|
||||
@ -229,6 +231,9 @@ shell_app_system_init (ShellAppSystem *self)
|
||||
priv->app_id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
NULL, (GDestroyNotify) shell_app_info_unref);
|
||||
|
||||
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.
|
||||
@ -262,6 +267,8 @@ 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_app);
|
||||
|
||||
g_slist_foreach (priv->cached_app_menus, (GFunc)shell_app_menu_entry_free, NULL);
|
||||
@ -279,6 +286,14 @@ 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)
|
||||
{
|
||||
@ -419,8 +434,12 @@ static gboolean
|
||||
on_tree_changed (gpointer user_data)
|
||||
{
|
||||
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
|
||||
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
|
||||
|
||||
reread_menus (self);
|
||||
g_hash_table_remove_all (self->priv->cached_menu_contents);
|
||||
|
||||
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
|
||||
|
||||
self->priv->app_change_timeout_id = 0;
|
||||
return FALSE;
|
||||
}
|
||||
@ -537,26 +556,32 @@ shell_app_menu_entry_get_type (void)
|
||||
* shell_app_system_get_applications_for_menu:
|
||||
*
|
||||
* Traverses a toplevel menu, and returns all items under it. Nested items
|
||||
* are flattened.
|
||||
* are flattened. This value is computed on initial call and cached thereafter
|
||||
* until the set of installed applications changes.
|
||||
*
|
||||
* Return value: (transfer full) (element-type ShellAppInfo): List of applications
|
||||
* Return value: (transfer none) (element-type ShellAppInfo): List of applications
|
||||
*/
|
||||
GSList *
|
||||
shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
|
||||
shell_app_system_get_applications_for_menu (ShellAppSystem *self,
|
||||
const char *menu)
|
||||
{
|
||||
char *path;
|
||||
GMenuTreeDirectory *menu_entry;
|
||||
GSList *apps;
|
||||
|
||||
path = g_strdup_printf ("/%s", menu);
|
||||
menu_entry = gmenu_tree_get_directory_from_path (monitor->priv->apps_tree, path);
|
||||
g_free (path);
|
||||
g_assert (menu_entry != NULL);
|
||||
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 (monitor, NULL, menu_entry);
|
||||
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);
|
||||
gmenu_tree_item_unref (menu_entry);
|
||||
}
|
||||
|
||||
return apps;
|
||||
}
|
||||
|
@ -147,17 +147,14 @@ shell_draw_clock (ClutterCairoTexture *texture,
|
||||
}
|
||||
|
||||
void
|
||||
shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||
ClutterGravity pointing_towards,
|
||||
ClutterColor *border_color,
|
||||
ClutterColor *background_color)
|
||||
shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||
ShellPointerDirection direction,
|
||||
ClutterColor *border_color,
|
||||
ClutterColor *background_color)
|
||||
{
|
||||
guint width, height;
|
||||
cairo_t *cr;
|
||||
|
||||
g_return_if_fail (pointing_towards == CLUTTER_GRAVITY_NORTH ||
|
||||
pointing_towards == CLUTTER_GRAVITY_WEST);
|
||||
|
||||
clutter_cairo_texture_get_surface_size (texture, &width, &height);
|
||||
|
||||
clutter_cairo_texture_clear (texture);
|
||||
@ -167,17 +164,34 @@ shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||
|
||||
clutter_cairo_set_source_color (cr, border_color);
|
||||
|
||||
if (pointing_towards == CLUTTER_GRAVITY_WEST)
|
||||
{
|
||||
cairo_move_to (cr, width, 0);
|
||||
cairo_line_to (cr, 0, floor (height * 0.5));
|
||||
cairo_line_to (cr, width, height);
|
||||
}
|
||||
else /* CLUTTER_GRAVITY_NORTH */
|
||||
switch (direction)
|
||||
{
|
||||
case SHELL_POINTER_UP:
|
||||
cairo_move_to (cr, 0, height);
|
||||
cairo_line_to (cr, floor (width * 0.5), 0);
|
||||
cairo_line_to (cr, width, height);
|
||||
break;
|
||||
|
||||
case SHELL_POINTER_DOWN:
|
||||
cairo_move_to (cr, width, 0);
|
||||
cairo_line_to (cr, floor (width * 0.5), height);
|
||||
cairo_line_to (cr, 0, 0);
|
||||
break;
|
||||
|
||||
case SHELL_POINTER_LEFT:
|
||||
cairo_move_to (cr, width, height);
|
||||
cairo_line_to (cr, 0, floor (height * 0.5));
|
||||
cairo_line_to (cr, width, 0);
|
||||
break;
|
||||
|
||||
case SHELL_POINTER_RIGHT:
|
||||
cairo_move_to (cr, 0, 0);
|
||||
cairo_line_to (cr, width, floor (height * 0.5));
|
||||
cairo_line_to (cr, 0, height);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
cairo_stroke_preserve (cr);
|
||||
|
@ -13,10 +13,17 @@ ClutterCairoTexture *shell_create_vertical_gradient (ClutterColor *top,
|
||||
ClutterCairoTexture *shell_create_horizontal_gradient (ClutterColor *left,
|
||||
ClutterColor *right);
|
||||
|
||||
void shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||
ClutterGravity pointing_towards,
|
||||
ClutterColor *border_color,
|
||||
ClutterColor *background_color);
|
||||
typedef enum {
|
||||
SHELL_POINTER_UP,
|
||||
SHELL_POINTER_DOWN,
|
||||
SHELL_POINTER_LEFT,
|
||||
SHELL_POINTER_RIGHT
|
||||
} ShellPointerDirection;
|
||||
|
||||
void shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||
ShellPointerDirection direction,
|
||||
ClutterColor *border_color,
|
||||
ClutterColor *background_color);
|
||||
|
||||
void shell_draw_clock (ClutterCairoTexture *texture,
|
||||
int hour,
|
||||
|
@ -882,3 +882,138 @@ shell_global_create_root_pixmap_actor (ShellGlobal *global)
|
||||
|
||||
return clutter_clone_new (global->root_pixmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_monitors:
|
||||
* @global: the #ShellGlobal
|
||||
*
|
||||
* Gets a list of the bounding boxes of the active screen's monitors.
|
||||
*
|
||||
* Return value: (transfer full) (element-type GdkRectangle): a list
|
||||
* of monitor bounding boxes.
|
||||
*/
|
||||
GSList *
|
||||
shell_global_get_monitors (ShellGlobal *global)
|
||||
{
|
||||
MetaScreen *screen = shell_global_get_screen (global);
|
||||
GSList *monitors = NULL;
|
||||
MetaRectangle rect;
|
||||
int i;
|
||||
|
||||
g_assert (sizeof (MetaRectangle) == sizeof (GdkRectangle) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, x) == G_STRUCT_OFFSET (GdkRectangle, x) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, y) == G_STRUCT_OFFSET (GdkRectangle, y) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, width) == G_STRUCT_OFFSET (GdkRectangle, width) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, height) == G_STRUCT_OFFSET (GdkRectangle, height));
|
||||
|
||||
for (i = meta_screen_get_n_monitors (screen) - 1; i >= 0; i--)
|
||||
{
|
||||
meta_screen_get_monitor_geometry (screen, i, &rect);
|
||||
monitors = g_slist_prepend (monitors,
|
||||
g_boxed_copy (GDK_TYPE_RECTANGLE, &rect));
|
||||
}
|
||||
return monitors;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_primary_monitor:
|
||||
* @global: the #ShellGlobal
|
||||
*
|
||||
* Gets the bounding box of the primary monitor (the one that the
|
||||
* panel is on).
|
||||
*
|
||||
* Return value: the bounding box of the primary monitor
|
||||
*/
|
||||
GdkRectangle *
|
||||
shell_global_get_primary_monitor (ShellGlobal *global)
|
||||
{
|
||||
MetaScreen *screen = shell_global_get_screen (global);
|
||||
MetaRectangle rect;
|
||||
|
||||
g_assert (sizeof (MetaRectangle) == sizeof (GdkRectangle) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, x) == G_STRUCT_OFFSET (GdkRectangle, x) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, y) == G_STRUCT_OFFSET (GdkRectangle, y) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, width) == G_STRUCT_OFFSET (GdkRectangle, width) &&
|
||||
G_STRUCT_OFFSET (MetaRectangle, height) == G_STRUCT_OFFSET (GdkRectangle, height));
|
||||
|
||||
meta_screen_get_monitor_geometry (screen, 0, &rect);
|
||||
return g_boxed_copy (GDK_TYPE_RECTANGLE, &rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_focus_monitor:
|
||||
* @global: the #ShellGlobal
|
||||
*
|
||||
* Gets the bounding box of the monitor containing the window that
|
||||
* currently contains the keyboard focus.
|
||||
*
|
||||
* Return value: the bounding box of the focus monitor
|
||||
*/
|
||||
GdkRectangle *
|
||||
shell_global_get_focus_monitor (ShellGlobal *global)
|
||||
{
|
||||
MetaScreen *screen = shell_global_get_screen (global);
|
||||
MetaDisplay *display = meta_screen_get_display (screen);
|
||||
MetaWindow *focus = meta_display_get_focus_window (display);
|
||||
MetaRectangle rect, wrect;
|
||||
int nmonitors, i;
|
||||
|
||||
if (focus)
|
||||
{
|
||||
meta_window_get_outer_rect (focus, &wrect);
|
||||
nmonitors = meta_screen_get_n_monitors (screen);
|
||||
|
||||
/* Find the monitor that the top-left corner of @focus is on. */
|
||||
for (i = 0; i < nmonitors; i++)
|
||||
{
|
||||
meta_screen_get_monitor_geometry (screen, i, &rect);
|
||||
|
||||
if (rect.x < wrect.x && rect.y < wrect.y &&
|
||||
rect.x + rect.width > wrect.x &&
|
||||
rect.y + rect.height > wrect.y)
|
||||
return g_boxed_copy (GDK_TYPE_RECTANGLE, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
meta_screen_get_monitor_geometry (screen, 0, &rect);
|
||||
return g_boxed_copy (GDK_TYPE_RECTANGLE, &rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_global_get_modifier_keys:
|
||||
* @global: the #ShellGlobal
|
||||
*
|
||||
* Gets the current set of modifier keys that are pressed down;
|
||||
* this is a wrapper around gdk_display_get_pointer() that strips
|
||||
* out any un-declared modifier flags, to make gjs happy; see
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=597292.
|
||||
*
|
||||
* Return value: the current modifiers
|
||||
*/
|
||||
GdkModifierType
|
||||
shell_global_get_modifier_keys (ShellGlobal *global)
|
||||
{
|
||||
GdkModifierType mods;
|
||||
|
||||
gdk_display_get_pointer (gdk_display_get_default (), NULL, NULL, NULL, &mods);
|
||||
return mods & GDK_MODIFIER_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_get_event_state:
|
||||
* @event: a #ClutterEvent
|
||||
*
|
||||
* Gets the current state of the event (the set of modifier keys that
|
||||
* are pressed down). Thhis is a wrapper around
|
||||
* clutter_event_get_state() that strips out any un-declared modifier
|
||||
* flags, to make gjs happy; see
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=597292.
|
||||
*
|
||||
* Return value: the state from the event
|
||||
*/
|
||||
ClutterModifierType
|
||||
shell_get_event_state (ClutterEvent *event)
|
||||
{
|
||||
ClutterModifierType state = clutter_event_get_state (event);
|
||||
return state & CLUTTER_MODIFIER_MASK;
|
||||
}
|
||||
|
@ -73,6 +73,14 @@ void shell_global_format_time_relative_pretty (ShellGlobal *global, guint delta,
|
||||
|
||||
ClutterActor *shell_global_create_root_pixmap_actor (ShellGlobal *global);
|
||||
|
||||
GSList *shell_global_get_monitors (ShellGlobal *global);
|
||||
GdkRectangle *shell_global_get_primary_monitor (ShellGlobal *global);
|
||||
GdkRectangle *shell_global_get_focus_monitor (ShellGlobal *global);
|
||||
|
||||
GdkModifierType shell_global_get_modifier_keys (ShellGlobal *global);
|
||||
|
||||
ClutterModifierType shell_get_event_state (ClutterEvent *event);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_GLOBAL_H__ */
|
||||
|
@ -15,6 +15,7 @@ G_DEFINE_TYPE(ShellMenu, shell_menu, BIG_TYPE_BOX);
|
||||
struct _ShellMenuPrivate {
|
||||
gboolean popped_up;
|
||||
gboolean have_grab;
|
||||
guint activating_button;
|
||||
|
||||
gboolean released_on_source;
|
||||
ClutterActor *source_actor;
|
||||
@ -117,9 +118,15 @@ shell_menu_button_release_event (ClutterActor *actor,
|
||||
{
|
||||
ShellMenu *box = SHELL_MENU (actor);
|
||||
|
||||
if (event->button != 1)
|
||||
/* Until the user releases the button that brought up the menu, we just
|
||||
* ignore other button press/releass.
|
||||
* See https://bugzilla.gnome.org/show_bug.cgi?id=596371
|
||||
*/
|
||||
if (box->priv->activating_button > 0 && box->priv->activating_button != event->button)
|
||||
return FALSE;
|
||||
|
||||
box->priv->activating_button = 0;
|
||||
|
||||
if (box->priv->source_actor && !box->priv->released_on_source)
|
||||
{
|
||||
if (box->priv->source_actor == event->source ||
|
||||
@ -134,13 +141,8 @@ shell_menu_button_release_event (ClutterActor *actor,
|
||||
|
||||
shell_menu_popdown_nosignal (box);
|
||||
|
||||
if (!container_contains (CLUTTER_CONTAINER (box), event->source))
|
||||
{
|
||||
g_signal_emit (G_OBJECT (box), shell_menu_signals[CANCELLED], 0);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (box->priv->selected == NULL)
|
||||
if (!container_contains (CLUTTER_CONTAINER (box), event->source) ||
|
||||
box->priv->selected == NULL)
|
||||
{
|
||||
g_signal_emit (G_OBJECT (box), shell_menu_signals[CANCELLED], 0);
|
||||
return FALSE;
|
||||
@ -158,6 +160,7 @@ shell_menu_popup (ShellMenu *box,
|
||||
{
|
||||
if (box->priv->popped_up)
|
||||
return;
|
||||
box->priv->activating_button = button;
|
||||
box->priv->popped_up = TRUE;
|
||||
box->priv->have_grab = TRUE;
|
||||
box->priv->released_on_source = FALSE;
|
||||
|
@ -603,7 +603,7 @@ compute_shrinks (StBoxLayout *self,
|
||||
* to expand from their minimum size up to the natural size. Or to put
|
||||
* it a different way, we want to start by shrinking only the child that
|
||||
* can shrink most, then shrink that and the next most shrinkable child,
|
||||
* to the point where we are shrinling everything.
|
||||
* to the point where we are shrinking everything.
|
||||
*/
|
||||
|
||||
/* Find the amount of possible shrink for each child */
|
||||
|
@ -35,7 +35,6 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -165,7 +164,7 @@ st_button_style_changed (StWidget *widget)
|
||||
|
||||
spacing = 6;
|
||||
st_theme_node_get_length (theme_node, "border-spacing", FALSE, &spacing);
|
||||
priv->spacing = round (spacing);
|
||||
priv->spacing = (int)(0.5 + spacing);
|
||||
|
||||
/* update the label styling */
|
||||
st_button_update_label_style (button);
|
||||
|
@ -53,10 +53,10 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <clutter-imcontext/clutter-imtext.h>
|
||||
|
||||
#include "st-entry.h"
|
||||
|
||||
#include "st-im-text.h"
|
||||
#include "st-widget.h"
|
||||
#include "st-texture-cache.h"
|
||||
#include "st-marshal.h"
|
||||
@ -645,7 +645,7 @@ st_entry_init (StEntry *entry)
|
||||
|
||||
priv = entry->priv = ST_ENTRY_GET_PRIVATE (entry);
|
||||
|
||||
priv->entry = g_object_new (CLUTTER_TYPE_IMTEXT,
|
||||
priv->entry = g_object_new (ST_TYPE_IM_TEXT,
|
||||
"line-alignment", PANGO_ALIGN_LEFT,
|
||||
"editable", TRUE,
|
||||
"reactive", TRUE,
|
||||
|
477
src/st/st-im-text.c
Normal file
477
src/st/st-im-text.c
Normal file
@ -0,0 +1,477 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-im-text.c
|
||||
*
|
||||
* This started as a copy of ClutterIMText converted to use
|
||||
* GtkIMContext rather than ClutterIMContext. Original code:
|
||||
*
|
||||
* Author: raymond liu <raymond.liu@intel.com>
|
||||
*
|
||||
* Copyright (C) 2009, Intel Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:StIMText
|
||||
* @short_description: Text widget with input method support
|
||||
* @stability: Unstable
|
||||
* @see_also: #ClutterText
|
||||
* @include: st-imtext/st-imtext.h
|
||||
*
|
||||
* #StIMText derives from ClutterText and hooks up better text input
|
||||
* via #GtkIMContext. It is meant to be a drop-in replacement for
|
||||
* ClutterIMText but using GtkIMContext rather than ClutterIMContext.
|
||||
*/
|
||||
|
||||
/* Places where this actor doesn't support all of GtkIMContext:
|
||||
*
|
||||
* A) It doesn't support preedit. This makes it fairly useless for
|
||||
* most complicated input methods. Fixing this requires support
|
||||
* directly in ClutterText, since there is no way to wedge a
|
||||
* preedit string in externally.
|
||||
* B) It doesn't support surrounding context via the
|
||||
* :retrieve-surrounding and :delete-surrounding signals. This could
|
||||
* be added here, but only affects a small number of input methods
|
||||
* and really doesn't make a lot of sense without A)
|
||||
*
|
||||
* Another problem that will show up with usage in GNOME Shell's overview
|
||||
* is that the user may have trouble seeing and interacting with ancilliary
|
||||
* windows shown by the IM.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <clutter/x11/clutter-x11.h>
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <X11/extensions/XKB.h>
|
||||
|
||||
#include "st-im-text.h"
|
||||
|
||||
#define ST_IM_TEXT_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_IM_TEXT, StIMTextPrivate))
|
||||
|
||||
struct _StIMTextPrivate
|
||||
{
|
||||
GtkIMContext *im_context;
|
||||
GdkWindow *window;
|
||||
|
||||
guint need_im_reset : 1;
|
||||
};
|
||||
|
||||
static void st_im_text_commit_cb (GtkIMContext *context,
|
||||
const gchar *str,
|
||||
StIMText *imtext);
|
||||
|
||||
G_DEFINE_TYPE (StIMText, st_im_text, CLUTTER_TYPE_TEXT)
|
||||
|
||||
static void
|
||||
st_im_text_dispose (GObject *object)
|
||||
{
|
||||
StIMTextPrivate *priv = ST_IM_TEXT (object)->priv;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (priv->im_context,
|
||||
(void *) st_im_text_commit_cb,
|
||||
object);
|
||||
|
||||
g_object_unref (priv->im_context);
|
||||
priv->im_context = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
update_im_cursor_location (StIMText *self)
|
||||
{
|
||||
StIMTextPrivate *priv = self->priv;
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (self);
|
||||
ClutterActor *parent;
|
||||
gint position;
|
||||
gfloat cursor_x, cursor_y, cursor_height;
|
||||
gfloat actor_x, actor_y;
|
||||
GdkRectangle area;
|
||||
|
||||
position = clutter_text_get_cursor_position (clutter_text);
|
||||
clutter_text_position_to_coords (clutter_text, position,
|
||||
&cursor_x, &cursor_y, &cursor_height);
|
||||
|
||||
/* This is a workaround for a bug in Clutter where
|
||||
* clutter_actor_get_transformed_position doesn't work during
|
||||
* clutter_actor_paint() because the actor has already set up
|
||||
* a model-view matrix.
|
||||
*
|
||||
* http://bugzilla.openedhand.com/show_bug.cgi?id=1115
|
||||
*/
|
||||
actor_x = actor_y = 0.;
|
||||
parent = CLUTTER_ACTOR (self);
|
||||
while (parent)
|
||||
{
|
||||
gfloat x, y;
|
||||
|
||||
clutter_actor_get_position (parent, &x, &y);
|
||||
actor_x += x;
|
||||
actor_y += y;
|
||||
|
||||
parent = clutter_actor_get_parent (parent);
|
||||
}
|
||||
|
||||
area.x = (int)(0.5 + cursor_x + actor_x);
|
||||
area.y = (int)(0.5 + cursor_y + actor_y);
|
||||
area.width = 0;
|
||||
area.height = (int)(0.5 + cursor_height);
|
||||
|
||||
gtk_im_context_set_cursor_location (priv->im_context, &area);
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_commit_cb (GtkIMContext *context,
|
||||
const gchar *str,
|
||||
StIMText *imtext)
|
||||
{
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (imtext);
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
{
|
||||
clutter_text_delete_selection (clutter_text);
|
||||
clutter_text_insert_text (clutter_text, str,
|
||||
clutter_text_get_cursor_position (clutter_text));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reset_im_context (StIMText *self)
|
||||
{
|
||||
StIMTextPrivate *priv = self->priv;
|
||||
|
||||
if (priv->need_im_reset)
|
||||
{
|
||||
gtk_im_context_reset (priv->im_context);
|
||||
priv->need_im_reset = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_paint (ClutterActor *actor)
|
||||
{
|
||||
StIMText *self = ST_IM_TEXT (actor);
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (actor);
|
||||
|
||||
/* This updates the cursor position as a side-effect */
|
||||
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->paint)
|
||||
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->paint (actor);
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
update_im_cursor_location (self);
|
||||
}
|
||||
|
||||
/* Returns a new reference to window */
|
||||
static GdkWindow *
|
||||
window_for_actor (ClutterActor *actor)
|
||||
{
|
||||
GdkDisplay *display = gdk_display_get_default ();
|
||||
ClutterActor *stage;
|
||||
Window xwindow;
|
||||
GdkWindow *window;
|
||||
|
||||
stage = clutter_actor_get_stage (actor);
|
||||
xwindow = clutter_x11_get_stage_window ((ClutterStage *)stage);
|
||||
|
||||
window = gdk_window_lookup_for_display (display, xwindow);
|
||||
if (window)
|
||||
g_object_ref (window);
|
||||
else
|
||||
window = gdk_window_foreign_new_for_display (display, xwindow);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_realize (ClutterActor *actor)
|
||||
{
|
||||
StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv;
|
||||
|
||||
priv->window = window_for_actor (actor);
|
||||
gtk_im_context_set_client_window (priv->im_context, priv->window);
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_unrealize (ClutterActor *actor)
|
||||
{
|
||||
StIMText *self = ST_IM_TEXT (actor);
|
||||
StIMTextPrivate *priv = self->priv;
|
||||
|
||||
reset_im_context (self);
|
||||
gtk_im_context_set_client_window (priv->im_context, NULL);
|
||||
g_object_unref (priv->window);
|
||||
priv->window = NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
key_is_modifier (guint16 keyval)
|
||||
{
|
||||
/* See gdkkeys-x11.c:_gdk_keymap_key_is_modifier() for how this
|
||||
* really should be implemented */
|
||||
|
||||
switch (keyval)
|
||||
{
|
||||
case GDK_Shift_L:
|
||||
case GDK_Shift_R:
|
||||
case GDK_Control_L:
|
||||
case GDK_Control_R:
|
||||
case GDK_Caps_Lock:
|
||||
case GDK_Shift_Lock:
|
||||
case GDK_Meta_L:
|
||||
case GDK_Meta_R:
|
||||
case GDK_Alt_L:
|
||||
case GDK_Alt_R:
|
||||
case GDK_Super_L:
|
||||
case GDK_Super_R:
|
||||
case GDK_Hyper_L:
|
||||
case GDK_Hyper_R:
|
||||
case GDK_ISO_Lock:
|
||||
case GDK_ISO_Level2_Latch:
|
||||
case GDK_ISO_Level3_Shift:
|
||||
case GDK_ISO_Level3_Latch:
|
||||
case GDK_ISO_Level3_Lock:
|
||||
case GDK_ISO_Level5_Shift:
|
||||
case GDK_ISO_Level5_Latch:
|
||||
case GDK_ISO_Level5_Lock:
|
||||
case GDK_ISO_Group_Shift:
|
||||
case GDK_ISO_Group_Latch:
|
||||
case GDK_ISO_Group_Lock:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static GdkEventKey *
|
||||
key_event_to_gdk (ClutterKeyEvent *event_clutter)
|
||||
{
|
||||
GdkDisplay *display = gdk_display_get_default ();
|
||||
GdkKeymap *keymap = gdk_keymap_get_for_display (display);
|
||||
GdkEventKey *event_gdk;
|
||||
event_gdk = (GdkEventKey *)gdk_event_new ((event_clutter->type == CLUTTER_KEY_PRESS) ?
|
||||
GDK_KEY_PRESS : GDK_KEY_RELEASE);
|
||||
|
||||
event_gdk->window = window_for_actor ((ClutterActor *)event_clutter->stage);
|
||||
event_gdk->send_event = FALSE;
|
||||
event_gdk->time = event_clutter->time;
|
||||
/* This depends on ClutterModifierType and GdkModifierType being
|
||||
* identical, which they are currently. (They both match the X
|
||||
* modifier state in the low 16-bits and have the same extensions.) */
|
||||
event_gdk->state = event_clutter->modifier_state;
|
||||
event_gdk->keyval = event_clutter->keyval;
|
||||
event_gdk->hardware_keycode = event_clutter->hardware_keycode;
|
||||
/* For non-proper non-XKB support, we'd need a huge cut-and-paste
|
||||
* from gdkkeys-x11.c; this is a macro that just shifts a few bits
|
||||
* out of state, so won't make the situation worse if the server
|
||||
* doesn't support XKB; we'll just end up with group == 0 */
|
||||
event_gdk->group = XkbGroupForCoreState (event_gdk->state);
|
||||
|
||||
gdk_keymap_translate_keyboard_state (keymap, event_gdk->hardware_keycode,
|
||||
event_gdk->state, event_gdk->group,
|
||||
&event_gdk->keyval, NULL, NULL, NULL);
|
||||
|
||||
if (event_clutter->unicode_value)
|
||||
{
|
||||
/* This is not particularly close to what GDK does - event_gdk->string
|
||||
* is supposed to be in the locale encoding, and have control keys
|
||||
* as control characters, etc. See gdkevents-x11.c:translate_key_event().
|
||||
* Hopefully no input method is using event.string.
|
||||
*/
|
||||
char buf[6];
|
||||
|
||||
event_gdk->length = g_unichar_to_utf8 (event_clutter->unicode_value, buf);
|
||||
event_gdk->string = g_strndup (buf, event_gdk->length);
|
||||
}
|
||||
|
||||
event_gdk->is_modifier = key_is_modifier (event_gdk->keyval);
|
||||
|
||||
return event_gdk;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_im_text_button_press_event (ClutterActor *actor,
|
||||
ClutterButtonEvent *event)
|
||||
{
|
||||
/* The button press indicates the user moving the cursor, or selecting
|
||||
* etc, so we should abort any current preedit operation. ClutterText
|
||||
* treats all buttons identically, so so do we.
|
||||
*/
|
||||
reset_im_context (ST_IM_TEXT (actor));
|
||||
|
||||
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->button_press_event)
|
||||
return CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->button_press_event (actor, event);
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_im_text_key_press_event (ClutterActor *actor,
|
||||
ClutterKeyEvent *event)
|
||||
{
|
||||
StIMText *self = ST_IM_TEXT (actor);
|
||||
StIMTextPrivate *priv = self->priv;
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (actor);
|
||||
gboolean result = FALSE;
|
||||
int old_position;
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
{
|
||||
GdkEventKey *event_gdk = key_event_to_gdk (event);
|
||||
|
||||
if (gtk_im_context_filter_keypress (priv->im_context, event_gdk))
|
||||
{
|
||||
priv->need_im_reset = TRUE;
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
gdk_event_free ((GdkEvent *)event_gdk);
|
||||
}
|
||||
|
||||
/* ClutterText:position isn't properly notified, so we have to
|
||||
* check before/after to catch a keypress (like an arrow key)
|
||||
* moving the cursor position, which should reset the IM context.
|
||||
* (Resetting on notify::position would require a sentinel when
|
||||
* committing text)
|
||||
*
|
||||
* http://bugzilla.openedhand.com/show_bug.cgi?id=1830
|
||||
*/
|
||||
old_position = clutter_text_get_cursor_position (clutter_text);
|
||||
|
||||
if (!result &&
|
||||
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_press_event)
|
||||
result = CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_press_event (actor, event);
|
||||
|
||||
if (clutter_text_get_cursor_position (clutter_text) != old_position)
|
||||
reset_im_context (self);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_im_text_key_release_event (ClutterActor *actor,
|
||||
ClutterKeyEvent *event)
|
||||
{
|
||||
StIMText *self = ST_IM_TEXT (actor);
|
||||
StIMTextPrivate *priv = self->priv;
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (actor);
|
||||
GdkEventKey *event_gdk;
|
||||
gboolean result = FALSE;
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
{
|
||||
event_gdk = key_event_to_gdk (event);
|
||||
|
||||
if (gtk_im_context_filter_keypress (priv->im_context, event_gdk))
|
||||
{
|
||||
priv->need_im_reset = TRUE;
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
gdk_event_free ((GdkEvent *)event_gdk);
|
||||
}
|
||||
|
||||
if (!result &&
|
||||
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_release_event)
|
||||
result = CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_release_event (actor, event);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_key_focus_in (ClutterActor *actor)
|
||||
{
|
||||
StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv;
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (actor);
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
{
|
||||
priv->need_im_reset = TRUE;
|
||||
gtk_im_context_focus_in (priv->im_context);
|
||||
}
|
||||
|
||||
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_in)
|
||||
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_in (actor);
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_key_focus_out (ClutterActor *actor)
|
||||
{
|
||||
StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv;
|
||||
ClutterText *clutter_text = CLUTTER_TEXT (actor);
|
||||
|
||||
if (clutter_text_get_editable (clutter_text))
|
||||
{
|
||||
priv->need_im_reset = TRUE;
|
||||
gtk_im_context_focus_out (priv->im_context);
|
||||
}
|
||||
|
||||
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_out)
|
||||
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_out (actor);
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_class_init (StIMTextClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StIMTextPrivate));
|
||||
|
||||
object_class->dispose = st_im_text_dispose;
|
||||
|
||||
actor_class->paint = st_im_text_paint;
|
||||
actor_class->realize = st_im_text_realize;
|
||||
actor_class->unrealize = st_im_text_unrealize;
|
||||
|
||||
actor_class->button_press_event = st_im_text_button_press_event;
|
||||
actor_class->key_press_event = st_im_text_key_press_event;
|
||||
actor_class->key_release_event = st_im_text_key_release_event;
|
||||
actor_class->key_focus_in = st_im_text_key_focus_in;
|
||||
actor_class->key_focus_out = st_im_text_key_focus_out;
|
||||
}
|
||||
|
||||
static void
|
||||
st_im_text_init (StIMText *self)
|
||||
{
|
||||
StIMTextPrivate *priv;
|
||||
|
||||
self->priv = priv = ST_IM_TEXT_GET_PRIVATE (self);
|
||||
|
||||
priv->im_context = gtk_im_multicontext_new ();
|
||||
g_signal_connect (priv->im_context, "commit",
|
||||
G_CALLBACK (st_im_text_commit_cb), self);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_im_text_new:
|
||||
* @text: text to set to
|
||||
*
|
||||
* Create a new #StIMText with the specified text
|
||||
*
|
||||
* Returns: a new #ClutterActor
|
||||
*/
|
||||
ClutterActor *
|
||||
st_im_text_new (const gchar *text)
|
||||
{
|
||||
return g_object_new (ST_TYPE_IM_TEXT,
|
||||
"text", text,
|
||||
NULL);
|
||||
}
|
69
src/st/st-im-text.h
Normal file
69
src/st/st-im-text.h
Normal file
@ -0,0 +1,69 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/*
|
||||
* st-imtext.h
|
||||
*
|
||||
* This is a copy of ClutterIMText converted to use GtkIMContext rather
|
||||
* than ClutterIMContext. Original code:
|
||||
*
|
||||
* Author: raymond liu <raymond.liu@intel.com>
|
||||
*
|
||||
* Copyright (C) 2009, Intel Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; 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_IM_TEXT_H__
|
||||
#define __ST_IM_TEXT_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#define ST_TYPE_IM_TEXT (st_im_text_get_type ())
|
||||
#define ST_IM_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_IM_TEXT, StIMText))
|
||||
#define ST_IS_IM_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_IM_TEXT))
|
||||
#define ST_IM_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_IM_TEXT, StIMTextClass))
|
||||
#define ST_IS_IM_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_IM_TEXT))
|
||||
#define ST_IM_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_IM_TEXT, StIMTextClass))
|
||||
|
||||
typedef struct _StIMText StIMText;
|
||||
typedef struct _StIMTextPrivate StIMTextPrivate;
|
||||
typedef struct _StIMTextClass StIMTextClass;
|
||||
|
||||
struct _StIMText
|
||||
{
|
||||
ClutterText parent_instance;
|
||||
|
||||
StIMTextPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StIMTextClass
|
||||
{
|
||||
ClutterTextClass parent_class;
|
||||
};
|
||||
|
||||
GType st_im_text_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ClutterActor *st_im_text_new (const gchar *text);
|
||||
void st_im_text_set_autoshow_im (StIMText *self,
|
||||
gboolean autoshow);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_IM_TEXT_H__ */
|
@ -18,6 +18,9 @@ struct _StThemeContextClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
#define DEFAULT_RESOLUTION 96.
|
||||
#define DEFAULT_FONT "sans-serif 10"
|
||||
|
||||
enum
|
||||
{
|
||||
CHANGED,
|
||||
@ -64,10 +67,18 @@ st_theme_context_class_init (StThemeContextClass *klass)
|
||||
static void
|
||||
st_theme_context_init (StThemeContext *context)
|
||||
{
|
||||
context->resolution = 96.;
|
||||
context->font = pango_font_description_from_string ("sans-serif 10");
|
||||
context->resolution = DEFAULT_RESOLUTION;
|
||||
context->font = pango_font_description_from_string (DEFAULT_FONT);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_new:
|
||||
*
|
||||
* Create a new theme context not associated with any #ClutterStage.
|
||||
* This can be useful in testing scenarios, or if using StThemeContext
|
||||
* with something other than #ClutterActor objects, but you generally
|
||||
* should use st_theme_context_get_for_stage() instead.
|
||||
*/
|
||||
StThemeContext *
|
||||
st_theme_context_new (void)
|
||||
{
|
||||
@ -87,6 +98,18 @@ on_stage_destroy (ClutterStage *stage)
|
||||
g_object_unref (context);
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_context_changed (StThemeContext *context)
|
||||
{
|
||||
StThemeNode *old_root = context->root_node;
|
||||
context->root_node = NULL;
|
||||
|
||||
g_signal_emit (context, signals[CHANGED], 0);
|
||||
|
||||
if (old_root)
|
||||
g_object_unref (old_root);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_get_for_stage:
|
||||
* @stage: a #ClutterStage
|
||||
@ -139,7 +162,7 @@ st_theme_context_set_theme (StThemeContext *context,
|
||||
if (context->theme)
|
||||
g_object_ref (context->theme);
|
||||
|
||||
g_signal_emit (context, signals[CHANGED], 0);
|
||||
st_theme_context_changed (context);
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,36 +182,82 @@ st_theme_context_get_theme (StThemeContext *context)
|
||||
return context->theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_set_resolution:
|
||||
* @context: a #StThemeContext
|
||||
* @resolution: resolution of the context (number of pixels in an "inch")
|
||||
*
|
||||
* Sets the resolution of the theme context. This is the scale factor
|
||||
* used to convert between points and the length units pt, in, and cm.
|
||||
* This does not necessarily need to correspond to the actual number
|
||||
* resolution of the device. A value of 72. means that points and
|
||||
* pixels are identical. The default value is 96.
|
||||
*/
|
||||
void
|
||||
st_theme_context_set_resolution (StThemeContext *context,
|
||||
double resolution)
|
||||
{
|
||||
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
|
||||
|
||||
if (resolution == context->resolution)
|
||||
return;
|
||||
|
||||
context->resolution = resolution;
|
||||
st_theme_context_changed (context);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_set_resolution:
|
||||
* @context: a #StThemeContext
|
||||
*
|
||||
* Gets the current resolution of the theme context.
|
||||
* See st_theme_context_set_resolution().
|
||||
*
|
||||
* Return value: the resolution (in dots-per-"inch")
|
||||
*/
|
||||
double
|
||||
st_theme_context_get_resolution (StThemeContext *context)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), 96.);
|
||||
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), DEFAULT_RESOLUTION);
|
||||
|
||||
return context->resolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_set_font:
|
||||
* @context: a #StThemeContext
|
||||
* @font: the default font for theme context
|
||||
*
|
||||
* Sets the default font for the theme context. This is the font that
|
||||
* is inherited by the root node of the tree of theme nodes. If the
|
||||
* font is not overriden, then this font will be used. If the font is
|
||||
* partially modified (for example, with 'font-size: 110%', then that
|
||||
* modification is based on this font.
|
||||
*/
|
||||
void
|
||||
st_theme_context_set_font (StThemeContext *context,
|
||||
const PangoFontDescription *font)
|
||||
{
|
||||
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
|
||||
g_return_if_fail (font != NULL);
|
||||
|
||||
if (context->font == font)
|
||||
if (context->font == font ||
|
||||
pango_font_description_equal (context->font, font))
|
||||
return;
|
||||
|
||||
pango_font_description_free (context->font);
|
||||
context->font = pango_font_description_copy (font);
|
||||
st_theme_context_changed (context);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_context_get_font:
|
||||
* @context: a #StThemeContext
|
||||
*
|
||||
* Gets the default font for the theme context. See st_theme_context_set_font().
|
||||
*
|
||||
* Return value: the default font for the theme context.
|
||||
*/
|
||||
const PangoFontDescription *
|
||||
st_theme_context_get_font (StThemeContext *context)
|
||||
{
|
||||
|
@ -8,6 +8,16 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* SECTION:StThemeContext
|
||||
* @short_description: holds global information about a tree of styled objects
|
||||
*
|
||||
* #StThemeContext is responsible for managing information global to a tree of styled objects,
|
||||
* such as the set of stylesheets or the default font. In normal usage, a #StThemeContext
|
||||
* is bound to a #ClutterStage; a singleton #StThemeContext can be obtained for a #ClutterStage
|
||||
* by using st_theme_context_get_for_stage().
|
||||
*/
|
||||
|
||||
typedef struct _StThemeContextClass StThemeContextClass;
|
||||
|
||||
#define ST_TYPE_THEME_CONTEXT (st_theme_context_get_type ())
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -10,18 +9,8 @@
|
||||
|
||||
static void st_theme_node_init (StThemeNode *node);
|
||||
static void st_theme_node_class_init (StThemeNodeClass *klass);
|
||||
static void st_theme_node_dispose (GObject *object);
|
||||
static void st_theme_node_finalize (GObject *object);
|
||||
|
||||
|
||||
#if 0
|
||||
enum {
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static int signals[LAST_SIGNAL];
|
||||
#endif
|
||||
|
||||
struct _StThemeNode {
|
||||
GObject parent;
|
||||
|
||||
@ -50,8 +39,7 @@ struct _StThemeNode {
|
||||
CRDeclaration **properties;
|
||||
int n_properties;
|
||||
|
||||
/* We hold onto these separately so we can unref them; the alternative
|
||||
* would be to ref everything in ->properties */
|
||||
/* We hold onto these separately so we can destroy them on finalize */
|
||||
CRDeclaration *inline_properties;
|
||||
|
||||
guint properties_computed : 1;
|
||||
@ -82,23 +70,13 @@ st_theme_node_class_init (StThemeNodeClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = st_theme_node_dispose;
|
||||
object_class->finalize = st_theme_node_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_node_dispose (GObject *object)
|
||||
{
|
||||
/* StThemeNode *node = ST_THEME_NODE (object); */
|
||||
|
||||
G_OBJECT_CLASS (st_theme_node_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_node_finalize (GObject *object)
|
||||
{
|
||||
StThemeNode *node = ST_THEME_NODE (object);
|
||||
CRDeclaration *cur_decl;
|
||||
|
||||
g_free (node->element_id);
|
||||
g_free (node->element_class);
|
||||
@ -112,8 +90,11 @@ st_theme_node_finalize (GObject *object)
|
||||
node->n_properties = 0;
|
||||
}
|
||||
|
||||
for (cur_decl = node->inline_properties; cur_decl; cur_decl = cur_decl->next)
|
||||
cr_declaration_unref (cur_decl);
|
||||
if (node->inline_properties)
|
||||
{
|
||||
/* This destroys the list, not just the head of the list */
|
||||
cr_declaration_destroy (node->inline_properties);
|
||||
}
|
||||
|
||||
if (node->font_desc)
|
||||
{
|
||||
@ -133,6 +114,28 @@ st_theme_node_finalize (GObject *object)
|
||||
G_OBJECT_CLASS (st_theme_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_node_new:
|
||||
* @context: the context representing global state for this themed tree
|
||||
* @parent_node: (allow-none): the parent node of this node
|
||||
* @theme: (allow-none): a theme (stylesheet set) that overrides the
|
||||
* theme inherited from the parent node
|
||||
* @element_type: the type of the GObject represented by this node
|
||||
* in the tree (corresponding to an element if we were theming an XML
|
||||
* document. %G_TYPE_NONE means this style was created for the stage
|
||||
* actor and matches a selector element name of 'stage'.
|
||||
* @element_id: (allow-none): the ID to match CSS rules against
|
||||
* @element_class: (allow-none): a whitespace-separated list of classes
|
||||
* to match CSS rules against
|
||||
* @pseudo_class: (allow-none): a whitespace-separated list of pseudo-classes
|
||||
* (like 'hover' or 'visited') to match CSS rules against
|
||||
*
|
||||
* Creates a new #StThemeNode. Once created, a node is immutable. Of any
|
||||
* of the attributes of the node (like the @element_class) change the node
|
||||
* and its child nodes must be destroyed and recreated.
|
||||
*
|
||||
* Return value: (transfer full): the theme node
|
||||
*/
|
||||
StThemeNode *
|
||||
st_theme_node_new (StThemeContext *context,
|
||||
StThemeNode *parent_node,
|
||||
@ -255,9 +258,7 @@ ensure_properties (StThemeNode *node)
|
||||
if (!properties)
|
||||
properties = g_ptr_array_new ();
|
||||
|
||||
node->inline_properties = cr_declaration_parse_list_from_buf ((const guchar *)node->inline_style,
|
||||
CR_UTF_8);
|
||||
|
||||
node->inline_properties = _st_theme_parse_declaration_list (node->inline_style);
|
||||
for (cur_decl = node->inline_properties; cur_decl; cur_decl = cur_decl->next)
|
||||
g_ptr_array_add (properties, cur_decl);
|
||||
}
|
||||
@ -300,7 +301,10 @@ term_is_transparent (CRTerm *term)
|
||||
static int
|
||||
color_component_from_double (double component)
|
||||
{
|
||||
/* http://people.redhat.com/otaylor/pixel-converting.html */
|
||||
/* We want to spread the range 0-1 equally over 0..255, but
|
||||
* 1.0 should map to 255 not 256, so we need to special-case it.
|
||||
* See http://people.redhat.com/otaylor/pixel-converting.html
|
||||
* for (very) detailed discussion of related issues. */
|
||||
if (component >= 1.0)
|
||||
return 255;
|
||||
else
|
||||
@ -340,17 +344,11 @@ get_color_from_rgba_term (CRTerm *term,
|
||||
if (i < 3)
|
||||
{
|
||||
if (num->type == NUM_PERCENTAGE)
|
||||
{
|
||||
value = num->val / 100;
|
||||
}
|
||||
value = num->val / 100;
|
||||
else if (num->type == NUM_GENERIC)
|
||||
{
|
||||
value = num->val / 255;
|
||||
}
|
||||
value = num->val / 255;
|
||||
else
|
||||
{
|
||||
return VALUE_NOT_FOUND;
|
||||
}
|
||||
return VALUE_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -408,12 +406,21 @@ get_color_from_term (StThemeNode *node,
|
||||
/* rgba () colors - a CSS3 addition, are not supported by libcroco,
|
||||
* but they are parsed as a "function", so we can emulate the
|
||||
* functionality.
|
||||
*
|
||||
* libcroco < 0.6.2 has a bug where functions starting with 'r' are
|
||||
* misparsed. We workaround this by pre-converting 'rgba' to 'RGBA'
|
||||
* before parsing the stylesheet. Since libcroco isn't
|
||||
* case-insensitive (a bug), it's fine with functions starting with
|
||||
* 'R'. (In theory, we should be doing a case-insensitive compare
|
||||
* everywhere, not just here, but that doesn't make much sense when
|
||||
* the built-in parsing of libcroco is case-sensitive and things
|
||||
* like 10PX don't work.)
|
||||
*/
|
||||
else if (term->type == TERM_FUNCTION &&
|
||||
term->content.str &&
|
||||
term->content.str->stryng &&
|
||||
term->content.str->stryng->str &&
|
||||
strcmp (term->content.str->stryng->str, "rgba") == 0)
|
||||
g_ascii_strcasecmp (term->content.str->stryng->str, "rgba") == 0)
|
||||
{
|
||||
return get_color_from_rgba_term (term, color);
|
||||
}
|
||||
@ -436,6 +443,27 @@ get_color_from_term (StThemeNode *node,
|
||||
return VALUE_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_node_get_color:
|
||||
* @node: a #StThemeNode
|
||||
* @property_name: The name of the color property
|
||||
* @inherit: if %TRUE, if a value is not found for the property on the
|
||||
* node, then it will be looked up on the parent node, and then on the
|
||||
* parent's parent, and so forth. Note that if the property has a
|
||||
* value of 'inherit' it will be inherited even if %FALSE is passed
|
||||
* in for @inherit; this only affects the default behavior for inheritance.
|
||||
* @color: (out): location to store the color that was determined.
|
||||
* If the property is not found, the value in this location
|
||||
* will not be changed.
|
||||
*
|
||||
* Generically looks up a property containing a single color value. When
|
||||
* specific getters (like st_theme_node_get_background_color()) exist, they
|
||||
* should be used instead. They are cached, so more efficient, and have
|
||||
* handling for shortcut properties and other details of CSS.
|
||||
*
|
||||
* Return value: %TRUE if the property was found in the properties for this
|
||||
* theme node (or in the properties of parent nodes when inheriting.)
|
||||
*/
|
||||
gboolean
|
||||
st_theme_node_get_color (StThemeNode *node,
|
||||
const char *property_name,
|
||||
@ -471,6 +499,25 @@ st_theme_node_get_color (StThemeNode *node,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_node_get_double:
|
||||
* @node: a #StThemeNode
|
||||
* @property_name: The name of the numeric property
|
||||
* @inherit: if %TRUE, if a value is not found for the property on the
|
||||
* node, then it will be looked up on the parent node, and then on the
|
||||
* parent's parent, and so forth. Note that if the property has a
|
||||
* value of 'inherit' it will be inherited even if %FALSE is passed
|
||||
* in for @inherit; this only affects the default behavior for inheritance.
|
||||
* @value: (out): location to store the value that was determined.
|
||||
* If the property is not found, the value in this location
|
||||
* will not be changed.
|
||||
*
|
||||
* Generically looks up a property containing a single numeric value
|
||||
* without units.
|
||||
*
|
||||
* Return value: %TRUE if the property was found in the properties for this
|
||||
* theme node (or in the properties of parent nodes when inheriting.)
|
||||
*/
|
||||
gboolean
|
||||
st_theme_node_get_double (StThemeNode *node,
|
||||
const char *property_name,
|
||||
@ -679,6 +726,28 @@ get_length_internal (StThemeNode *node,
|
||||
return VALUE_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_theme_node_get_length:
|
||||
* @node: a #StThemeNode
|
||||
* @property_name: The name of the length property
|
||||
* @inherit: if %TRUE, if a value is not found for the property on the
|
||||
* node, then it will be looked up on the parent node, and then on the
|
||||
* parent's parent, and so forth. Note that if the property has a
|
||||
* value of 'inherit' it will be inherited even if %FALSE is passed
|
||||
* in for @inherit; this only affects the default behavior for inheritance.
|
||||
* @length: (out): location to store the length that was determined.
|
||||
* If the property is not found, the value in this location
|
||||
* will not be changed. The returned length is resolved
|
||||
* to pixels.
|
||||
*
|
||||
* Generically looks up a property containing a single length value. When
|
||||
* specific getters (like st_theme_node_get_border_width()) exist, they
|
||||
* should be used instead. They are cached, so more efficient, and have
|
||||
* handling for shortcut properties and other details of CSS.
|
||||
*
|
||||
* Return value: %TRUE if the property was found in the properties for this
|
||||
* theme node (or in the properties of parent nodes when inheriting.)
|
||||
*/
|
||||
gboolean
|
||||
st_theme_node_get_length (StThemeNode *node,
|
||||
const char *property_name,
|
||||
@ -770,21 +839,13 @@ do_border_radius (StThemeNode *node,
|
||||
return;
|
||||
|
||||
if (strcmp (property_name, "-topleft") == 0)
|
||||
{
|
||||
do_border_radius_term (node, decl->value, TRUE, FALSE, FALSE, FALSE);
|
||||
}
|
||||
do_border_radius_term (node, decl->value, TRUE, FALSE, FALSE, FALSE);
|
||||
else if (strcmp (property_name, "-topright") == 0)
|
||||
{
|
||||
do_border_radius_term (node, decl->value, FALSE, TRUE, FALSE, FALSE);
|
||||
}
|
||||
do_border_radius_term (node, decl->value, FALSE, TRUE, FALSE, FALSE);
|
||||
else if (strcmp (property_name, "-bottomright") == 0)
|
||||
{
|
||||
do_border_radius_term (node, decl->value, FALSE, FALSE, TRUE, FALSE);
|
||||
}
|
||||
do_border_radius_term (node, decl->value, FALSE, FALSE, TRUE, FALSE);
|
||||
else if (strcmp (property_name, "-bottomleft") == 0)
|
||||
{
|
||||
do_border_radius_term (node, decl->value, FALSE, FALSE, FALSE, TRUE);
|
||||
}
|
||||
do_border_radius_term (node, decl->value, FALSE, FALSE, FALSE, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -890,9 +951,8 @@ do_border_property (StThemeNode *node,
|
||||
return;
|
||||
|
||||
if (get_color_from_term (node, decl->value, &color) == VALUE_FOUND)
|
||||
{ /* Ignore inherit */
|
||||
color_set = TRUE;
|
||||
}
|
||||
/* Ignore inherit */
|
||||
color_set = TRUE;
|
||||
}
|
||||
else if (strcmp (property_name, "-width") == 0)
|
||||
{
|
||||
@ -900,9 +960,8 @@ do_border_property (StThemeNode *node,
|
||||
return;
|
||||
|
||||
if (get_length_from_term (node, decl->value, FALSE, &width) == VALUE_FOUND)
|
||||
{ /* Ignore inherit */
|
||||
width_set = TRUE;
|
||||
}
|
||||
/* Ignore inherit */
|
||||
width_set = TRUE;
|
||||
}
|
||||
|
||||
if (side == (StSide)-1)
|
||||
@ -996,21 +1055,13 @@ do_padding_property (StThemeNode *node,
|
||||
return;
|
||||
|
||||
if (strcmp (property_name, "-left") == 0)
|
||||
{
|
||||
do_padding_property_term (node, decl->value, TRUE, FALSE, FALSE, FALSE);
|
||||
}
|
||||
do_padding_property_term (node, decl->value, TRUE, FALSE, FALSE, FALSE);
|
||||
else if (strcmp (property_name, "-right") == 0)
|
||||
{
|
||||
do_padding_property_term (node, decl->value, FALSE, TRUE, FALSE, FALSE);
|
||||
}
|
||||
do_padding_property_term (node, decl->value, FALSE, TRUE, FALSE, FALSE);
|
||||
else if (strcmp (property_name, "-top") == 0)
|
||||
{
|
||||
do_padding_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE);
|
||||
}
|
||||
do_padding_property_term (node, decl->value, FALSE, FALSE, TRUE, FALSE);
|
||||
else if (strcmp (property_name, "-bottom") == 0)
|
||||
{
|
||||
do_padding_property_term (node, decl->value, FALSE, FALSE, FALSE, TRUE);
|
||||
}
|
||||
do_padding_property_term (node, decl->value, FALSE, FALSE, FALSE, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1038,19 +1089,15 @@ ensure_borders (StThemeNode *node)
|
||||
const char *property_name = decl->property->stryng->str;
|
||||
|
||||
if (g_str_has_prefix (property_name, "border"))
|
||||
{
|
||||
do_border_property (node, decl);
|
||||
}
|
||||
do_border_property (node, decl);
|
||||
else if (g_str_has_prefix (property_name, "padding"))
|
||||
{
|
||||
do_padding_property (node, decl);
|
||||
}
|
||||
do_padding_property (node, decl);
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
st_theme_node_get_border_width (StThemeNode *node,
|
||||
StSide side)
|
||||
StSide side)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
|
||||
g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
|
||||
@ -1132,6 +1179,7 @@ ensure_background (StThemeNode *node)
|
||||
GetFromTermResult result = get_background_color_from_term (node, term, &node->background_color);
|
||||
if (result == VALUE_FOUND)
|
||||
{
|
||||
/* color stored in node->background_color */
|
||||
}
|
||||
else if (result == VALUE_INHERIT)
|
||||
{
|
||||
@ -1143,6 +1191,7 @@ ensure_background (StThemeNode *node)
|
||||
}
|
||||
else if (term_is_none (term))
|
||||
{
|
||||
/* leave node->background_color as transparent */
|
||||
}
|
||||
else if (term->type == TERM_URI)
|
||||
{
|
||||
@ -1162,6 +1211,7 @@ ensure_background (StThemeNode *node)
|
||||
result = get_background_color_from_term (node, decl->value, &node->background_color);
|
||||
if (result == VALUE_FOUND)
|
||||
{
|
||||
/* color stored in node->background_color */
|
||||
}
|
||||
else if (result == VALUE_INHERIT)
|
||||
{
|
||||
@ -1178,8 +1228,8 @@ ensure_background (StThemeNode *node)
|
||||
{
|
||||
g_free (node->background_image);
|
||||
node->background_image = _st_theme_resolve_url (node->theme,
|
||||
decl->parent_statement->parent_sheet,
|
||||
decl->value->content.str->stryng->str);
|
||||
decl->parent_statement->parent_sheet,
|
||||
decl->value->content.str->stryng->str);
|
||||
}
|
||||
else if (term_is_inherit (decl->value))
|
||||
{
|
||||
@ -1370,18 +1420,14 @@ font_family_from_terms (CRTerm *term,
|
||||
{
|
||||
if (term->the_operator != COMMA && term->the_operator != NO_OP)
|
||||
goto out;
|
||||
/* Can concetenate to bare words, but not two quoted strings */
|
||||
/* Can concatenate two bare words, but not two quoted strings */
|
||||
if ((term->the_operator == NO_OP && last_was_quoted) || term->type == TERM_STRING)
|
||||
goto out;
|
||||
|
||||
if (term->the_operator == NO_OP)
|
||||
{
|
||||
g_string_append (family_string, " ");
|
||||
}
|
||||
g_string_append (family_string, " ");
|
||||
else
|
||||
{
|
||||
g_string_append (family_string, ", ");
|
||||
}
|
||||
g_string_append (family_string, ", ");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1434,33 +1480,19 @@ font_size_from_term (StThemeNode *node,
|
||||
int size_points = (int)(0.5 + *size * (72. / resolution));
|
||||
|
||||
if (strcmp (term->content.str->stryng->str, "xx-small") == 0)
|
||||
{
|
||||
size_points = font_sizes[0];
|
||||
}
|
||||
size_points = font_sizes[0];
|
||||
else if (strcmp (term->content.str->stryng->str, "x-small") == 0)
|
||||
{
|
||||
size_points = font_sizes[1];
|
||||
}
|
||||
size_points = font_sizes[1];
|
||||
else if (strcmp (term->content.str->stryng->str, "small") == 0)
|
||||
{
|
||||
size_points = font_sizes[2];
|
||||
}
|
||||
size_points = font_sizes[2];
|
||||
else if (strcmp (term->content.str->stryng->str, "medium") == 0)
|
||||
{
|
||||
size_points = font_sizes[3];
|
||||
}
|
||||
size_points = font_sizes[3];
|
||||
else if (strcmp (term->content.str->stryng->str, "large") == 0)
|
||||
{
|
||||
size_points = font_sizes[4];
|
||||
}
|
||||
size_points = font_sizes[4];
|
||||
else if (strcmp (term->content.str->stryng->str, "x-large") == 0)
|
||||
{
|
||||
size_points = font_sizes[5];
|
||||
}
|
||||
size_points = font_sizes[5];
|
||||
else if (strcmp (term->content.str->stryng->str, "xx-large") == 0)
|
||||
{
|
||||
size_points = font_sizes[6];
|
||||
}
|
||||
size_points = font_sizes[6];
|
||||
else if (strcmp (term->content.str->stryng->str, "smaller") == 0)
|
||||
{
|
||||
/* Find the standard size equal to or smaller than the current size */
|
||||
@ -1470,7 +1502,8 @@ font_size_from_term (StThemeNode *node,
|
||||
i++;
|
||||
|
||||
if (i > 6)
|
||||
{ /* original size greater than any standard size */
|
||||
{
|
||||
/* original size greater than any standard size */
|
||||
size_points = (int)(0.5 + size_points / 1.2);
|
||||
}
|
||||
else
|
||||
@ -1538,7 +1571,7 @@ font_weight_from_term (CRTerm *term,
|
||||
if (term->content.num->type != NUM_GENERIC)
|
||||
return FALSE;
|
||||
|
||||
weight_int = (int)(term->content.num->val + 0.5);
|
||||
weight_int = (int)(0.5 + term->content.num->val);
|
||||
|
||||
*weight = weight_int;
|
||||
*weight_absolute = TRUE;
|
||||
@ -1671,13 +1704,13 @@ st_theme_node_get_font (StThemeNode *node)
|
||||
for (; term; term = term->next)
|
||||
{
|
||||
if (font_style_from_term (term, &tmp_style))
|
||||
;
|
||||
else if (font_variant_from_term (term, &tmp_variant))
|
||||
;
|
||||
else if (font_weight_from_term (term, &tmp_weight, &tmp_weight_absolute))
|
||||
;
|
||||
else
|
||||
break;
|
||||
continue;
|
||||
if (font_variant_from_term (term, &tmp_variant))
|
||||
continue;
|
||||
if (font_weight_from_term (term, &tmp_weight, &tmp_weight_absolute))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* The size is mandatory */
|
||||
@ -1872,7 +1905,7 @@ st_theme_node_get_border_image (StThemeNode *node)
|
||||
|
||||
if (term->content.num->type == NUM_GENERIC)
|
||||
{
|
||||
borders[n_borders] = round (0.5 + term->content.num->val);
|
||||
borders[n_borders] = (int)(0.5 + term->content.num->val);
|
||||
n_borders++;
|
||||
}
|
||||
else if (term->content.num->type == NUM_PERCENTAGE)
|
||||
@ -1935,15 +1968,15 @@ st_theme_node_get_border_image (StThemeNode *node)
|
||||
static float
|
||||
get_width_inc (StThemeNode *node)
|
||||
{
|
||||
return (round (node->border_width[ST_SIDE_LEFT]) + node->padding[ST_SIDE_LEFT] +
|
||||
round (node->border_width[ST_SIDE_RIGHT]) + node->padding[ST_SIDE_RIGHT]);
|
||||
return ((int)(0.5 + node->border_width[ST_SIDE_LEFT]) + node->padding[ST_SIDE_LEFT] +
|
||||
(int)(0.5 + node->border_width[ST_SIDE_RIGHT]) + node->padding[ST_SIDE_RIGHT]);
|
||||
}
|
||||
|
||||
static float
|
||||
get_height_inc (StThemeNode *node)
|
||||
{
|
||||
return (round (node->border_width[ST_SIDE_TOP]) + node->padding[ST_SIDE_TOP] +
|
||||
round (node->border_width[ST_SIDE_BOTTOM]) + node->padding[ST_SIDE_BOTTOM]);
|
||||
return ((int)(0.5 + node->border_width[ST_SIDE_TOP]) + node->padding[ST_SIDE_TOP] +
|
||||
(int)(0.5 + node->border_width[ST_SIDE_BOTTOM]) + node->padding[ST_SIDE_BOTTOM]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2076,10 +2109,10 @@ st_theme_node_get_content_box (StThemeNode *node,
|
||||
|
||||
ensure_borders (node);
|
||||
|
||||
content_box->x1 = round (node->border_width[ST_SIDE_LEFT]) + node->padding[ST_SIDE_LEFT];
|
||||
content_box->y1 = round (node->border_width[ST_SIDE_TOP]) + node->padding[ST_SIDE_TOP];
|
||||
content_box->x2 = allocation->x2 - allocation->x1 - (round (node->border_width[ST_SIDE_RIGHT]) + node->padding[ST_SIDE_RIGHT]);
|
||||
content_box->y2 = allocation->y2 - allocation->y1 - (round (node->border_width[ST_SIDE_BOTTOM]) + node->padding[ST_SIDE_BOTTOM]);
|
||||
content_box->x1 = (int)(0.5 + node->border_width[ST_SIDE_LEFT]) + node->padding[ST_SIDE_LEFT];
|
||||
content_box->y1 = (int)(0.5 + node->border_width[ST_SIDE_TOP]) + node->padding[ST_SIDE_TOP];
|
||||
content_box->x2 = allocation->x2 - allocation->x1 - ((int)(0.5 + node->border_width[ST_SIDE_RIGHT]) + node->padding[ST_SIDE_RIGHT]);
|
||||
content_box->y2 = allocation->y2 - allocation->y1 - ((int)(0.5 + node->border_width[ST_SIDE_BOTTOM]) + node->padding[ST_SIDE_BOTTOM]);
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,6 +7,21 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* SECTION:StThemeNode
|
||||
* @short_description: style information for one node in a tree of themed objects
|
||||
*
|
||||
* A #StThemeNode represents the CSS style information (the set of CSS properties) for one
|
||||
* node in a tree of themed objects. In typical usage, it represents the style information
|
||||
* for a single #ClutterActor. A #StThemeNode is immutable: attributes such as the
|
||||
* CSS classes for the node are passed in at construction. If the attributes of the node
|
||||
* or any parent node change, the node should be discarded and a new node created.
|
||||
* #StThemeNode has generic accessors to look up properties by name and specific
|
||||
* accessors for standard CSS properties that add caching and handling of various
|
||||
* details of the CSS specification. #StThemeNode also has convenience functions to help
|
||||
* in implementing a #ClutterActor with borders and padding.
|
||||
*/
|
||||
|
||||
typedef struct _StTheme StTheme;
|
||||
typedef struct _StThemeContext StThemeContext;
|
||||
|
||||
@ -44,9 +59,6 @@ typedef enum {
|
||||
|
||||
GType st_theme_node_get_type (void) G_GNUC_CONST;
|
||||
|
||||
/* An element_type of G_TYPE_NONE means this style was created for the stage
|
||||
* actor and matches a selector element name of 'stage'
|
||||
*/
|
||||
StThemeNode *st_theme_node_new (StThemeContext *context,
|
||||
StThemeNode *parent_node, /* can be null */
|
||||
StTheme *theme, /* can be null */
|
||||
@ -80,8 +92,6 @@ gboolean st_theme_node_get_double (StThemeNode *node,
|
||||
gboolean inherit,
|
||||
double *value);
|
||||
|
||||
/* The length here is already resolved to pixels
|
||||
*/
|
||||
gboolean st_theme_node_get_length (StThemeNode *node,
|
||||
const char *property_name,
|
||||
gboolean inherit,
|
||||
|
@ -15,6 +15,8 @@ char *_st_theme_resolve_url (StTheme *theme,
|
||||
CRStyleSheet *base_stylesheet,
|
||||
const char *url);
|
||||
|
||||
CRDeclaration *_st_theme_parse_declaration_list (const char *str);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __ST_THEME_PRIVATE_H__ */
|
||||
|
@ -9,14 +9,14 @@
|
||||
* make sense in our context.
|
||||
* - The code to get a list of matching properties works quite differently;
|
||||
* we order things in priority order, but we don't actually try to
|
||||
* coelesce properties with the same name.
|
||||
* coalesce properties with the same name.
|
||||
*
|
||||
* In moving it to GNOME Shell:
|
||||
* - Renamed again to StTheme
|
||||
* - Reformatted to match the gnome-st coding style
|
||||
* - Reformatted to match the gnome-shell coding style
|
||||
* - Removed notion of "theme engine" from hippo-canvas
|
||||
* - pseudo-class matching changed from link enum to strings
|
||||
* - Some code simplication
|
||||
* - Some code simplification
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -51,7 +51,6 @@ static GObject *st_theme_constructor (GType type,
|
||||
guint n_construct_properties,
|
||||
GObjectConstructParam *construct_properties);
|
||||
|
||||
static void st_theme_dispose (GObject *object);
|
||||
static void st_theme_finalize (GObject *object);
|
||||
static void st_theme_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
@ -62,15 +61,6 @@ static void st_theme_get_property (GObject *object,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
#if 0
|
||||
enum
|
||||
{
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static int signals[LAST_SIGNAL];
|
||||
#endif
|
||||
|
||||
struct _StTheme
|
||||
{
|
||||
GObject parent;
|
||||
@ -104,7 +94,8 @@ G_DEFINE_TYPE (StTheme, st_theme, G_TYPE_OBJECT)
|
||||
#define strqcmp(str,lit,lit_len) \
|
||||
(strlen (str) != (lit_len) || memcmp (str, lit, lit_len))
|
||||
|
||||
static void st_theme_init (StTheme * theme)
|
||||
static void
|
||||
st_theme_init (StTheme *theme)
|
||||
{
|
||||
theme->stylesheets_by_filename = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
(GDestroyNotify)g_free, (GDestroyNotify)cr_stylesheet_unref);
|
||||
@ -112,60 +103,145 @@ static void st_theme_init (StTheme * theme)
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_class_init (StThemeClass * klass)
|
||||
st_theme_class_init (StThemeClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->constructor = st_theme_constructor;
|
||||
object_class->dispose = st_theme_dispose;
|
||||
object_class->finalize = st_theme_finalize;
|
||||
object_class->set_property = st_theme_set_property;
|
||||
object_class->get_property = st_theme_get_property;
|
||||
|
||||
/**
|
||||
* StTheme:application-stylesheet
|
||||
* StTheme:application-stylesheet:
|
||||
*
|
||||
* The highest priority stylesheet, representing application-specific
|
||||
* styling; this is associated with the CSS "author" stylesheet.
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_APPLICATION_STYLESHEET,
|
||||
g_param_spec_string ("application-stylesheet",
|
||||
PROP_APPLICATION_STYLESHEET,
|
||||
g_param_spec_string ("application-stylesheet",
|
||||
"Application Stylesheet",
|
||||
"Stylesheet with application-specific styling",
|
||||
NULL,
|
||||
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
|
||||
|
||||
/**
|
||||
* StTheme:theme-stylesheet
|
||||
* StTheme:theme-stylesheet:
|
||||
*
|
||||
* The second priority stylesheet, representing theme-specific styling;
|
||||
* this is associated with the CSS "user" stylesheet.
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_THEME_STYLESHEET,
|
||||
g_param_spec_string ("theme-stylesheet",
|
||||
"Theme Stylesheet",
|
||||
"Stylesheet with theme-specific styling",
|
||||
NULL,
|
||||
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
|
||||
PROP_THEME_STYLESHEET,
|
||||
g_param_spec_string ("theme-stylesheet",
|
||||
"Theme Stylesheet",
|
||||
"Stylesheet with theme-specific styling",
|
||||
NULL,
|
||||
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
|
||||
|
||||
/**
|
||||
* StTheme:default-stylesheet
|
||||
* StTheme:default-stylesheet:
|
||||
*
|
||||
* The lowest priority stylesheet, representing global default
|
||||
* styling; this is associated with the CSS "user agent" stylesheet.
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_DEFAULT_STYLESHEET,
|
||||
g_param_spec_string ("default-stylesheet",
|
||||
"Default Stylesheet",
|
||||
"Stylesheet with global default styling",
|
||||
NULL,
|
||||
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
|
||||
PROP_DEFAULT_STYLESHEET,
|
||||
g_param_spec_string ("default-stylesheet",
|
||||
"Default Stylesheet",
|
||||
"Stylesheet with global default styling",
|
||||
NULL,
|
||||
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
|
||||
|
||||
}
|
||||
|
||||
/* This is a workaround for a bug in libcroco < 0.6.2 where
|
||||
* function starting with 'r' (and 'u') are misparsed. We work
|
||||
* around this by exploiting the fact that libcroco is incomformant
|
||||
* with the CSS-spec and case sensitive and pre-convert all
|
||||
* occurrences of rgba to RGBA. Then we make our own parsing
|
||||
* code check for RGBA as well.
|
||||
*/
|
||||
#if LIBCROCO_VERSION_NUMBER < 602
|
||||
static gboolean
|
||||
is_identifier_character (char c)
|
||||
{
|
||||
/* Actual CSS rules allow for unicode > 0x00a1 and escaped
|
||||
* characters, but we'll assume we won't do that in our stylesheets
|
||||
* or at least not next to the string 'rgba'.
|
||||
*/
|
||||
return g_ascii_isalnum(c) || c == '-' || c == '_';
|
||||
}
|
||||
|
||||
static void
|
||||
convert_rgba_RGBA (char *buf)
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = strstr (buf, "rgba");
|
||||
while (p)
|
||||
{
|
||||
/* Check if this looks like a complete token; this is to
|
||||
* avoiding mangling, say, a selector '.rgba-entry' */
|
||||
if (!((p > buf && is_identifier_character (*(p - 1))) ||
|
||||
(is_identifier_character (*(p + 4)))))
|
||||
memcpy(p, "RGBA", 4);
|
||||
p += 4;
|
||||
p = strstr (p, "rgba");
|
||||
}
|
||||
}
|
||||
|
||||
static CRStyleSheet *
|
||||
parse_stylesheet (const char *filename)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
convert_rgba_RGBA (contents);
|
||||
|
||||
status = cr_om_parser_simply_parse_buf ((const guchar *) contents,
|
||||
length,
|
||||
CR_UTF_8,
|
||||
&stylesheet);
|
||||
|
||||
if (status != CR_OK)
|
||||
g_warning ("Error parsing stylesheet '%s'", filename);
|
||||
|
||||
g_free (contents);
|
||||
|
||||
return stylesheet;
|
||||
}
|
||||
|
||||
CRDeclaration *
|
||||
_st_theme_parse_declaration_list (const char *str)
|
||||
{
|
||||
char *copy = g_strdup (str);
|
||||
CRDeclaration *result;
|
||||
|
||||
convert_rgba_RGBA (copy);
|
||||
|
||||
result = cr_declaration_parse_list_from_buf ((const guchar *)copy,
|
||||
CR_UTF_8);
|
||||
g_free (copy);
|
||||
|
||||
return result;
|
||||
}
|
||||
#else /* LIBCROCO_VERSION_NUMBER >= 602 */
|
||||
static CRStyleSheet *
|
||||
parse_stylesheet (const char *filename)
|
||||
{
|
||||
@ -176,7 +252,7 @@ parse_stylesheet (const char *filename)
|
||||
return NULL;
|
||||
|
||||
status = cr_om_parser_simply_parse_file ((const guchar *) filename,
|
||||
CR_UTF_8,
|
||||
CR_UTF_8,
|
||||
&stylesheet);
|
||||
|
||||
if (status != CR_OK)
|
||||
@ -188,9 +264,17 @@ parse_stylesheet (const char *filename)
|
||||
return stylesheet;
|
||||
}
|
||||
|
||||
CRDeclaration *
|
||||
_st_theme_parse_declaration_list (const char *str)
|
||||
{
|
||||
return cr_declaration_parse_list_from_buf ((const guchar *)str,
|
||||
CR_UTF_8);
|
||||
}
|
||||
#endif /* LIBCROCO_VERSION_NUMBER < 602 */
|
||||
|
||||
static void
|
||||
insert_stylesheet (StTheme *theme,
|
||||
const char *filename,
|
||||
const char *filename,
|
||||
CRStyleSheet *stylesheet)
|
||||
{
|
||||
char *filename_copy;
|
||||
@ -217,8 +301,8 @@ st_theme_constructor (GType type,
|
||||
CRStyleSheet *default_stylesheet;
|
||||
|
||||
object = (*G_OBJECT_CLASS (st_theme_parent_class)->constructor) (type,
|
||||
n_construct_properties,
|
||||
construct_properties);
|
||||
n_construct_properties,
|
||||
construct_properties);
|
||||
theme = ST_THEME (object);
|
||||
|
||||
application_stylesheet = parse_stylesheet (theme->application_stylesheet);
|
||||
@ -226,7 +310,7 @@ st_theme_constructor (GType type,
|
||||
default_stylesheet = parse_stylesheet (theme->default_stylesheet);
|
||||
|
||||
theme->cascade = cr_cascade_new (application_stylesheet,
|
||||
theme_stylesheet,
|
||||
theme_stylesheet,
|
||||
default_stylesheet);
|
||||
|
||||
if (theme->cascade == NULL)
|
||||
@ -239,14 +323,6 @@ st_theme_constructor (GType type,
|
||||
return object;
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_dispose (GObject * object)
|
||||
{
|
||||
/* StTheme *theme = ST_THEME(object); */
|
||||
|
||||
G_OBJECT_CLASS (st_theme_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_finalize (GObject * object)
|
||||
{
|
||||
@ -280,39 +356,39 @@ st_theme_set_property (GObject *object,
|
||||
{
|
||||
case PROP_APPLICATION_STYLESHEET:
|
||||
{
|
||||
const char *path = g_value_get_string (value);
|
||||
const char *path = g_value_get_string (value);
|
||||
|
||||
if (path != theme->application_stylesheet)
|
||||
{
|
||||
g_free (theme->application_stylesheet);
|
||||
theme->application_stylesheet = g_strdup (path);
|
||||
}
|
||||
if (path != theme->application_stylesheet)
|
||||
{
|
||||
g_free (theme->application_stylesheet);
|
||||
theme->application_stylesheet = g_strdup (path);
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case PROP_THEME_STYLESHEET:
|
||||
{
|
||||
const char *path = g_value_get_string (value);
|
||||
const char *path = g_value_get_string (value);
|
||||
|
||||
if (path != theme->theme_stylesheet)
|
||||
{
|
||||
g_free (theme->theme_stylesheet);
|
||||
theme->theme_stylesheet = g_strdup (path);
|
||||
}
|
||||
if (path != theme->theme_stylesheet)
|
||||
{
|
||||
g_free (theme->theme_stylesheet);
|
||||
theme->theme_stylesheet = g_strdup (path);
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case PROP_DEFAULT_STYLESHEET:
|
||||
{
|
||||
const char *path = g_value_get_string (value);
|
||||
const char *path = g_value_get_string (value);
|
||||
|
||||
if (path != theme->default_stylesheet)
|
||||
{
|
||||
g_free (theme->default_stylesheet);
|
||||
theme->default_stylesheet = g_strdup (path);
|
||||
}
|
||||
if (path != theme->default_stylesheet)
|
||||
{
|
||||
g_free (theme->default_stylesheet);
|
||||
theme->default_stylesheet = g_strdup (path);
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
@ -362,10 +438,10 @@ st_theme_new (const char *application_stylesheet,
|
||||
const char *default_stylesheet)
|
||||
{
|
||||
StTheme *theme = g_object_new (ST_TYPE_THEME,
|
||||
"application-stylesheet", application_stylesheet,
|
||||
"theme-stylesheet", theme_stylesheet,
|
||||
"default-stylesheet", default_stylesheet,
|
||||
NULL);
|
||||
"application-stylesheet", application_stylesheet,
|
||||
"theme-stylesheet", theme_stylesheet,
|
||||
"default-stylesheet", default_stylesheet,
|
||||
NULL);
|
||||
|
||||
return theme;
|
||||
}
|
||||
@ -398,18 +474,18 @@ string_in_list (GString *stryng,
|
||||
|
||||
static gboolean
|
||||
pseudo_class_add_sel_matches_style (StTheme *a_this,
|
||||
CRAdditionalSel *a_add_sel,
|
||||
StThemeNode *a_node)
|
||||
CRAdditionalSel *a_add_sel,
|
||||
StThemeNode *a_node)
|
||||
{
|
||||
const char *node_pseudo_class;
|
||||
|
||||
g_return_val_if_fail (a_this
|
||||
&& a_add_sel
|
||||
&& a_add_sel->content.pseudo
|
||||
&& a_add_sel->content.pseudo->name
|
||||
&& a_add_sel->content.pseudo->name->stryng
|
||||
&& a_add_sel->content.pseudo->name->stryng->str
|
||||
&& a_node, FALSE);
|
||||
&& a_add_sel
|
||||
&& a_add_sel->content.pseudo
|
||||
&& a_add_sel->content.pseudo->name
|
||||
&& a_add_sel->content.pseudo->name->stryng
|
||||
&& a_add_sel->content.pseudo->name->stryng->str
|
||||
&& a_node, FALSE);
|
||||
|
||||
node_pseudo_class = st_theme_node_get_pseudo_class (a_node);
|
||||
|
||||
@ -427,16 +503,16 @@ pseudo_class_add_sel_matches_style (StTheme *a_this,
|
||||
*/
|
||||
static gboolean
|
||||
class_add_sel_matches_style (CRAdditionalSel *a_add_sel,
|
||||
StThemeNode *a_node)
|
||||
StThemeNode *a_node)
|
||||
{
|
||||
const char *element_class;
|
||||
|
||||
g_return_val_if_fail (a_add_sel
|
||||
&& a_add_sel->type == CLASS_ADD_SELECTOR
|
||||
&& a_add_sel->content.class_name
|
||||
&& a_add_sel->content.class_name->stryng
|
||||
&& a_add_sel->content.class_name->stryng->str
|
||||
&& a_node, FALSE);
|
||||
&& a_add_sel->type == CLASS_ADD_SELECTOR
|
||||
&& a_add_sel->content.class_name
|
||||
&& a_add_sel->content.class_name->stryng
|
||||
&& a_add_sel->content.class_name->stryng->str
|
||||
&& a_node, FALSE);
|
||||
|
||||
element_class = st_theme_node_get_element_class (a_node);
|
||||
if (element_class == NULL)
|
||||
@ -453,30 +529,30 @@ class_add_sel_matches_style (CRAdditionalSel *a_add_sel,
|
||||
*/
|
||||
static gboolean
|
||||
id_add_sel_matches_style (CRAdditionalSel *a_add_sel,
|
||||
StThemeNode *a_node)
|
||||
StThemeNode *a_node)
|
||||
{
|
||||
gboolean result = FALSE;
|
||||
const char *id;
|
||||
|
||||
g_return_val_if_fail (a_add_sel
|
||||
&& a_add_sel->type == ID_ADD_SELECTOR
|
||||
&& a_add_sel->content.id_name
|
||||
&& a_add_sel->content.id_name->stryng
|
||||
&& a_add_sel->content.id_name->stryng->str
|
||||
&& a_node, FALSE);
|
||||
&& a_add_sel->type == ID_ADD_SELECTOR
|
||||
&& a_add_sel->content.id_name
|
||||
&& a_add_sel->content.id_name->stryng
|
||||
&& a_add_sel->content.id_name->stryng->str
|
||||
&& a_node, FALSE);
|
||||
g_return_val_if_fail (a_add_sel
|
||||
&& a_add_sel->type == ID_ADD_SELECTOR
|
||||
&& a_node, FALSE);
|
||||
&& a_add_sel->type == ID_ADD_SELECTOR
|
||||
&& a_node, FALSE);
|
||||
|
||||
id = st_theme_node_get_element_id (a_node);
|
||||
|
||||
if (id != NULL)
|
||||
{
|
||||
if (!strqcmp (id, a_add_sel->content.id_name->stryng->str,
|
||||
a_add_sel->content.id_name->stryng->len))
|
||||
{
|
||||
result = TRUE;
|
||||
}
|
||||
a_add_sel->content.id_name->stryng->len))
|
||||
{
|
||||
result = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -490,8 +566,8 @@ id_add_sel_matches_style (CRAdditionalSel *a_add_sel,
|
||||
*/
|
||||
static gboolean
|
||||
additional_selector_matches_style (StTheme *a_this,
|
||||
CRAdditionalSel *a_add_sel,
|
||||
StThemeNode *a_node)
|
||||
CRAdditionalSel *a_add_sel,
|
||||
StThemeNode *a_node)
|
||||
{
|
||||
CRAdditionalSel *cur_add_sel = NULL;
|
||||
|
||||
@ -502,20 +578,20 @@ additional_selector_matches_style (StTheme *a_this,
|
||||
switch (cur_add_sel->type)
|
||||
{
|
||||
case NO_ADD_SELECTOR:
|
||||
return FALSE;
|
||||
return FALSE;
|
||||
case CLASS_ADD_SELECTOR:
|
||||
if (!class_add_sel_matches_style (cur_add_sel, a_node))
|
||||
if (!class_add_sel_matches_style (cur_add_sel, a_node))
|
||||
return FALSE;
|
||||
break;
|
||||
case ID_ADD_SELECTOR:
|
||||
if (!id_add_sel_matches_style (cur_add_sel, a_node))
|
||||
if (!id_add_sel_matches_style (cur_add_sel, a_node))
|
||||
return FALSE;
|
||||
break;
|
||||
case ATTRIBUTE_ADD_SELECTOR:
|
||||
g_warning ("Attribute selectors not supported");
|
||||
return FALSE;
|
||||
g_warning ("Attribute selectors not supported");
|
||||
return FALSE;
|
||||
case PSEUDO_CLASS_ADD_SELECTOR:
|
||||
if (!pseudo_class_add_sel_matches_style (a_this, cur_add_sel, a_node))
|
||||
if (!pseudo_class_add_sel_matches_style (a_this, cur_add_sel, a_node))
|
||||
return FALSE;
|
||||
break;
|
||||
}
|
||||
@ -524,64 +600,21 @@ additional_selector_matches_style (StTheme *a_this,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* This is a workaround for:
|
||||
* - Python encodes '.' as '+' in type names
|
||||
* - libcroco doesn't correctly parse the \+ escape in types
|
||||
* - it's far too ugly to write foo\2b\bar\2bWidget...
|
||||
*
|
||||
* So we consider an element name of foo-bar-Widget to match a type
|
||||
* of foo+bar+Widget
|
||||
*/
|
||||
static gboolean
|
||||
element_name_matches_type_name (const char *element_name,
|
||||
const char *type_name)
|
||||
{
|
||||
const char *p = element_name;
|
||||
const char *q = type_name;
|
||||
|
||||
while (*p || *q)
|
||||
{
|
||||
if (*p == '-')
|
||||
{
|
||||
if (*q != '-' && *q != '+')
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*p != *q)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
element_name_matches_type (const char *element_name,
|
||||
GType element_type)
|
||||
{
|
||||
/* This is not efficient, but the number of selectors with an element_name
|
||||
* is probably fairly small.
|
||||
*/
|
||||
if (element_type == G_TYPE_NONE)
|
||||
{
|
||||
return strcmp (element_name, "stage") == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
if (element_name_matches_type_name (element_name, g_type_name (element_type)))
|
||||
return TRUE;
|
||||
GType match_type = g_type_from_name (element_name);
|
||||
if (match_type == G_TYPE_INVALID)
|
||||
return FALSE;
|
||||
|
||||
if (element_type == G_TYPE_OBJECT)
|
||||
return FALSE;
|
||||
|
||||
element_type = g_type_parent (element_type);
|
||||
}
|
||||
return g_type_is_a (element_type, match_type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -605,10 +638,10 @@ element_name_matches_type (const char *element_name,
|
||||
*/
|
||||
static enum CRStatus
|
||||
sel_matches_style_real (StTheme *a_this,
|
||||
CRSimpleSel *a_sel,
|
||||
StThemeNode *a_node,
|
||||
gboolean *a_result,
|
||||
gboolean a_eval_sel_list_from_end,
|
||||
CRSimpleSel *a_sel,
|
||||
StThemeNode *a_node,
|
||||
gboolean *a_result,
|
||||
gboolean a_eval_sel_list_from_end,
|
||||
gboolean a_recurse)
|
||||
{
|
||||
CRSimpleSel *cur_sel = NULL;
|
||||
@ -634,51 +667,51 @@ sel_matches_style_real (StTheme *a_this,
|
||||
while (cur_sel)
|
||||
{
|
||||
if (((cur_sel->type_mask & TYPE_SELECTOR)
|
||||
&& (cur_sel->name
|
||||
&& cur_sel->name->stryng
|
||||
&& cur_sel->name->stryng->str)
|
||||
&&
|
||||
(element_name_matches_type (cur_sel->name->stryng->str, cur_type)))
|
||||
|| (cur_sel->type_mask & UNIVERSAL_SELECTOR))
|
||||
{
|
||||
/*
|
||||
*this simple selector
|
||||
*matches the current style node
|
||||
*Let's see if the preceding
|
||||
*simple selectors also match
|
||||
*their style node counterpart.
|
||||
*/
|
||||
if (cur_sel->add_sel)
|
||||
{
|
||||
if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node))
|
||||
&& (cur_sel->name
|
||||
&& cur_sel->name->stryng
|
||||
&& cur_sel->name->stryng->str)
|
||||
&&
|
||||
(element_name_matches_type (cur_sel->name->stryng->str, cur_type)))
|
||||
|| (cur_sel->type_mask & UNIVERSAL_SELECTOR))
|
||||
{
|
||||
/*
|
||||
*this simple selector
|
||||
*matches the current style node
|
||||
*Let's see if the preceding
|
||||
*simple selectors also match
|
||||
*their style node counterpart.
|
||||
*/
|
||||
if (cur_sel->add_sel)
|
||||
{
|
||||
if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node))
|
||||
goto walk_a_step_in_expr;
|
||||
else
|
||||
else
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
goto walk_a_step_in_expr;
|
||||
}
|
||||
}
|
||||
if (!(cur_sel->type_mask & TYPE_SELECTOR)
|
||||
&& !(cur_sel->type_mask & UNIVERSAL_SELECTOR))
|
||||
{
|
||||
if (!cur_sel->add_sel)
|
||||
&& !(cur_sel->type_mask & UNIVERSAL_SELECTOR))
|
||||
{
|
||||
if (!cur_sel->add_sel)
|
||||
goto done;
|
||||
if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node))
|
||||
if (additional_selector_matches_style (a_this, cur_sel->add_sel, cur_node))
|
||||
goto walk_a_step_in_expr;
|
||||
else
|
||||
else
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
walk_a_step_in_expr:
|
||||
if (a_recurse == FALSE)
|
||||
{
|
||||
*a_result = TRUE;
|
||||
goto done;
|
||||
}
|
||||
{
|
||||
*a_result = TRUE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
*here, depending on the combinator of cur_sel
|
||||
@ -686,72 +719,72 @@ sel_matches_style_real (StTheme *a_this,
|
||||
*and walk one step in the element tree.
|
||||
*/
|
||||
if (!cur_sel->prev)
|
||||
break;
|
||||
break;
|
||||
|
||||
switch (cur_sel->combinator)
|
||||
{
|
||||
case NO_COMBINATOR:
|
||||
break;
|
||||
{
|
||||
case NO_COMBINATOR:
|
||||
break;
|
||||
|
||||
case COMB_WS: /*descendant selector */
|
||||
{
|
||||
StThemeNode *n = NULL;
|
||||
case COMB_WS: /*descendant selector */
|
||||
{
|
||||
StThemeNode *n = NULL;
|
||||
|
||||
/*
|
||||
*walk the element tree upward looking for a parent
|
||||
*style that matches the preceding selector.
|
||||
*/
|
||||
for (n = st_theme_node_get_parent (a_node); n; n = st_theme_node_get_parent (n))
|
||||
{
|
||||
enum CRStatus status;
|
||||
gboolean matches = FALSE;
|
||||
/*
|
||||
*walk the element tree upward looking for a parent
|
||||
*style that matches the preceding selector.
|
||||
*/
|
||||
for (n = st_theme_node_get_parent (a_node); n; n = st_theme_node_get_parent (n))
|
||||
{
|
||||
enum CRStatus status;
|
||||
gboolean matches = FALSE;
|
||||
|
||||
status = sel_matches_style_real (a_this, cur_sel->prev, n, &matches, FALSE, TRUE);
|
||||
status = sel_matches_style_real (a_this, cur_sel->prev, n, &matches, FALSE, TRUE);
|
||||
|
||||
if (status != CR_OK)
|
||||
goto done;
|
||||
if (status != CR_OK)
|
||||
goto done;
|
||||
|
||||
if (matches)
|
||||
{
|
||||
cur_node = n;
|
||||
cur_type = st_theme_node_get_element_type (cur_node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matches)
|
||||
{
|
||||
cur_node = n;
|
||||
cur_type = st_theme_node_get_element_type (cur_node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!n)
|
||||
{
|
||||
/*
|
||||
*didn't find any ancestor that matches
|
||||
*the previous simple selector.
|
||||
*/
|
||||
goto done;
|
||||
}
|
||||
/*
|
||||
*in this case, the preceding simple sel
|
||||
*will have been interpreted twice, which
|
||||
*is a cpu and mem waste ... I need to find
|
||||
*another way to do this. Anyway, this is
|
||||
*my first attempt to write this function and
|
||||
*I am a bit clueless.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
if (!n)
|
||||
{
|
||||
/*
|
||||
*didn't find any ancestor that matches
|
||||
*the previous simple selector.
|
||||
*/
|
||||
goto done;
|
||||
}
|
||||
/*
|
||||
*in this case, the preceding simple sel
|
||||
*will have been interpreted twice, which
|
||||
*is a cpu and mem waste ... I need to find
|
||||
*another way to do this. Anyway, this is
|
||||
*my first attempt to write this function and
|
||||
*I am a bit clueless.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
case COMB_PLUS:
|
||||
g_warning ("+ combinators are not supported");
|
||||
goto done;
|
||||
case COMB_PLUS:
|
||||
g_warning ("+ combinators are not supported");
|
||||
goto done;
|
||||
|
||||
case COMB_GT:
|
||||
cur_node = st_theme_node_get_parent (cur_node);
|
||||
if (!cur_node)
|
||||
goto done;
|
||||
cur_type = st_theme_node_get_element_type (cur_node);
|
||||
break;
|
||||
case COMB_GT:
|
||||
cur_node = st_theme_node_get_parent (cur_node);
|
||||
if (!cur_node)
|
||||
goto done;
|
||||
cur_type = st_theme_node_get_element_type (cur_node);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto done;
|
||||
}
|
||||
default:
|
||||
goto done;
|
||||
}
|
||||
|
||||
cur_sel = cur_sel->prev;
|
||||
}
|
||||
@ -768,8 +801,8 @@ done:
|
||||
|
||||
static void
|
||||
add_matched_properties (StTheme *a_this,
|
||||
CRStyleSheet *a_nodesheet,
|
||||
StThemeNode *a_node,
|
||||
CRStyleSheet *a_nodesheet,
|
||||
StThemeNode *a_node,
|
||||
GPtrArray *props)
|
||||
{
|
||||
CRStatement *cur_stmt = NULL;
|
||||
@ -797,46 +830,46 @@ add_matched_properties (StTheme *a_this,
|
||||
*which we have to look
|
||||
*/
|
||||
switch (cur_stmt->type)
|
||||
{
|
||||
case RULESET_STMT:
|
||||
if (cur_stmt->kind.ruleset && cur_stmt->kind.ruleset->sel_list)
|
||||
{
|
||||
sel_list = cur_stmt->kind.ruleset->sel_list;
|
||||
}
|
||||
break;
|
||||
{
|
||||
case RULESET_STMT:
|
||||
if (cur_stmt->kind.ruleset && cur_stmt->kind.ruleset->sel_list)
|
||||
{
|
||||
sel_list = cur_stmt->kind.ruleset->sel_list;
|
||||
}
|
||||
break;
|
||||
|
||||
case AT_MEDIA_RULE_STMT:
|
||||
if (cur_stmt->kind.media_rule
|
||||
&& cur_stmt->kind.media_rule->rulesets
|
||||
&& cur_stmt->kind.media_rule->rulesets->kind.ruleset
|
||||
&& cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list)
|
||||
{
|
||||
sel_list = cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list;
|
||||
}
|
||||
break;
|
||||
case AT_MEDIA_RULE_STMT:
|
||||
if (cur_stmt->kind.media_rule
|
||||
&& cur_stmt->kind.media_rule->rulesets
|
||||
&& cur_stmt->kind.media_rule->rulesets->kind.ruleset
|
||||
&& cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list)
|
||||
{
|
||||
sel_list = cur_stmt->kind.media_rule->rulesets->kind.ruleset->sel_list;
|
||||
}
|
||||
break;
|
||||
|
||||
case AT_IMPORT_RULE_STMT:
|
||||
{
|
||||
CRAtImportRule *import_rule = cur_stmt->kind.import_rule;
|
||||
case AT_IMPORT_RULE_STMT:
|
||||
{
|
||||
CRAtImportRule *import_rule = cur_stmt->kind.import_rule;
|
||||
|
||||
if (import_rule->sheet == NULL)
|
||||
{
|
||||
char *filename = NULL;
|
||||
if (import_rule->sheet == NULL)
|
||||
{
|
||||
char *filename = NULL;
|
||||
|
||||
if (import_rule->url->stryng && import_rule->url->stryng->str)
|
||||
filename = _st_theme_resolve_url (a_this,
|
||||
if (import_rule->url->stryng && import_rule->url->stryng->str)
|
||||
filename = _st_theme_resolve_url (a_this,
|
||||
a_nodesheet,
|
||||
import_rule->url->stryng->str);
|
||||
|
||||
if (filename)
|
||||
import_rule->sheet = parse_stylesheet (filename);
|
||||
if (filename)
|
||||
import_rule->sheet = parse_stylesheet (filename);
|
||||
|
||||
if (import_rule->sheet)
|
||||
{
|
||||
insert_stylesheet (a_this, filename, import_rule->sheet);
|
||||
/* refcount of stylesheets starts off at zero, so we don't need to unref! */
|
||||
}
|
||||
else
|
||||
if (import_rule->sheet)
|
||||
{
|
||||
insert_stylesheet (a_this, filename, import_rule->sheet);
|
||||
/* refcount of stylesheets starts off at zero, so we don't need to unref! */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set a marker to avoid repeatedly trying to parse a non-existent or
|
||||
* broken stylesheet
|
||||
@ -910,9 +943,9 @@ get_origin (const CRDeclaration * decl)
|
||||
if (decl->important)
|
||||
{
|
||||
if (origin == ORIGIN_AUTHOR)
|
||||
return ORIGIN_AUTHOR_IMPORTANT;
|
||||
return ORIGIN_AUTHOR_IMPORTANT;
|
||||
else if (origin == ORIGIN_USER)
|
||||
return ORIGIN_USER_IMPORTANT;
|
||||
return ORIGIN_USER_IMPORTANT;
|
||||
}
|
||||
|
||||
return origin;
|
||||
@ -955,7 +988,7 @@ _st_theme_get_matched_properties (StTheme *theme,
|
||||
{
|
||||
sheet = cr_cascade_get_sheet (theme->cascade, origin);
|
||||
if (!sheet)
|
||||
continue;
|
||||
continue;
|
||||
|
||||
add_matched_properties (theme, sheet, node, props);
|
||||
}
|
||||
@ -990,10 +1023,10 @@ _st_theme_resolve_url (StTheme *theme,
|
||||
|
||||
filename = g_filename_from_uri (url, NULL, &error);
|
||||
if (filename == NULL)
|
||||
{
|
||||
g_warning ("%s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
{
|
||||
g_warning ("%s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -8,6 +8,16 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* SECTION:StTheme
|
||||
* @short_description: a set of stylesheets
|
||||
*
|
||||
* #StTheme holds a set of stylesheets. (The "cascade" of the name
|
||||
* Cascading Stylesheets.) A #StTheme can be set to apply to all the actors
|
||||
* in a stage using st_theme_context_set_theme() or applied to a subtree
|
||||
* of actors using st_widget_set_theme().
|
||||
*/
|
||||
|
||||
typedef struct _StThemeClass StThemeClass;
|
||||
|
||||
#define ST_TYPE_THEME (st_theme_get_type ())
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -533,7 +532,7 @@ st_widget_real_style_changed (StWidget *self)
|
||||
double width = st_theme_node_get_border_width (theme_node, side);
|
||||
if (width > 0.5)
|
||||
{
|
||||
border_width = round (width);
|
||||
border_width = (int)(0.5 + width);
|
||||
st_theme_node_get_border_color (theme_node, side, &border_color);
|
||||
break;
|
||||
}
|
||||
@ -544,7 +543,7 @@ st_widget_real_style_changed (StWidget *self)
|
||||
double radius = st_theme_node_get_border_radius (theme_node, corner);
|
||||
if (radius > 0.5)
|
||||
{
|
||||
border_radius = round (radius);
|
||||
border_radius = (int)(0.5 + radius);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -700,6 +699,20 @@ get_root_theme_node (ClutterStage *stage)
|
||||
return st_theme_context_get_root_node (context);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_widget_get_theme_node:
|
||||
* @widget: a #StWidget
|
||||
*
|
||||
* Gets the theme node holding style information for the widget.
|
||||
* The theme node is used to access standard and custom CSS
|
||||
* properties of the widget.
|
||||
*
|
||||
* Return value: (transfer none): the theme node for the widget.
|
||||
* This is owned by the widget. When attributes of the widget
|
||||
* or the environment that affect the styling change (for example
|
||||
* the style_class property of the widget), it will be recreated,
|
||||
* and the ::style-changed signal will be emitted on the widget.
|
||||
*/
|
||||
StThemeNode *
|
||||
st_widget_get_theme_node (StWidget *widget)
|
||||
{
|
||||
|
@ -2,16 +2,23 @@ noinst_SCRIPTS = run-test.sh
|
||||
EXTRA_DIST = run-test.sh.in
|
||||
|
||||
TEST_JS = \
|
||||
testcommon/ui.js \
|
||||
interactive/box-layout.js
|
||||
interactive/borders.js \
|
||||
interactive/box-layout.js \
|
||||
interactive/calendar.js \
|
||||
interactive/css-fonts.js \
|
||||
interactive/entry.js \
|
||||
interactive/inline-style.js \
|
||||
interactive/scrolling.js \
|
||||
interactive/table.js \
|
||||
testcommon/border-image.png \
|
||||
testcommon/ui.js \
|
||||
unit/format.js
|
||||
EXTRA_DIST += $(TEST_JS)
|
||||
|
||||
TEST_MISC = \
|
||||
testcommon/test.css
|
||||
EXTRA_DIST += $(TEST_MISC)
|
||||
|
||||
# We substitute in bindir so it works as an autostart
|
||||
# file when built in a non-system prefix
|
||||
run-test.sh: run-test.sh.in
|
||||
$(AM_V_GEN) sed \
|
||||
-e "s|@GJS_JS_DIR[@]|$(GJS_JS_DIR)|" \
|
||||
@ -19,3 +26,5 @@ run-test.sh: run-test.sh.in
|
||||
-e "s|@MUTTER_LIB_DIR[@]|$(MUTTER_LIB_DIR)|" \
|
||||
-e "s|@srcdir[@]|$(srcdir)|" \
|
||||
$< > $@ && chmod a+x $@
|
||||
|
||||
CLEANFILES = run-test.sh
|
||||
|
29
tests/interactive/calendar.js
Normal file
29
tests/interactive/calendar.js
Normal file
@ -0,0 +1,29 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Calendar = imports.ui.calendar;
|
||||
const UI = imports.testcommon.ui;
|
||||
|
||||
UI.init();
|
||||
let stage = Clutter.Stage.get_default();
|
||||
stage.width = stage.height = 400;
|
||||
stage.show();
|
||||
|
||||
let vbox = new St.BoxLayout({ vertical: true,
|
||||
width: stage.width,
|
||||
height: stage.height,
|
||||
style: 'padding: 10px; spacing: 10px; font: 15px sans-serif;' });
|
||||
stage.add_actor(vbox);
|
||||
|
||||
let calendar = new Calendar.Calendar();
|
||||
vbox.add(calendar.actor,
|
||||
{ expand: true,
|
||||
x_fill: false, x_align: St.Align.MIDDLE,
|
||||
y_fill: false, y_align: St.Align.START });
|
||||
|
||||
stage.show();
|
||||
Clutter.main();
|
||||
stage.destroy();
|
32
tests/interactive/entry.js
Normal file
32
tests/interactive/entry.js
Normal file
@ -0,0 +1,32 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Calendar = imports.ui.calendar;
|
||||
const UI = imports.testcommon.ui;
|
||||
|
||||
Gtk.init(null, null);
|
||||
|
||||
UI.init();
|
||||
let stage = Clutter.Stage.get_default();
|
||||
stage.width = stage.height = 400;
|
||||
stage.show();
|
||||
|
||||
let vbox = new St.BoxLayout({ vertical: true,
|
||||
width: stage.width,
|
||||
height: stage.height,
|
||||
style: 'padding: 10px; spacing: 10px; font: 15px sans-serif;' });
|
||||
stage.add_actor(vbox);
|
||||
|
||||
let entry = new St.Entry({ style: 'border: 1px solid black;' });
|
||||
vbox.add(entry,
|
||||
{ expand: true,
|
||||
y_fill: false, y_align: St.Align.MIDDLE });
|
||||
entry.grab_key_focus();
|
||||
|
||||
stage.show();
|
||||
Clutter.main();
|
||||
stage.destroy();
|
29
tests/unit/format.js
Normal file
29
tests/unit/format.js
Normal file
@ -0,0 +1,29 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
* Test cases for the Format module
|
||||
*/
|
||||
|
||||
const JsUnit = imports.jsUnit;
|
||||
const assertEquals = JsUnit.assertEquals;
|
||||
const assertRaises = JsUnit.assertRaises;
|
||||
|
||||
// We can't depend on environment.js to set up the String.prototype.format,
|
||||
// because the tests run in one JS context, and the imports run in the GJS
|
||||
// "load context" which has its own copy of the String class
|
||||
const Format = imports.misc.format;
|
||||
String.prototype.format = Format.format;
|
||||
|
||||
// Test common usage and %% handling
|
||||
assertEquals("foo", "%s".format('foo'));
|
||||
assertEquals("%s", "%%s".format('foo'));
|
||||
assertEquals("%%s", "%%%%s".format('foo'));
|
||||
assertEquals("foo 5", "%s %d".format('foo', 5));
|
||||
assertEquals("8", "%d".format(8));
|
||||
assertEquals("2.58 6.96", "%f %.2f".format(2.58, 6.958));
|
||||
|
||||
// Precision is only allowed for %f
|
||||
assertRaises(function() { "%.2d".format(5.21) });
|
||||
|
||||
// Wrong conversion character ' '
|
||||
assertRaises( function() { "%s is 50% done".format('foo') });
|
@ -74,7 +74,7 @@ dpkg_is_installed() {
|
||||
return 1
|
||||
}
|
||||
|
||||
if test x$system = xUbuntu -o x$system = xDebian ; then
|
||||
if test x$system = xUbuntu -o x$system = xDebian -o x$system = xLinuxMint ; then
|
||||
reqd=""
|
||||
for pkg in \
|
||||
build-essential curl \
|
||||
@ -82,7 +82,7 @@ if test x$system = xUbuntu -o x$system = xDebian ; then
|
||||
libdbus-glib-1-dev libgconf2-dev libgtk2.0-dev libffi-dev \
|
||||
libgnome-menu-dev libgnome-desktop-dev librsvg2-dev libwnck-dev libgl1-mesa-dev \
|
||||
libreadline5-dev mesa-common-dev mesa-utils python-dev python-gconf python-gobject \
|
||||
xulrunner-dev xserver-xephyr \
|
||||
xulrunner-dev xserver-xephyr libcroco3-dev \
|
||||
libgstreamer0.10-dev gstreamer0.10-plugins-base gstreamer0.10-plugins-good \
|
||||
; do
|
||||
if ! dpkg_is_installed $pkg; then
|
||||
@ -104,7 +104,7 @@ if test x$system = xFedora ; then
|
||||
libtool pkgconfig \
|
||||
dbus-glib-devel GConf2-devel gnome-menus-devel gnome-python2-gconf gtk2-devel libffi-devel \
|
||||
gnome-desktop-devel librsvg2-devel libwnck-devel mesa-libGL-devel python-devel pygobject2 \
|
||||
readline-devel xulrunner-devel libXdamage-devel \
|
||||
readline-devel xulrunner-devel libXdamage-devel libcroco-devel \
|
||||
gstreamer-devel gstreamer-plugins-base gstreamer-plugins-good \
|
||||
glx-utils xorg-x11-apps xorg-x11-server-Xephyr xterm zenity \
|
||||
; do
|
||||
@ -124,7 +124,7 @@ if test x$system = xSUSE ; then
|
||||
bison flex gnome-doc-utils-devel \
|
||||
gconf2-devel libffi-devel gnome-desktop-devel librsvg-devel libwnck-devel \
|
||||
xorg-x11-proto-devel readline-devel mozilla-xulrunner190-devel \
|
||||
xorg-x11-devel xterm xorg-x11 xorg-x11-server-extra \
|
||||
libcroco-devel xorg-x11-devel xterm xorg-x11 xorg-x11-server-extra \
|
||||
; do
|
||||
if ! rpm -q $pkg > /dev/null 2>&1; then
|
||||
reqd="$pkg $reqd"
|
||||
@ -145,6 +145,7 @@ if test x$system = xMandrivaLinux ; then
|
||||
libGConf2-devel ffi5-devel libgnomeui2-devel librsvg2-devel \
|
||||
libwnck-1-devel GL-devel readline-devel libxulrunner-devel \
|
||||
libxdamage-devel mesa-demos x11-server-xephyr x11-apps xterm zenity \
|
||||
libcroco0.6-devel \
|
||||
; do
|
||||
if ! rpm -q --whatprovides $pkg > /dev/null 2>&1; then
|
||||
reqd="$pkg $reqd"
|
||||
|
@ -35,13 +35,7 @@
|
||||
<branch repo="git.clutter-project.org" module="clutter" revision="clutter-1.0"/>
|
||||
<dependencies>
|
||||
<dep package="gobject-introspection"/>
|
||||
</dependencies>
|
||||
</autotools>
|
||||
|
||||
<autotools id="clutter-imcontext">
|
||||
<branch repo="git.moblin.org" module="clutter-imcontext"/>
|
||||
<dependencies>
|
||||
<dep package="clutter"/>
|
||||
<dep package="gir-repository"/>
|
||||
</dependencies>
|
||||
</autotools>
|
||||
|
||||
|
Reference in New Issue
Block a user