diff --git a/data/Makefile.am b/data/Makefile.am index 06827d0b3..912ad3dbe 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -16,8 +16,6 @@ imagesdir = $(pkgdatadir)/images dist_images_DATA = \ add-workspace.svg \ app-well-glow.png \ - back.svg \ - close.svg \ close-black.svg \ info.svg \ magnifier.svg \ @@ -26,11 +24,14 @@ dist_images_DATA = \ themedir = $(pkgdatadir)/theme dist_theme_DATA = \ theme/gnome-shell.css \ + theme/close.svg \ theme/scroll-button-down.png \ theme/scroll-button-down-hover.png \ theme/scroll-button-up.png \ theme/scroll-button-up-hover.png \ - theme/scroll-vhandle.png + theme/scroll-vhandle.png \ + theme/section-back.svg \ + theme/section-more.svg schemadir = @GCONF_SCHEMA_FILE_DIR@ schema_DATA = gnome-shell.schemas diff --git a/data/close.svg b/data/theme/close.svg similarity index 100% rename from data/close.svg rename to data/theme/close.svg diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index dfe8dc7cf..b04cb79a7 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -19,7 +19,6 @@ StScrollBar { - background-color: #354761; padding: 0px; } @@ -61,6 +60,14 @@ StScrollBar StButton#vhandle:hover border-image: url("scroll-vhandle.png") 5; } +StTooltip { + border: 1px solid rgba(79,111,173,0.9); + border-radius: 5px; + padding: 4px; + background-color: rgba(79,111,173,0.9); + color: #ffffff; +} + /* Panel */ .panel-button { @@ -74,6 +81,154 @@ StScrollBar StButton#vhandle:hover background-color: #314a6c; } +/* Dash */ + +#dash { + color: #5f5f5f; + background-color: rgba(0,0,0,0.75); + padding: 0px 14px; +} + +#dashSections { + spacing: 12px; +} + +.dash-search-section-header { + padding: 6px 0px; + spacing: 4px; + font-size: 12px; + color: #bbbbbb; +} + +.dash-search-section-title, dash-search-section-count { + font-weight: bold; +} + +#searchEntry { + padding: 4px; + border-bottom: 1px solid #262626; +} + +#searchEntry:active { + background-color: #c4c4c4; +} + +.dash-section { + spacing: 8px; +} + +.section-header { + border: 1px solid #262626; + -shell-gradient-top: #161616; + -shell-gradient-bottom: #000000; + font-weight: bold; + font-size: 12px; +} + +.section-header-inner { + border: 1px solid #000000; + padding: 0px 4px; + spacing: 4px; +} + +.section-text-content { + padding: 4px 0px; +} + +.section-header-back { + padding: 0px 4px 0px 0px; + border-right: 1px solid #262626; +} + +.section-header-back-image { + background-image: url("section-back.svg"); + width: 12px; + height: 16px; +} + +.section-count { +} + +.dash-section-content { + font-size: 14px; + color: #ffffff; + spacing: 8px; +} + +.more-link { +} + +.more-link-expander { + background-image: url("section-more.svg"); + width: 9px; + height: 9px; +} + +.dash-pane { + background-color: rgba(0,0,0,0.95); + border: 1px solid #262626; + padding: 4px; + spacing: 4px; +} + +.dash-pane-close { + background-image: url("close.svg"); + width: 16px; + height: 16px; +} + +/* GenericDisplay */ + +.generic-display-container { + spacing: 4px; +} + +.generic-display-item { + height: 50px; + border-radius: 4px; + color: #ffffff; + font-size: 14px; + spacing: 4px; +} + +.generic-display-item:selected { + background-color: rgba(79,111,173,0.66); +} + +.generic-display-item-text { + spacing: 4px; +} + +.generic-display-item-description { + font-size: 12px; + color: #bababa; +} + +.generic-display-details { + font-size: 14px; + color: #ffffff; +} + +.generic-display-details-name { + font-weight: bold; +} + +/* AppIcon */ + +.app-icon-label { + font-size: 12px; +} + +/* Places */ + +.places-actions { + spacing: 4px; +} + +#placesDevices { + padding-top: 4px; +} + /* LookingGlass */ #LookingGlassDialog @@ -203,3 +358,10 @@ StScrollBar StButton#vhandle:hover width: 1px; background: rgba(255,255,255,0.33); } + +/* Status Menu */ +#StatusMenu { + spacing: 4px; + font: 16px sans-serif; + color: white; +} diff --git a/data/back.svg b/data/theme/section-back.svg similarity index 100% rename from data/back.svg rename to data/theme/section-back.svg diff --git a/data/theme/section-more.svg b/data/theme/section-more.svg new file mode 100755 index 000000000..74f579d63 --- /dev/null +++ b/data/theme/section-more.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/js/misc/docInfo.js b/js/misc/docInfo.js index 021dce412..8123e281b 100644 --- a/js/misc/docInfo.js +++ b/js/misc/docInfo.js @@ -36,8 +36,8 @@ DocInfo.prototype = { // shorter in terms of lines of code, we are not doing so // because that would duplicate the work of retrieving the // mime type. - - let appInfo = Gio.app_info_get_default_for_type(this.mimeType, true); + let needsUri = Gio.file_new_for_uri(this.uri).get_path() == null; + let appInfo = Gio.app_info_get_default_for_type(this.mimeType, needsUri); if (appInfo != null) { appInfo.launch_uris([this.uri], Main.createAppLaunchContext()); diff --git a/js/ui/Makefile.am b/js/ui/Makefile.am index c5824a4ac..f303aa685 100644 --- a/js/ui/Makefile.am +++ b/js/ui/Makefile.am @@ -20,10 +20,11 @@ dist_jsui_DATA = \ messageTray.js \ overview.js \ panel.js \ - places.js \ + placeDisplay.js \ runDialog.js \ shellDBus.js \ sidebar.js \ + statusMenu.js \ tweener.js \ widget.js \ widgetBox.js \ diff --git a/js/ui/altTab.js b/js/ui/altTab.js index da4c99641..dd7726631 100644 --- a/js/ui/altTab.js +++ b/js/ui/altTab.js @@ -724,7 +724,19 @@ ThumbnailList.prototype = { _init : function(windows) { SwitcherList.prototype._init.call(this); + let activeWorkspace = global.screen.get_active_workspace(); + + // We fake the value of "separatorAdded" when the app has no window + // on the current workspace, to avoid displaying a useless separator in + // that case. + let separatorAdded = windows.length == 0 || windows[0].get_workspace() != activeWorkspace; + for (let i = 0; i < windows.length; i++) { + if (!separatorAdded && windows[i].get_workspace() != activeWorkspace) { + this.addSeparator(); + separatorAdded = true; + } + let mutterWindow = windows[i].get_compositor_private(); let windowTexture = mutterWindow.get_texture (); let [width, height] = windowTexture.get_size(); @@ -739,11 +751,14 @@ ThumbnailList.prototype = { height: height * scale }); box.add_actor(clone); - let name = new St.Label({ text: windows[i].get_title() }); - // St.Label doesn't support text-align so use a Bin - let bin = new St.Bin({ x_align: St.Align.MIDDLE }); - bin.add_actor(name); - box.add_actor(bin); + let title = windows[i].get_title(); + if (title) { + let name = new St.Label({ text: title }); + // St.Label doesn't support text-align so use a Bin + let bin = new St.Bin({ x_align: St.Align.MIDDLE }); + bin.add_actor(name); + box.add_actor(bin); + } this.addItem(box); } diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index db2b507e3..c88191016 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -166,15 +166,15 @@ Signals.addSignalMethods(MenuItem.prototype); * showPrefs - a boolean indicating if this AppDisplay should contain preference * applets, rather than applications */ -function AppDisplay(showPrefs) { - this._init(showPrefs); +function AppDisplay(showPrefs, flags) { + this._init(showPrefs, flags); } AppDisplay.prototype = { __proto__: GenericDisplay.GenericDisplay.prototype, - _init : function(showPrefs) { - GenericDisplay.GenericDisplay.prototype._init.call(this); + _init : function(showPrefs, flags) { + GenericDisplay.GenericDisplay.prototype._init.call(this, flags); this._showPrefs = showPrefs; @@ -190,156 +190,10 @@ AppDisplay.prototype = { this._appsStale = true; this._redisplay(GenericDisplay.RedisplayFlags.NONE); })); - - this._focusInMenus = true; - this._activeMenuIndex = -1; - this._activeMenu = null; - this._activeMenuApps = null; - this._menuDisplay = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL, - spacing: MENU_SPACING - }); - - this.connect('expanded', Lang.bind(this, function (self) { - this._filterReset(); - })); - this._filterReset(); - }, - - moveRight: function() { - if (this._expanded && this._focusInMenu) { - this._focusInMenu = false; - this._activeMenu.setState(MENU_ENTERED); - this.selectFirstItem(); - } - }, - - moveLeft: function() { - if (this._expanded && !this._focusInMenu) { - this._activeMenu.setState(MENU_SELECTED); - this.unsetSelected(); - this._focusInMenu = true; - } - }, - - // Override genericDisplay.js - getNavigationArea: function() { - return this._menuDisplay; - }, - - selectUp: function() { - if (!(this._expanded && this._focusInMenu)) - return GenericDisplay.GenericDisplay.prototype.selectUp.call(this); - this._selectMenuIndex(this._activeMenuIndex - 1); - return true; - }, - - selectDown: function() { - if (!(this._expanded && this._focusInMenu)) - return GenericDisplay.GenericDisplay.prototype.selectDown.call(this); - this._selectMenuIndex(this._activeMenuIndex+1); - 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() { - // We always have a filter now since a menu must be selected - return true; - }, - - _filterReset: function() { - GenericDisplay.GenericDisplay.prototype._filterReset.call(this); - this._selectMenuIndex(0); }, //// Private //// - _emitStateChange: function() { - this.emit('state-changed'); - }, - - _selectMenuIndex: function(index) { - if (index < 0 || index >= this._menus.length) - return; - this._menuDisplays[index].setState(MENU_SELECTED); - }, - - _getMostUsed: function() { - let context = ""; - let usage = Shell.AppUsage.get_default(); - return usage.get_most_used(context, 30); - }, - - _addMenuItem: function(name, id, index) { - let display = new MenuItem(name, id); - this._menuDisplays.push(display); - display.connect('state-changed', Lang.bind(this, function (display) { - let activated = display.getState() != MENU_UNSELECTED; - if (!activated && display == this._activeMenu) { - this._activeMenuIndex = -1; - this._activeMenu = null; - } else if (activated) { - if (display != this._activeMenu && this._activeMenu != null) - this._activeMenu.setState(MENU_UNSELECTED); - this._activeMenuIndex = index; - this._activeMenu = display; - if (id == null) { - this._activeMenuApps = this._getMostUsed(); - } else { - this._activeMenuApps = this._appSystem.get_applications_for_menu(id); - } - } - this._redisplay(GenericDisplay.RedisplayFlags.FULL); - })); - this._menuDisplay.append(display.actor, 0); - }, - - _redisplayMenus: function() { - this._menuDisplay.remove_all(); - this._addMenuItem(_("Frequent"), null, 'gtk-select-all'); - // Adding an empty box here results in double spacing between - // "Frequent" and the other items. - let separator_actor = new Big.Box(); - this._menuDisplay.append(separator_actor, 0); - for (let i = 0; i < this._menus.length; i++) { - let menu = this._menus[i]; - this._addMenuItem(menu.name, menu.id, i+1); - } - }, - _addApp: function(appInfo) { let appId = appInfo.get_id(); this._allItems[appId] = appInfo; @@ -363,33 +217,33 @@ AppDisplay.prototype = { } } else { // Loop over the toplevel menu items, load the set of desktop file ids - // associated with each one, skipping empty menus + // associated with each one. let allMenus = this._appSystem.get_menus(); - this._menus = []; for (let i = 0; i < allMenus.length; i++) { let menu = allMenus[i]; let menuApps = this._appSystem.get_applications_for_menu(menu.id); - let hasVisibleApps = menuApps.some(function (app) { return !app.get_is_nodisplay(); }); - if (!hasVisibleApps) { - continue; - } - this._menus.push(menu); + for (let j = 0; j < menuApps.length; j++) { let app = menuApps[j]; this._addApp(app); } } - this._redisplayMenus(); } this._appsStale = false; return false; }, - // Stub this out; the app display always has a category selected _setDefaultList : function() { - this._matchedItems = {}; + this._matchedItems = this._allItems; this._matchedItemKeys = []; + for (let itemId in this._matchedItems) { + let app = this._allItems[itemId]; + if (app.get_is_nodisplay()) + continue; + this._matchedItemKeys.push(itemId); + } + this._matchedItemKeys.sort(Lang.bind(this, this._compareItems)); }, // Compares items associated with the item ids based on the alphabetical order @@ -409,25 +263,7 @@ AppDisplay.prototype = { // Don't show nodisplay items here if (itemInfo.get_is_nodisplay()) return false; - // Search takes precedence; not typically useful to search within a - // menu - if (this._activeMenu == null || search != "") - return this._isInfoMatchingSearch(itemInfo, search); - else - return this._isInfoMatchingMenu(itemInfo); - }, - _isInfoMatchingMenu: function(itemInfo) { - let id = itemInfo.get_id(); - for (let i = 0; i < this._activeMenuApps.length; i++) { - let activeApp = this._activeMenuApps[i]; - if (activeApp.get_id() == id) - return true; - } - return false; - }, - - _isInfoMatchingSearch: function(itemInfo, search) { if (search == null || search == '') return true; @@ -455,13 +291,6 @@ AppDisplay.prototype = { return true; } - 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; }, diff --git a/js/ui/appIcon.js b/js/ui/appIcon.js index b2cc8af68..571e05097 100644 --- a/js/ui/appIcon.js +++ b/js/ui/appIcon.js @@ -8,6 +8,7 @@ const Mainloop = imports.mainloop; const Pango = imports.gi.Pango; const Shell = imports.gi.Shell; const Signals = imports.signals; +const St = imports.gi.St; const Gettext = imports.gettext.domain('gnome-shell'); const _ = Gettext.gettext; @@ -99,11 +100,9 @@ AppIcon.prototype = { nameBox.connect('allocate', Lang.bind(this, this._nameBoxAllocate)); this._nameBox = nameBox; - this._name = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR, - font_name: "Sans 12px", - line_alignment: Pango.Alignment.CENTER, - ellipsize: Pango.EllipsizeMode.END, - text: this.app.get_name() }); + this._name = new St.Label({ style_class: "app-icon-label", + text: this.app.get_name() }); + this._name.clutter_text.line_alignment = Pango.Alignment.CENTER; nameBox.add_actor(this._name); if (this._showGlow) { this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL }); diff --git a/js/ui/dash.js b/js/ui/dash.js index 2f367753a..3398fce9e 100644 --- a/js/ui/dash.js +++ b/js/ui/dash.js @@ -8,22 +8,19 @@ const Pango = imports.gi.Pango; const Shell = imports.gi.Shell; const Signals = imports.signals; const Lang = imports.lang; +const St = imports.gi.St; const Gettext = imports.gettext.domain('gnome-shell'); const _ = Gettext.gettext; const AppDisplay = imports.ui.appDisplay; const DocDisplay = imports.ui.docDisplay; -const Places = imports.ui.places; +const PlaceDisplay = imports.ui.placeDisplay; const GenericDisplay = imports.ui.genericDisplay; const Button = imports.ui.button; const Main = imports.ui.main; const DEFAULT_PADDING = 4; const DEFAULT_SPACING = 4; -const DASH_SECTION_PADDING = 6; -const DASH_SECTION_SPACING = 40; -const DASH_CORNER_RADIUS = 5; -const DASH_PADDING_SIDE = 14; const BACKGROUND_COLOR = new Clutter.Color(); BACKGROUND_COLOR.from_pixel(0x000000c0); @@ -43,42 +40,22 @@ SEARCH_TEXT_COLOR.from_pixel(0x333333ff); const SEARCH_CURSOR_COLOR = BRIGHT_TEXT_COLOR; const HIGHLIGHTED_SEARCH_CURSOR_COLOR = SEARCH_TEXT_COLOR; -const HIGHLIGHTED_SEARCH_BACKGROUND_COLOR = new Clutter.Color(); -HIGHLIGHTED_SEARCH_BACKGROUND_COLOR.from_pixel(0xc4c4c4ff); - const SEARCH_BORDER_BOTTOM_COLOR = new Clutter.Color(); SEARCH_BORDER_BOTTOM_COLOR.from_pixel(0x191919ff); -const SECTION_BORDER_COLOR = new Clutter.Color(); -SECTION_BORDER_COLOR.from_pixel(0x262626ff); -const SECTION_BORDER = 1; -const SECTION_INNER_BORDER_COLOR = new Clutter.Color(); -SECTION_INNER_BORDER_COLOR.from_pixel(0x000000ff); -const SECTION_BACKGROUND_TOP_COLOR = new Clutter.Color(); -SECTION_BACKGROUND_TOP_COLOR.from_pixel(0x161616ff); -const SECTION_BACKGROUND_BOTTOM_COLOR = new Clutter.Color(); -SECTION_BACKGROUND_BOTTOM_COLOR.from_pixel(0x000000ff); -const SECTION_INNER_SPACING = 8; - const BROWSE_ACTIVATED_BG = new Clutter.Color(); BROWSE_ACTIVATED_BG.from_pixel(0x303030f0); -const PANE_BORDER_COLOR = new Clutter.Color(); -PANE_BORDER_COLOR.from_pixel(0x101d3cfa); -const PANE_BORDER_WIDTH = 2; - -const PANE_BACKGROUND_COLOR = new Clutter.Color(); -PANE_BACKGROUND_COLOR.from_pixel(0x000000f4); - const APPS = "apps"; const PREFS = "prefs"; const DOCS = "docs"; +const PLACES = "places"; /* * Returns the index in an array of a given length that is obtained * if the provided index is incremented by an increment and the array * is wrapped in if necessary. - * + * * index: prior index, expects 0 <= index < length * increment: the change in index, expects abs(increment) <= length * length: the length of the array @@ -87,14 +64,15 @@ function _getIndexWrapped(index, increment, length) { return (index + increment + length) % length; } -function _createDisplay(displayType) { +function _createDisplay(displayType, flags) { if (displayType == APPS) - return new AppDisplay.AppDisplay(); + return new AppDisplay.AppDisplay(false, flags); else if (displayType == PREFS) - return new AppDisplay.AppDisplay(true); + return new AppDisplay.AppDisplay(true, flags); else if (displayType == DOCS) - return new DocDisplay.DocDisplay(); - + return new DocDisplay.DocDisplay(flags); + else if (displayType == PLACES) + return new PlaceDisplay.PlaceDisplay(flags); return null; } @@ -106,36 +84,27 @@ Pane.prototype = { _init: function () { this._open = false; - this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL, - background_color: PANE_BACKGROUND_COLOR, - border: PANE_BORDER_WIDTH, - border_color: PANE_BORDER_COLOR, - padding: DEFAULT_PADDING, - reactive: true }); + this.actor = new St.BoxLayout({ style_class: "dash-pane", + vertical: true, + reactive: true }); this.actor.connect('button-press-event', Lang.bind(this, function (a, e) { // Eat button press events so they don't go through and close the pane return true; })); - let chromeTop = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, - spacing: 6 }); + let chromeTop = new St.BoxLayout(); - let closeIconUri = "file://" + global.imagedir + "close.svg"; - let closeIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER, - closeIconUri, - 16, - 16); - closeIcon.reactive = true; - closeIcon.connect('button-press-event', Lang.bind(this, function (b, e) { + let closeIcon = new St.Button({ style_class: "dash-pane-close" }); + closeIcon.connect('clicked', Lang.bind(this, function (b, e) { this.close(); - return true; })); - chromeTop.append(closeIcon, Big.BoxPackFlags.END); - this.actor.append(chromeTop, Big.BoxPackFlags.NONE); + let dummy = new St.Bin(); + chromeTop.add(dummy, { expand: true }); + chromeTop.add(closeIcon, { x_align: St.Align.END }); + this.actor.add(chromeTop); - this.content = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL, - spacing: DEFAULT_PADDING }); - this.actor.append(this.content, Big.BoxPackFlags.EXPAND); + this.content = new St.BoxLayout({ vertical: true }); + this.actor.add(this.content, { expand: true }); // Hidden by default this.actor.hide(); @@ -173,32 +142,20 @@ Pane.prototype = { } Signals.addSignalMethods(Pane.prototype); -function ResultArea(displayType, enableNavigation) { - this._init(displayType, enableNavigation); +function ResultArea(displayType, flags) { + this._init(displayType, flags); } ResultArea.prototype = { - _init : function(displayType, enableNavigation) { + _init : function(displayType, flags) { this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL }); this.resultsContainer = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, spacing: DEFAULT_PADDING }); this.actor.append(this.resultsContainer, Big.BoxPackFlags.EXPAND); - this.navContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL }); - this.resultsContainer.append(this.navContainer, Big.BoxPackFlags.NONE); - - this.display = _createDisplay(displayType); - - this.navArea = this.display.getNavigationArea(); - if (enableNavigation && this.navArea) - this.navContainer.append(this.navArea, Big.BoxPackFlags.EXPAND); + this.display = _createDisplay(displayType, flags); this.resultsContainer.append(this.display.actor, Big.BoxPackFlags.EXPAND); - - this.controlBox = new Big.Box({ x_align: Big.BoxAlignment.CENTER }); - this.controlBox.append(this.display.displayControl, Big.BoxPackFlags.NONE); - this.actor.append(this.controlBox, Big.BoxPackFlags.NONE); - this.display.load(); } } @@ -223,7 +180,7 @@ function createPaneForDetails(dash, display) { if (index >= 0) { detailPane.destroyContent(); let details = display.createDetailsForIndex(index); - detailPane.content.append(details, Big.BoxPackFlags.EXPAND); + detailPane.content.add(details, { expand: true }); detailPane.open(); } else { detailPane.close(); @@ -246,12 +203,12 @@ ResultPane.prototype = { // Create a display of displayType and pack it into this pane's // content area. Return the display. - packResults: function(displayType, enableNavigation) { - let resultArea = new ResultArea(displayType, enableNavigation); + packResults: function(displayType) { + let resultArea = new ResultArea(displayType); createPaneForDetails(this._dash, resultArea.display); - this.content.append(resultArea.actor, Big.BoxPackFlags.EXPAND); + this.content.add(resultArea.actor, { expand: true }); this.connect('open-state-changed', Lang.bind(this, function(pane, isOpen) { resultArea.display.resetState(); })); @@ -265,14 +222,11 @@ function SearchEntry() { SearchEntry.prototype = { _init : function() { - this.actor = new Big.Box({ padding: DEFAULT_PADDING, - border_bottom: SECTION_BORDER, - border_color: SEARCH_BORDER_BOTTOM_COLOR, - corner_radius: DASH_CORNER_RADIUS, - reactive: true }); + this.actor = new St.BoxLayout({ name: "searchEntry", + reactive: true }); let box = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, y_align: Big.BoxAlignment.CENTER }); - this.actor.append(box, Big.BoxPackFlags.EXPAND); + this.actor.add(box, { expand: true }); this.actor.connect('button-press-event', Lang.bind(this, function () { this._resetTextState(true); return false; @@ -332,7 +286,7 @@ SearchEntry.prototype = { else this.entry.text = ''; - // Return true to stop the signal emission, so that this.actor doesn't get + // Return true to stop the signal emission, so that this.actor doesn't get // the button-press-event and re-highlight itself. return true; })); @@ -355,18 +309,18 @@ SearchEntry.prototype = { _resetTextState: function (searchEntryClicked) { let text = this.getText(); this._iconBox.remove_all(); - // We highlight the search box if the user starts typing in it + // We highlight the search box if the user starts typing in it // or just clicks in it to indicate that the search is active. if (text != '' || searchEntryClicked) { if (!searchEntryClicked) this._defaultText.hide(); this._iconBox.append(this._closeIcon, Big.BoxPackFlags.NONE); - this.actor.background_color = HIGHLIGHTED_SEARCH_BACKGROUND_COLOR; + this.actor.set_style_pseudo_class('active'); this.entry.cursor_color = HIGHLIGHTED_SEARCH_CURSOR_COLOR; } else { this._defaultText.show(); this._iconBox.append(this._magnifierIcon, Big.BoxPackFlags.NONE); - this.actor.background_color = BACKGROUND_COLOR; + this.actor.set_style_pseudo_class(null); this.entry.cursor_color = SEARCH_CURSOR_COLOR; } }, @@ -385,20 +339,12 @@ function MoreLink() { MoreLink.prototype = { _init : function () { - this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, - padding_right: DEFAULT_PADDING, - padding_left: DEFAULT_PADDING, - reactive: true, - x_align: Big.BoxAlignment.CENTER, - y_align: Big.BoxAlignment.CENTER, - border_left: SECTION_BORDER, - border_color: SECTION_BORDER_COLOR }); + this.actor = new St.BoxLayout({ style_class: "more-link", + reactive: true }); this.pane = null; - let text = new Clutter.Text({ font_name: "Sans 12px", - color: BRIGHT_TEXT_COLOR, - text: _("More") }); - this.actor.append(text, Big.BoxPackFlags.NONE); + let expander = new St.Bin({ style_class: "more-link-expander" }); + this.actor.add(expander, { expand: true, y_fill: false }); this.actor.connect('button-press-event', Lang.bind(this, function (b, e) { if (this.pane == null) { @@ -425,21 +371,9 @@ function BackLink() { BackLink.prototype = { _init : function () { - this.actor = new Shell.ButtonBox({ orientation: Big.BoxOrientation.HORIZONTAL, - padding_right: DEFAULT_PADDING, - padding_left: DEFAULT_PADDING, - reactive: true, - x_align: Big.BoxAlignment.CENTER, - y_align: Big.BoxAlignment.CENTER, - border_right: SECTION_BORDER, - border_color: SECTION_BORDER_COLOR }); - - let backIconUri = "file://" + global.imagedir + "back.svg"; - let backIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER, - backIconUri, - 12, - 16); - this.actor.append(backIcon, Big.BoxPackFlags.NONE); + this.actor = new St.Button({ style_class: "section-header-back", + reactive: true }); + this.actor.set_child(new St.Bin({ style_class: "section-header-back-image" })); } } @@ -449,52 +383,72 @@ function SectionHeader(title, suppressBrowse) { SectionHeader.prototype = { _init : function (title, suppressBrowse) { - this.actor = new Big.Box({ border: SECTION_BORDER, - border_color: SECTION_BORDER_COLOR }); - this._innerBox = new Big.Box({ border: SECTION_BORDER, - border_color: SECTION_INNER_BORDER_COLOR, - padding_left: DEFAULT_PADDING, - padding_right: DEFAULT_PADDING, - orientation: Big.BoxOrientation.HORIZONTAL, - spacing: DEFAULT_SPACING }); - this.actor.append(this._innerBox, Big.BoxPackFlags.EXPAND); - let backgroundGradient = Shell.create_vertical_gradient(SECTION_BACKGROUND_TOP_COLOR, - SECTION_BACKGROUND_BOTTOM_COLOR); - this._innerBox.add_actor(backgroundGradient); - this._innerBox.connect('notify::allocation', Lang.bind(this, function (actor) { - let [width, height] = actor.get_size(); - backgroundGradient.set_size(width, height); + this.actor = new St.Bin({ style_class: "section-header", + x_align: St.Align.START, + x_fill: true, + y_fill: true }); + this._innerBox = new St.BoxLayout({ style_class: "section-header-inner" }); + this.actor.set_child(this._innerBox); + + this._backgroundGradient = null; + this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged)); + this.actor.connect('notify::allocation', Lang.bind(this, function (actor) { + if (!this._backgroundGradient) + return; + this._onStyleChanged(); })); this.backLink = new BackLink(); - this._innerBox.append(this.backLink.actor, Big.BoxPackFlags.NONE); + this._innerBox.add(this.backLink.actor); this.backLink.actor.hide(); - - this.backLink.actor.connect('activate', Lang.bind(this, function (actor) { - this.emit('back-link-activated'); + this.backLink.actor.connect('clicked', Lang.bind(this, function (actor) { + this.emit('back-link-activated'); })); - let textBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, - padding_top: DEFAULT_PADDING, - padding_bottom: DEFAULT_PADDING }); - this.text = new Clutter.Text({ color: TEXT_COLOR, - font_name: "Sans Bold 12px", - text: title }); - textBox.append(this.text, Big.BoxPackFlags.NONE); + let textBox = new St.BoxLayout({ style_class: "section-text-content" }); + this.text = new St.Label({ style_class: "section-title", + text: title }); + textBox.add(this.text, { x_align: St.Align.START }); - this.countText = new Clutter.Text({ color: TEXT_COLOR, - font_name: 'Sans Bold 14px' }); - textBox.append(this.countText, Big.BoxPackFlags.END); + this.countText = new St.Label({ style_class: "section-count" }); + textBox.add(this.countText, { expand: true, x_fill: false, x_align: St.Align.END }); this.countText.hide(); - this._innerBox.append(textBox, Big.BoxPackFlags.EXPAND); + this._innerBox.add(textBox, { expand: true }); if (!suppressBrowse) { this.moreLink = new MoreLink(); - this._innerBox.append(this.moreLink.actor, Big.BoxPackFlags.END); + this._innerBox.add(this.moreLink.actor, { x_align: St.Align.END }); } }, + _onStyleChanged: function () { + if (this._backgroundGradient) { + this._backgroundGradient.destroy(); + } + // Manually implement the gradient + let themeNode = this.actor.get_theme_node(); + let gradientTopColor = new Clutter.Color(); + if (!themeNode.get_color("-shell-gradient-top", false, gradientTopColor)) + return; + let gradientBottomColor = new Clutter.Color(); + if (!themeNode.get_color("-shell-gradient-bottom", false, gradientBottomColor)) + return; + this._backgroundGradient = Shell.create_vertical_gradient(gradientTopColor, + gradientBottomColor); + let box = this.actor.allocation; + let contentBox = new Clutter.ActorBox(); + themeNode.get_content_box(box, contentBox); + let width = contentBox.x2 - contentBox.x1; + let height = contentBox.y2 - contentBox.y1; + this._backgroundGradient.set_size(width, height); + // This will set a fixed position, which puts us outside of the normal box layout + this._backgroundGradient.set_position(0, 0); + + this._innerBox.add_actor(this._backgroundGradient); + this._backgroundGradient.lower_bottom(); + }, + setTitle : function(title) { this.text.text = title; }, @@ -531,41 +485,19 @@ function SearchSectionHeader(title, onClick) { SearchSectionHeader.prototype = { _init : function(title, onClick) { - let box = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, - padding_top: DASH_SECTION_PADDING, - padding_bottom: DASH_SECTION_PADDING, - spacing: DEFAULT_SPACING }); - let titleText = new Clutter.Text({ color: BRIGHTER_TEXT_COLOR, - font_name: 'Sans Bold 12px', - text: title }); - this.tooltip = new Clutter.Text({ color: BRIGHTER_TEXT_COLOR, - font_name: 'Sans 12px', - text: _("(see all)") }); - this.countText = new Clutter.Text({ color: BRIGHTER_TEXT_COLOR, - font_name: 'Sans Bold 14px' }); + this.actor = new St.Button({ style_class: "dash-search-section-header", + x_fill: true, + y_fill: true }); + let box = new St.BoxLayout(); + this.actor.set_child(box); + let titleText = new St.Label({ style_class: "dash-search-section-title", + text: title }); + this.countText = new St.Label({ style_class: "dash-search-section-count" }); - box.append(titleText, Big.BoxPackFlags.NONE); - box.append(this.tooltip, Big.BoxPackFlags.NONE); - box.append(this.countText, Big.BoxPackFlags.END); + box.add(titleText); + box.add(this.countText, { expand: true, x_fill: false, x_align: St.Align.END }); - this.tooltip.hide(); - - let button = new Button.Button(box, PRELIGHT_COLOR, BACKGROUND_COLOR, - TEXT_COLOR); - button.actor.height = box.height; - button.actor.padding_left = DEFAULT_PADDING; - button.actor.padding_right = DEFAULT_PADDING; - - button.actor.connect('activate', onClick); - button.actor.connect('notify::hover', Lang.bind(this, this._updateTooltip)); - this.actor = button.actor; - }, - - _updateTooltip : function(actor) { - if (actor.hover) - this.tooltip.show(); - else - this.tooltip.hide(); + this.actor.connect('clicked', onClick); } } @@ -575,11 +507,13 @@ function Section(titleString, suppressBrowse) { Section.prototype = { _init: function(titleString, suppressBrowse) { - this.actor = new Big.Box({ spacing: SECTION_INNER_SPACING }); + this.actor = new St.BoxLayout({ style_class: 'dash-section', + vertical: true }); this.header = new SectionHeader(titleString, suppressBrowse); - this.actor.append(this.header.actor, Big.BoxPackFlags.NONE); - this.content = new Big.Box({spacing: SECTION_INNER_SPACING }); - this.actor.append(this.content, Big.BoxPackFlags.EXPAND); + this.actor.add(this.header.actor); + this.content = new St.BoxLayout({ style_class: 'dash-section-content', + vertical: true }); + this.actor.add(this.content); } } @@ -598,21 +532,18 @@ Dash.prototype = { // of the Group actor ends up including the width of its hidden children, so we were getting a reactive object as // wide as the details pane that was blocking the clicks to the workspaces underneath it even when the details pane // was actually hidden. - this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL, - background_color: BACKGROUND_COLOR, - corner_radius: DASH_CORNER_RADIUS, - padding_left: DASH_PADDING_SIDE, - padding_right: DASH_PADDING_SIDE, - reactive: true }); + this.actor = new St.BoxLayout({ name: "dash", + vertical: true, + reactive: true }); // Size for this one explicitly set from overlay.js this.searchArea = new Big.Box({ y_align: Big.BoxAlignment.CENTER }); - this.sectionArea = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL, - spacing: DASH_SECTION_SPACING }); + this.sectionArea = new St.BoxLayout({ name: "dashSections", + vertical: true }); - this.actor.append(this.searchArea, Big.BoxPackFlags.NONE); - this.actor.append(this.sectionArea, Big.BoxPackFlags.NONE); + this.actor.add(this.searchArea); + this.actor.add(this.sectionArea); // The currently active popup display this._activePane = null; @@ -724,41 +655,41 @@ Dash.prototype = { this._appsSection = new Section(_("APPLICATIONS")); let appWell = new AppDisplay.AppWell(); - this._appsSection.content.append(appWell.actor, Big.BoxPackFlags.EXPAND); + this._appsSection.content.add(appWell.actor, { expand: true }); this._moreAppsPane = null; this._appsSection.header.moreLink.connect('activated', Lang.bind(this, function (link) { if (this._moreAppsPane == null) { this._moreAppsPane = new ResultPane(this); - this._moreAppsPane.packResults(APPS, true); + this._moreAppsPane.packResults(APPS); this._addPane(this._moreAppsPane); link.setPane(this._moreAppsPane); } })); - this.sectionArea.append(this._appsSection.actor, Big.BoxPackFlags.NONE); + this.sectionArea.add(this._appsSection.actor); /***** Places *****/ /* Translators: This is in the sense of locations for documents, network locations, etc. */ this._placesSection = new Section(_("PLACES"), true); - let placesDisplay = new Places.Places(); - this._placesSection.content.append(placesDisplay.actor, Big.BoxPackFlags.EXPAND); - this.sectionArea.append(this._placesSection.actor, Big.BoxPackFlags.NONE); + let placesDisplay = new PlaceDisplay.DashPlaceDisplay(); + this._placesSection.content.add(placesDisplay.actor, { expand: true }); + this.sectionArea.add(this._placesSection.actor); /***** Documents *****/ this._docsSection = new Section(_("RECENT DOCUMENTS")); this._docDisplay = new DocDisplay.DashDocDisplay(); - this._docsSection.content.append(this._docDisplay.actor, Big.BoxPackFlags.EXPAND); + this._docsSection.content.add(this._docDisplay.actor, { expand: true }); this._moreDocsPane = null; this._docsSection.header.moreLink.connect('activated', Lang.bind(this, function (link) { if (this._moreDocsPane == null) { this._moreDocsPane = new ResultPane(this); - this._moreDocsPane.packResults(DOCS, true); + this._moreDocsPane.packResults(DOCS); this._addPane(this._moreDocsPane); link.setPane(this._moreDocsPane); } @@ -770,7 +701,7 @@ Dash.prototype = { })); this._docDisplay.emit('changed'); - this.sectionArea.append(this._docsSection.actor, Big.BoxPackFlags.EXPAND); + this.sectionArea.add(this._docsSection.actor, { expand: true }); /***** Search Results *****/ @@ -797,6 +728,11 @@ Dash.prototype = { title: _("RECENT DOCUMENTS"), header: null, resultArea: null + }, + { type: PLACES, + title: _("PLACES"), + header: null, + resultArea: null } ]; @@ -807,14 +743,13 @@ Dash.prototype = { function () { this._showSingleSearchSection(section.type); })); - this._searchResultsSection.content.append(section.header.actor, Big.BoxPackFlags.NONE); - section.resultArea = new ResultArea(section.type, false); - section.resultArea.controlBox.hide(); - this._searchResultsSection.content.append(section.resultArea.actor, Big.BoxPackFlags.EXPAND); + this._searchResultsSection.content.add(section.header.actor); + section.resultArea = new ResultArea(section.type, GenericDisplay.GenericDisplayFlags.DISABLE_VSCROLLING); + this._searchResultsSection.content.add(section.resultArea.actor, { expand: true }); createPaneForDetails(this, section.resultArea.display); } - this.sectionArea.append(this._searchResultsSection.actor, Big.BoxPackFlags.EXPAND); + this.sectionArea.add(this._searchResultsSection.actor, { expand: true }); this._searchResultsSection.actor.hide(); }, @@ -859,6 +794,11 @@ Dash.prototype = { } } + // Here work around a bug that I never quite tracked down + // the root cause of; it appeared that the search results + // section was getting a 0 height allocation. + this._searchResultsSection.content.queue_relayout(); + return false; }, @@ -926,7 +866,6 @@ Dash.prototype = { if (section.type == type) { // This will be the only section shown. section.resultArea.display.selectFirstItem(); - section.resultArea.controlBox.show(); let itemCount = section.resultArea.display.getMatchedItemsCount(); let itemCountText = itemCount + ""; section.header.actor.hide(); @@ -950,8 +889,6 @@ Dash.prototype = { let section = this._searchSections[i]; if (section.type == this._searchResultsSingleShownSection) { // This will no longer be the only section shown. - section.resultArea.display.displayPage(0); - section.resultArea.controlBox.hide(); let itemCount = section.resultArea.display.getMatchedItemsCount(); if (itemCount != 0) { section.header.actor.show(); diff --git a/js/ui/docDisplay.js b/js/ui/docDisplay.js index 9596b0b43..2b67f50ba 100644 --- a/js/ui/docDisplay.js +++ b/js/ui/docDisplay.js @@ -8,6 +8,7 @@ const Lang = imports.lang; const Pango = imports.gi.Pango; const Shell = imports.gi.Shell; const Signals = imports.signals; +const St = imports.gi.St; const Mainloop = imports.mainloop; const DocInfo = imports.misc.docInfo; @@ -111,19 +112,19 @@ DocDisplayItem.prototype = { /* This class represents a display containing a collection of document items. * The documents are sorted by how recently they were last visited. */ -function DocDisplay() { - this._init(); +function DocDisplay(flags) { + this._init(flags); } DocDisplay.prototype = { __proto__: GenericDisplay.GenericDisplay.prototype, - _init : function() { - GenericDisplay.GenericDisplay.prototype._init.call(this); + _init : function(flags) { + GenericDisplay.GenericDisplay.prototype._init.call(this, flags); // We keep a single timeout callback for updating last visited times // for all the items in the display. This avoids creating individual // callbacks for each item in the display. So proper time updates - // for individual items and item details depend on the item being + // for individual items and item details depend on the item being // associated with one of the displays. this._updateTimeoutTargetTime = -1; this._updateTimeoutId = 0; @@ -278,10 +279,8 @@ DashDocDisplayItem.prototype = { let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER }); iconBox.append(this._icon, Big.BoxPackFlags.NONE); this.actor.append(iconBox, Big.BoxPackFlags.NONE); - let name = new Clutter.Text({ font_name: "Sans 14px", - color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR, - ellipsize: Pango.EllipsizeMode.END, - text: docInfo.name }); + let name = new St.Label({ style_class: "dash-recent-docs-item", + text: docInfo.name }); this.actor.append(name, Big.BoxPackFlags.EXPAND); let draggable = DND.makeDraggable(this.actor); diff --git a/js/ui/environment.js b/js/ui/environment.js index 6d0bb4884..dbf816413 100644 --- a/js/ui/environment.js +++ b/js/ui/environment.js @@ -1,6 +1,7 @@ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ const St = imports.gi.St; +const Gettext_gtk20 = imports.gettext.domain('gtk20'); const Tweener = imports.ui.tweener; @@ -33,4 +34,9 @@ _patchContainerClass(St.Table); function init() { Tweener.init(); String.prototype.format = Format.format; + + // Set the default direction for St widgets (this needs to be done before any use of St) + if (Gettext_gtk20.gettext("default:LTR") == "default:RTL") { + St.Widget.set_default_direction(St.TextDirection.RTL); + } } diff --git a/js/ui/genericDisplay.js b/js/ui/genericDisplay.js index 4744669a2..d5c3e3f38 100644 --- a/js/ui/genericDisplay.js +++ b/js/ui/genericDisplay.js @@ -11,6 +11,7 @@ const Meta = imports.gi.Meta; const Pango = imports.gi.Pango; const Signals = imports.signals; const Shell = imports.gi.Shell; +const St = imports.gi.St; const Button = imports.ui.button; const DND = imports.ui.dnd; @@ -18,18 +19,12 @@ const Link = imports.ui.link; const Main = imports.ui.main; const RedisplayFlags = { NONE: 0, - RESET_CONTROLS: 1 << 0, FULL: 1 << 1, - SUBSEARCH: 1 << 2 }; + SUBSEARCH: 1 << 2, + IMMEDIATE: 1 << 3 }; -const ITEM_DISPLAY_NAME_COLOR = new Clutter.Color(); -ITEM_DISPLAY_NAME_COLOR.from_pixel(0xffffffff); const ITEM_DISPLAY_DESCRIPTION_COLOR = new Clutter.Color(); ITEM_DISPLAY_DESCRIPTION_COLOR.from_pixel(0xffffffbb); -const ITEM_DISPLAY_BACKGROUND_COLOR = new Clutter.Color(); -ITEM_DISPLAY_BACKGROUND_COLOR.from_pixel(0x00000000); -const ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR = new Clutter.Color(); -ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR.from_pixel(0x4f6fadaa); const DISPLAY_CONTROL_SELECTED_COLOR = new Clutter.Color(); DISPLAY_CONTROL_SELECTED_COLOR.from_pixel(0x112288ff); const PREVIEW_BOX_BACKGROUND_COLOR = new Clutter.Color(); @@ -37,10 +32,7 @@ PREVIEW_BOX_BACKGROUND_COLOR.from_pixel(0xADADADf0); const DEFAULT_PADDING = 4; -const ITEM_DISPLAY_HEIGHT = 50; const ITEM_DISPLAY_ICON_SIZE = 48; -const ITEM_DISPLAY_PADDING = 1; -const ITEM_DISPLAY_PADDING_RIGHT = 2; const DEFAULT_COLUMN_GAP = 6; const PREVIEW_ICON_SIZE = 96; @@ -63,12 +55,8 @@ function GenericDisplayItem() { GenericDisplayItem.prototype = { _init: function() { - this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, - spacing: ITEM_DISPLAY_PADDING, - reactive: true, - background_color: ITEM_DISPLAY_BACKGROUND_COLOR, - corner_radius: 4, - height: ITEM_DISPLAY_HEIGHT }); + this.actor = new St.BoxLayout({ style_class: "generic-display-item", + reactive: true }); this.actor._delegate = this; this.actor.connect('button-release-event', @@ -82,16 +70,12 @@ GenericDisplayItem.prototype = { let draggable = DND.makeDraggable(this.actor); draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin)); - this._infoContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, - spacing: DEFAULT_PADDING }); - this.actor.append(this._infoContent, Big.BoxPackFlags.EXPAND); + this._iconBin = new St.Bin(); + this.actor.add(this._iconBin); - this._iconBox = new Big.Box(); - this._infoContent.append(this._iconBox, Big.BoxPackFlags.NONE); - - this._infoText = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL, - spacing: DEFAULT_PADDING }); - this._infoContent.append(this._infoText, Big.BoxPackFlags.EXPAND); + this._infoText = new St.BoxLayout({ style_class: "generic-display-item-text", + vertical: true }); + this.actor.add(this._infoText, { expand: true, y_fill: false }); let infoIconUri = "file://" + global.imagedir + "info.svg"; let infoIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER, @@ -104,7 +88,7 @@ GenericDisplayItem.prototype = { padding_left: DEFAULT_PADDING, padding_right: DEFAULT_PADDING, y_align: Big.BoxAlignment.CENTER }); buttonBox.append(this._informationButton.actor, Big.BoxPackFlags.NONE); - this.actor.append(buttonBox, Big.BoxPackFlags.END); + this.actor.add(buttonBox, { x_fill: false, x_align: St.Align.END }); // Connecting to the button-press-event for the information button ensures that the actor, // which is a draggable actor, does not get the button-press-event and doesn't initiate @@ -160,16 +144,8 @@ GenericDisplayItem.prototype = { // Highlights the item by setting a different background color than the default // if isSelected is true, removes the highlighting otherwise. markSelected: function(isSelected) { - let color; - if (isSelected) { - color = ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR; - this._informationButton.forceShow(true); - } - else { - color = ITEM_DISPLAY_BACKGROUND_COLOR; - this._informationButton.forceShow(false); - } - this.actor.background_color = color; + this.actor.set_style_pseudo_class(isSelected ? "selected" : null); + this._informationButton.forceShow(isSelected) }, /* @@ -185,19 +161,14 @@ GenericDisplayItem.prototype = { spacing: PREVIEW_BOX_SPACING }); // Inner box with name and description - let textDetails = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL, - spacing: PREVIEW_BOX_SPACING }); - let detailsName = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR, - font_name: "Sans bold 14px", - line_wrap: true, - text: this._name.text }); - textDetails.append(detailsName, Big.BoxPackFlags.NONE); + let textDetails = new St.BoxLayout({ style_class: 'generic-display-details', + vertical: true }); + let detailsName = new St.Label({ style_class: 'generic-display-details-name', + text: this._name.text }); + textDetails.add(detailsName); - let detailsDescription = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR, - font_name: "Sans 14px", - line_wrap: true, - text: this._description.text }); - textDetails.append(detailsDescription, Big.BoxPackFlags.NONE); + let detailsDescription = new St.Label({ text: this._description.text }); + textDetails.add(detailsDescription); this._detailsDescriptions.push(detailsDescription); @@ -223,7 +194,7 @@ GenericDisplayItem.prototype = { // Destroys the item. destroy: function() { - this.actor.destroy(); + this.actor.destroy(); }, //// Pure virtual public methods //// @@ -260,20 +231,15 @@ GenericDisplayItem.prototype = { } this._icon = this._createIcon(); - this._iconBox.append(this._icon, Big.BoxPackFlags.NONE); + this._iconBin.set_child(this._icon); - this._name = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR, - font_name: "Sans 14px", - ellipsize: Pango.EllipsizeMode.END, - text: nameText }); - this._infoText.append(this._name, Big.BoxPackFlags.EXPAND); + this._name = new St.Label({ style_class: "generic-display-item-name", + text: nameText }); + this._infoText.add(this._name); - this._description = new Clutter.Text({ color: ITEM_DISPLAY_DESCRIPTION_COLOR, - font_name: "Sans 12px", - ellipsize: Pango.EllipsizeMode.END, - text: descriptionText ? descriptionText : "" - }); - this._infoText.append(this._description, Big.BoxPackFlags.EXPAND); + this._description = new St.Label({ style_class: "generic-display-item-description", + text: descriptionText ? descriptionText : "" }); + this._infoText.add(this._description); }, // Sets the description text for the item, including the description text @@ -319,31 +285,36 @@ GenericDisplayItem.prototype = { Signals.addSignalMethods(GenericDisplayItem.prototype); +const GenericDisplayFlags = { + DISABLE_VSCROLLING: 1 << 0 +} + /* This is a virtual class that represents a display containing a collection of items * that can be filtered with a search string. */ -function GenericDisplay() { - this._init(); +function GenericDisplay(flags) { + this._init(flags); } GenericDisplay.prototype = { - _init : function() { + _init : function(flags) { + let disableVScrolling = (flags & GenericDisplayFlags.DISABLE_VSCROLLING) != 0; this._search = ''; this._expanded = false; - this._maxItemsPerPage = null; - this._list = new Shell.OverflowList({ spacing: 6.0, - item_height: ITEM_DISPLAY_HEIGHT }); - - this._list.connect('notify::n-pages', Lang.bind(this, function () { - this._updateDisplayControl(true); - })); - this._list.connect('notify::page', Lang.bind(this, function () { - this._updateDisplayControl(false); - })); + if (disableVScrolling) { + this.actor = this._list = new Shell.OverflowList({ spacing: 6, + item_height: 50 }); + } else { + this.actor = new St.ScrollView({ x_fill: true, y_fill: true }); + this.actor.get_hscroll_bar().hide(); + this._list = new St.BoxLayout({ style_class: 'generic-display-container', + vertical: true }); + this.actor.add_actor(this._list); + } this._pendingRedisplay = RedisplayFlags.NONE; - this._list.connect('notify::mapped', Lang.bind(this, this._onMappedNotify)); + this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify)); // map where Object represents the item info this._allItems = {}; @@ -355,13 +326,6 @@ GenericDisplay.prototype = { this._displayedItems = {}; this._openDetailIndex = -1; this._selectedIndex = -1; - // These two are public - .actor is the normal "actor subclass" property, - // but we also expose a .displayControl actor which is separate. - // See also getNavigationArea. - this.actor = this._list; - this.displayControl = new Big.Box({ background_color: ITEM_DISPLAY_BACKGROUND_COLOR, - spacing: 12, - orientation: Big.BoxOrientation.HORIZONTAL}); }, //// Public methods //// @@ -369,16 +333,18 @@ GenericDisplay.prototype = { // Sets the search string and displays the matching items. setSearch: function(text) { let lowertext = text.toLowerCase(); - if (lowertext == this._search) + if (lowertext == this._search) { return; - let flags = RedisplayFlags.RESET_CONTROLS; + } + let flags = RedisplayFlags.IMMEDIATE; if (this._search != '') { // 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) + lowertext.split(/\s+/).length == this._search.split(/\s+/).length) { flags |= RedisplayFlags.SUBSEARCH; + } } this._search = lowertext; this._redisplay(flags); @@ -398,7 +364,7 @@ GenericDisplay.prototype = { // to the bottom one. Returns true if the selection actually moved up, false if it wrapped // around to the bottom. selectUp: function() { - let count = this._list.displayedCount; + let count = this._getVisibleCount(); let selectedUp = true; let prev = this._selectedIndex - 1; if (this._selectedIndex <= 0) { @@ -413,7 +379,7 @@ GenericDisplay.prototype = { // to the top one. Returns true if the selection actually moved down, false if it wrapped // around to the top. selectDown: function() { - let count = this._list.displayedCount; + let count = this._getVisibleCount(); let selectedDown = true; let next = this._selectedIndex + 1; if (this._selectedIndex == count - 1) { @@ -432,7 +398,7 @@ GenericDisplay.prototype = { // Selects the last item among the displayed items. selectLastItem: function() { - let count = this._list.displayedCount; + let count = this._getVisibleCount(); if (this.hasItems()) this._selectIndex(count - 1); }, @@ -469,6 +435,8 @@ GenericDisplay.prototype = { resetState: function() { this._filterReset(); this._openDetailIndex = -1; + if (!(this.actor instanceof Shell.OverflowList)) + this.actor.get_vscroll_bar().get_adjustment().value = 0; }, // Returns an actor which acts as a sidebar; this is used for @@ -482,15 +450,6 @@ GenericDisplay.prototype = { return item.createDetailsActor(); }, - // Displays the page specified by the pageNumber argument. - displayPage: function(pageNumber) { - // Cleanup from the previous selection, but don't unset this._selectedIndex - if (this.hasSelected()) { - this._findDisplayedByIndex(this._selectedIndex).markSelected(false); - } - this._list.page = pageNumber; - }, - //// Protected methods //// _recreateDisplayItems: function() { @@ -516,14 +475,14 @@ GenericDisplay.prototype = { Lang.bind(this, function() { // update the selection - this._selectIndex(this._list.get_actor_index(displayItem.actor)); + this._selectIndex(this._list.get_children().indexOf(displayItem.actor)); this.activateSelected(); })); displayItem.connect('show-details', Lang.bind(this, function() { - let index = this._list.get_actor_index(displayItem.actor); + let index = this._list.get_children().indexOf(displayItem.actor); /* Close the details pane if already open */ if (index == this._openDetailIndex) { this._openDetailIndex = -1; @@ -538,9 +497,10 @@ GenericDisplay.prototype = { // Removes an item identifed by the itemId from the displayed items. _removeDisplayItem: function(itemId) { - let count = this._list.displayedCount; + let children = this._list.get_children(); + let count = children.length; let displayItem = this._displayedItems[itemId]; - let displayItemIndex = this._list.get_actor_index(displayItem.actor); + let displayItemIndex = children.indexOf(displayItem.actor); if (this.hasSelected() && count == 1) { this.unsetSelected(); @@ -635,24 +595,22 @@ GenericDisplay.prototype = { /* * Updates the displayed items, applying the search string if one exists. * @flags: Flags controlling redisplay behavior as follows: - * RESET_CONTROLS - indicates if the page selection should be reset when displaying the matching results. - * We reset the page selection when the change in results was initiated by the user by - * entering a different search criteria or by viewing the results list in a different - * size mode, but we keep the page selection the same if the results got updated on - * 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 recreate all displayed items; implies RESET_CONTROLS as well + * FULL - Indicates that we need recreate all displayed items. + * IMMEDIATE - Do the full redisplay even if we're not mapped. This is useful + * if you want to get the number of matched items and show/hide a section based on + * that number. */ _redisplay: function(flags) { - if (!this._list.mapped) { + let immediate = (flags & RedisplayFlags.IMMEDIATE) != 0; + if (!immediate && !this.actor.mapped) { this._pendingRedisplay |= flags; return; } - let isSubSearch = (flags & RedisplayFlags.SUBSEARCH) > 0; - let fullReload = (flags & RedisplayFlags.FULL) > 0; - let resetPage = (flags & RedisplayFlags.RESET_CONTROLS) > 0 || fullReload; + let isSubSearch = (flags & RedisplayFlags.SUBSEARCH) != 0; + let fullReload = (flags & RedisplayFlags.FULL) != 0; let hadSelected = this.hasSelected(); this.unsetSelected(); @@ -674,9 +632,6 @@ GenericDisplay.prototype = { this._redisplayReordering(); } - if (resetPage) - this._list.page = 0; - if (hadSelected) { this._selectedIndex = -1; this.selectFirstItem(); @@ -776,59 +731,14 @@ GenericDisplay.prototype = { return matchScores; }, - /* - * Updates the display control to reflect the matched items set and the page selected. - * - * resetDisplayControl - indicates if the display control should be re-created because - * the results or the space allocated for them changed. If it's false, - * the existing display control is used and only the page links are - * updated to reflect the current page selection. - */ - _updateDisplayControl: function(resetDisplayControl) { - if (resetDisplayControl) { - this.displayControl.remove_all(); - let nPages = this._list.n_pages; - // Don't show the page indicator if there is only one page. - if (nPages == 1) - return; - let pageNumber = this._list.page; - for (let i = 0; i < nPages; i++) { - let pageControl = new Link.Link({ color: (i == pageNumber) ? DISPLAY_CONTROL_SELECTED_COLOR : ITEM_DISPLAY_DESCRIPTION_COLOR, - font_name: "Sans Bold 16px", - text: (i+1) + "", - reactive: (i == pageNumber) ? false : true}); - this.displayControl.append(pageControl.actor, Big.BoxPackFlags.NONE); - - // we use pageNumberLocalScope to get the page number right in the callback function - let pageNumberLocalScope = i; - pageControl.connect('clicked', - Lang.bind(this, - function(o, event) { - this.displayPage(pageNumberLocalScope); - })); - } - } else { - let pageControlActors = this.displayControl.get_children(); - for (let i = 0; i < pageControlActors.length; i++) { - let pageControlActor = pageControlActors[i]; - if (i == this._list.page) { - pageControlActor.color = DISPLAY_CONTROL_SELECTED_COLOR; - pageControlActor.reactive = false; - } else { - pageControlActor.color = ITEM_DISPLAY_DESCRIPTION_COLOR; - pageControlActor.reactive = true; - } - } - } - if (this.hasSelected()) { - this.selectFirstItem(); - } - }, - // Returns a display item based on its index in the ordering of the // display children. _findDisplayedByIndex: function(index) { - let actor = this._list.get_displayed_actor(index); + let actor; + if (this.actor instanceof Shell.OverflowList) + actor = this.actor.get_displayed_actor(index); + else + actor = this._list.get_children()[index]; return this._findDisplayedByActor(actor); }, @@ -862,8 +772,14 @@ GenericDisplay.prototype = { this.emit('selected'); }, + _getVisibleCount: function() { + if (this.actor instanceof Shell.OverflowList) + return this._list.displayed_count; + return this._list.get_n_children(); + }, + _onMappedNotify: function () { - let mapped = this._list.mapped; + let mapped = this.actor.mapped; if (mapped && this._pendingRedisplay > RedisplayFlags.NONE) this._redisplay(this._pendingRedisplay); diff --git a/js/ui/lookingGlass.js b/js/ui/lookingGlass.js index 35431f0ad..afe99b864 100644 --- a/js/ui/lookingGlass.js +++ b/js/ui/lookingGlass.js @@ -320,6 +320,10 @@ LookingGlass.prototype = { this._savedText = null; this._historyNavIndex = -1; this._history = []; + this._borderPaintTarget = null; + this._borderPaintId = 0; + this._borderDestroyId = 0; + this._readHistory(); this._open = false; @@ -487,6 +491,18 @@ LookingGlass.prototype = { this._results.push(result); this._resultsArea.append(result.actor, Big.BoxPackFlags.NONE); this._propInspector.setTarget(obj); + if (this._borderPaintTarget != null) { + this._borderPaintTarget.disconnect(this._borderPaintId); + this._borderPaintTarget = null; + } + if (obj instanceof Clutter.Actor) { + this._borderPaintTarget = obj; + this._borderPaintId = Shell.add_hook_paint_red_border(obj); + this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () { + this._borderDestroyId = 0; + this._borderPaintTarget = null; + })); + } let children = this._resultsArea.get_children(); if (children.length > this._maxItems) { this._results.shift(); @@ -582,6 +598,12 @@ LookingGlass.prototype = { this._open = false; Tweener.removeTweens(this.actor); + if (this._borderPaintTarget != null) { + this._borderPaintTarget.disconnect(this._borderPaintId); + this._borderPaintTarget.disconnect(this._borderDestroyId); + this._borderPaintTarget = null; + } + Main.popModal(this.actor); Tweener.addTween(this.actor, { time: 0.5, diff --git a/js/ui/main.js b/js/ui/main.js index f0dae6815..0bea5d59e 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -18,6 +18,7 @@ const MessageTray = imports.ui.messageTray; const Messaging = imports.ui.messaging; const Overview = imports.ui.overview; const Panel = imports.ui.panel; +const PlaceDisplay = imports.ui.placeDisplay; const RunDialog = imports.ui.runDialog; const LookingGlass = imports.ui.lookingGlass; const NotificationDaemon = imports.ui.notificationDaemon; @@ -31,6 +32,7 @@ DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff); let chrome = null; let panel = null; let sidebar = null; +let placesManager = null; let overview = null; let runDialog = null; let lookingGlass = null; @@ -102,6 +104,7 @@ function start() { getRunDialog().open(); }); + placesManager = new PlaceDisplay.PlacesManager(); overview = new Overview.Overview(); chrome = new Chrome.Chrome(); panel = new Panel.Panel(); diff --git a/js/ui/overview.js b/js/ui/overview.js index dad7822fa..e217bfe1a 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -123,8 +123,7 @@ Overview.prototype = { // Container to hold popup pane chrome. this._paneContainer = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, - spacing: 6 - }); + spacing: 6 }); // Note here we explicitly don't set the paneContainer to be reactive yet; that's done // inside the notify::visible handler on panes. this._paneContainer.connect('button-release-event', Lang.bind(this, function(background) { @@ -149,11 +148,11 @@ Overview.prototype = { // We divide the screen into an imaginary grid which helps us determine the layout of // different visual components. if (wideScreen) { - displayGridColumnWidth = primary.width / COLUMNS_WIDE_SCREEN; - displayGridRowHeight = primary.height / ROWS_WIDE_SCREEN; + displayGridColumnWidth = Math.floor(primary.width / COLUMNS_WIDE_SCREEN); + displayGridRowHeight = Math.floor(primary.height / ROWS_WIDE_SCREEN); } else { - displayGridColumnWidth = primary.width / COLUMNS_REGULAR_SCREEN; - displayGridRowHeight = primary.height / ROWS_REGULAR_SCREEN; + displayGridColumnWidth = Math.floor(primary.width / COLUMNS_REGULAR_SCREEN); + displayGridRowHeight = Math.floor(primary.height / ROWS_REGULAR_SCREEN); } }, @@ -175,11 +174,11 @@ Overview.prototype = { - WORKSPACE_GRID_PADDING * 2; // 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 * (primary.height / primary.width) * 2; + this._workspacesHeight = Math.floor(displayGridRowHeight * workspaceRowsUsed + - WORKSPACE_GRID_PADDING * (primary.height / primary.width) * 2); this._workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING; - this._workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (primary.height / primary.width); + this._workspacesY = Math.floor(displayGridRowHeight + WORKSPACE_GRID_PADDING * (primary.height / primary.width)); this._dash.actor.set_position(0, contentY); this._dash.actor.set_size(displayGridColumnWidth, contentHeight); @@ -197,9 +196,9 @@ Overview.prototype = { 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); + this._workspacesY); // Dynamic width - this._paneContainer.height = contentHeight; + this._paneContainer.height = this._workspacesHeight; this._transparentBackground.set_position(this._paneContainer.x, this._paneContainer.y); this._transparentBackground.set_size(primary.width - this._paneContainer.x, @@ -228,6 +227,7 @@ Overview.prototype = { this._activeDisplayPane.close(); return true; })); + this._workspaces.actor.opacity = 64; } else if (pane == this._activeDisplayPane) { this._activeDisplayPane = null; if (backgroundEventId != null) { @@ -236,6 +236,7 @@ Overview.prototype = { } this._transparentBackground.lower_bottom(); this._paneContainer.lower_bottom(); + this._workspaces.actor.opacity = 255; } })); }, diff --git a/js/ui/panel.js b/js/ui/panel.js index 447bb854e..9e2fdb431 100644 --- a/js/ui/panel.js +++ b/js/ui/panel.js @@ -16,6 +16,7 @@ const _ = Gettext.gettext; const Button = imports.ui.button; const Calendar = imports.ui.calendar; const Main = imports.ui.main; +const StatusMenu = imports.ui.statusMenu; const PANEL_HEIGHT = 26; const TRAY_HEIGHT = PANEL_HEIGHT - 1; @@ -176,6 +177,8 @@ Panel.prototype = { this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL }); + this.actor._delegate = this; + let backgroundGradient = Shell.create_vertical_gradient(BACKGROUND_TOP, BACKGROUND_BOTTOM); this.actor.connect('notify::allocation', Lang.bind(this, function () { @@ -371,11 +374,8 @@ Panel.prototype = { this._traymanager.manage_stage(global.stage); let statusbox = new Big.Box(); - let statusmenu = this._statusmenu = new Shell.StatusMenu(); - statusmenu.get_icon().hide(); - statusmenu.get_name().fontName = DEFAULT_FONT; - statusmenu.get_name().color = PANEL_FOREGROUND_COLOR; - statusbox.append(this._statusmenu, Big.BoxPackFlags.NONE); + let statusmenu = this._statusmenu = new StatusMenu.StatusMenu(); + statusbox.append(this._statusmenu.actor, Big.BoxPackFlags.NONE); let statusbutton = new Button.Button(statusbox, PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR, @@ -385,7 +385,7 @@ Panel.prototype = { if (e.get_button() == 1 && e.get_click_count() == 1) { statusmenu.toggle(e); // The statusmenu might not pop up if it couldn't get a pointer grab - if (statusmenu.is_active()) + if (statusmenu.isActive()) statusbutton.actor.active = true; return true; } else { diff --git a/js/ui/placeDisplay.js b/js/ui/placeDisplay.js new file mode 100644 index 000000000..f408fc978 --- /dev/null +++ b/js/ui/placeDisplay.js @@ -0,0 +1,540 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +const Big = imports.gi.Big; +const Clutter = imports.gi.Clutter; +const Pango = imports.gi.Pango; +const GLib = imports.gi.GLib; +const Gio = imports.gi.Gio; +const Shell = imports.gi.Shell; +const Lang = imports.lang; +const Mainloop = imports.mainloop; +const Signals = imports.signals; +const St = imports.gi.St; +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_ICON_SIZE = 16; + +/** + * Represents a place object, which is most normally a bookmark entry, + * a mount/volume, or a special place like the Home Folder, Computer, and Network. + * + * @name: String title + * @iconFactory: A JavaScript callback which will create an icon texture given a size parameter + * @launch: A JavaScript callback to launch the entry + */ +function PlaceInfo(name, iconFactory, launch) { + this._init(name, iconFactory, launch); +} + +PlaceInfo.prototype = { + _init: function(name, iconFactory, launch) { + this.name = name; + this.iconFactory = iconFactory; + this.launch = launch; + this.id = null; + } +} + +function PlacesManager() { + this._init(); +} + +PlacesManager.prototype = { + _init: function() { + let gconf = Shell.GConf.get_default(); + gconf.watch_directory(NAUTILUS_PREFS_DIR); + + this._mounts = []; + this._bookmarks = []; + this._isDesktopHome = false; + + let homeFile = Gio.file_new_for_path (GLib.get_home_dir()); + let homeUri = homeFile.get_uri(); + let homeLabel = Shell.util_get_label_for_uri (homeUri); + let homeIcon = Shell.util_get_icon_for_uri (homeUri); + this._home = new PlaceInfo(homeLabel, + function(size) { + return Shell.TextureCache.get_default().load_gicon(homeIcon, size); + }, + function() { + Gio.app_info_launch_default_for_uri(homeUri, Main.createAppLaunchContext()); + }); + + 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 PlaceInfo(desktopLabel, + function(size) { + return Shell.TextureCache.get_default().load_gicon(desktopIcon, size); + }, + function() { + Gio.app_info_launch_default_for_uri(desktopUri, Main.createAppLaunchContext()); + }); + + this._connect = new PlaceInfo(_("Connect to..."), + function (size) { + return Shell.TextureCache.get_default().load_icon_name("applications-internet", size); + }, + function () { + new Shell.Process({ args: ['nautilus-connect-server'] }).run(); + }); + + let networkApp = null; + try { + networkApp = Shell.AppSystem.get_default().load_from_desktop_file('gnome-network-scheme.desktop'); + } catch(e) { + try { + networkApp = Shell.AppSystem.get_default().load_from_desktop_file('network-scheme.desktop'); + } catch(e) { + log("Cannot create \"Network\" item, .desktop file not found or corrupt."); + } + } + + if (networkApp != null) { + this._network = new PlaceInfo(networkApp.get_name(), + function(size) { + return networkApp.create_icon_texture(size); + }, + function () { + networkApp.launch(); + }); + } + + /* + * Show devices, code more or less ported from nautilus-places-sidebar.c + */ + 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)); + this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices)); + this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices)); + this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices)); + this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices)); + this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices)); + this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices)); + this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices)); + this._updateDevices(); + + this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), ".gtk-bookmarks"]); + this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath); + let monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null); + this._bookmarkTimeoutId = 0; + monitor.connect('changed', Lang.bind(this, function () { + if (this._bookmarkTimeoutId > 0) + return; + /* Defensive event compression */ + this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () { + this._bookmarkTimeoutId = 0; + this._reloadBookmarks(); + return false; + })); + })); + + this._reloadBookmarks(); + this._updateDesktopMenuVisibility(); + + gconf.connect('changed::' + DESKTOP_IS_HOME_KEY, Lang.bind(this, this._updateDesktopMenuVisibility)); + + }, + + _updateDevices: function() { + this._mounts = []; + + /* first go through all connected drives */ + let drives = this._volumeMonitor.get_connected_drives(); + for (let i = 0; i < drives.length; i++) { + let volumes = drives[i].get_volumes(); + for(let j = 0; j < volumes.length; j++) { + let mount = volumes[j].get_mount(); + if(mount != null) { + this._addMount(mount); + } + } + } + + /* add all volumes that is not associated with a drive */ + let volumes = this._volumeMonitor.get_volumes(); + for(let i = 0; i < volumes.length; i++) { + if(volumes[i].get_drive() != null) + continue; + + let mount = volumes[i].get_mount(); + if(mount != null) { + this._addMount(mount); + } + } + + /* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */ + let mounts = this._volumeMonitor.get_mounts(); + for(let i = 0; i < mounts.length; i++) { + if(mounts[i].is_shadowed()) + continue; + + if(mounts[i].get_volume()) + continue; + + this._addMount(mounts[i]); + } + + /* We emit two signals, one for a generic 'all places' update + * and the other for one specific to mounts. We do this because + * clients like PlaceDisplay may only care about places in general + * being updated while clients like DashPlaceDisplay care which + * specific type of place got updated. + */ + this.emit('mounts-updated'); + this.emit('places-updated'); + + }, + + _reloadBookmarks: function() { + + this._bookmarks = []; + + if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS)) + return; + + let [success, bookmarksContent, len] = GLib.file_get_contents(this._bookmarksPath); + + if (!success) + return; + + let bookmarks = bookmarksContent.split('\n'); + + let bookmarksToLabel = {}; + let bookmarksOrder = []; + for (let i = 0; i < bookmarks.length; i++) { + let bookmarkLine = bookmarks[i]; + let components = bookmarkLine.split(' '); + let bookmark = components[0]; + if (bookmark in bookmarksToLabel) + continue; + let label = null; + if (components.length > 1) + label = components.slice(1).join(' '); + bookmarksToLabel[bookmark] = label; + bookmarksOrder.push(bookmark); + } + + for (let i = 0; i < bookmarksOrder.length; i++) { + let bookmark = bookmarksOrder[i]; + let label = bookmarksToLabel[bookmark]; + let file = Gio.file_new_for_uri(bookmark); + if (!file.query_exists(null)) + continue; + if (label == null) + label = Shell.util_get_label_for_uri(bookmark); + if (label == null) + continue; + let icon = Shell.util_get_icon_for_uri(bookmark); + + let item = new PlaceInfo(label, + function(size) { + return Shell.TextureCache.get_default().load_gicon(icon, size); + }, + function() { + Gio.app_info_launch_default_for_uri(bookmark, Main.createAppLaunchContext()); + }); + this._bookmarks.push(item); + } + + /* See comment in _updateDevices for explanation why there are two signals. */ + this.emit('bookmarks-updated'); + this.emit('places-updated'); + }, + + _updateDesktopMenuVisibility: function() { + let gconf = Shell.GConf.get_default(); + this._isDesktopHome = gconf.get_boolean(DESKTOP_IS_HOME_KEY); + + /* See comment in _updateDevices for explanation why there are two signals. */ + this.emit('defaults-updated'); + this.emit('places-updated'); + }, + + _addMount: function(mount) { + let mountLabel = mount.get_name(); + let mountIcon = mount.get_icon(); + let root = mount.get_root(); + let mountUri = root.get_uri(); + let devItem = new PlaceInfo(mountLabel, + function(size) { + return Shell.TextureCache.get_default().load_gicon(mountIcon, size); + }, + function() { + Gio.app_info_launch_default_for_uri(mountUri, Main.createAppLaunchContext()); + }); + this._mounts.push(devItem); + }, + + getAllPlaces: function () { + return this.getDefaultPlaces().concat(this.getBookmarks(), this.getMounts()); + }, + + getDefaultPlaces: function () { + let places = [this._home]; + + if (!this._isDesktopHome) + places.push(this._desktopMenu); + + if (this._network) + places.push(this._network); + + places.push(this._connect); + return places; + }, + + getBookmarks: function () { + return this._bookmarks; + }, + + getMounts: function () { + return this._mounts; + } +}; + +Signals.addSignalMethods(PlacesManager.prototype); + +/** + * An entry in the places menu. + * @info The corresponding PlaceInfo to populate this entry. + */ +function DashPlaceDisplayItem(info) { + this._init(info); +} + +DashPlaceDisplayItem.prototype = { + _init: function(info) { + this.name = info.name; + this._info = info; + this._icon = info.iconFactory(PLACES_ICON_SIZE); + this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, + reactive: true, + spacing: 4 }); + this.actor.connect('button-release-event', Lang.bind(this, function (b, e) { + this._info.launch(); + Main.overview.hide(); + })); + let text = new St.Label({ style_class: 'places-item', + text: info.name }); + let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER }); + iconBox.append(this._icon, Big.BoxPackFlags.NONE); + this.actor.append(iconBox, Big.BoxPackFlags.NONE); + this.actor.append(text, Big.BoxPackFlags.EXPAND); + + this.actor._delegate = this; + let draggable = DND.makeDraggable(this.actor); + }, + + getDragActorSource: function() { + return this._icon; + }, + + getDragActor: function(stageX, stageY) { + return this._info.iconFactory(PLACES_ICON_SIZE); + }, + + //// Drag and drop methods //// + + shellWorkspaceLaunch: function() { + this._info.launch(); + } +}; + +function DashPlaceDisplay() { + this._init(); +} + +DashPlaceDisplay.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._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 St.BoxLayout({ style_class: 'places-actions', + vertical: true }); + + this._devBox = new St.BoxLayout({ style_class: 'places-actions', + name: 'placesDevices', + vertical: true }); + + this._dirsBox = new St.BoxLayout({ style_class: 'places-actions', + vertical: true }); + + this._leftBox.append(this._actionsBox, Big.BoxPackFlags.NONE); + this._leftBox.append(this._devBox, Big.BoxPackFlags.NONE); + + this._rightBox.append(this._dirsBox, Big.BoxPackFlags.NONE); + + Main.placesManager.connect('defaults-updated', Lang.bind(this, this._updateDefaults)); + Main.placesManager.connect('bookmarks-updated', Lang.bind(this, this._updateBookmarks)); + Main.placesManager.connect('mounts-updated', Lang.bind(this, this._updateMounts)); + + this._updateDefaults(); + this._updateMounts(); + this._updateBookmarks(); + }, + + _updateDefaults: function() { + this._actionsBox.destroy_children(); + + let places = Main.placesManager.getDefaultPlaces(); + for (let i = 0; i < places.length; i++) + this._actionsBox.add(new DashPlaceDisplayItem(places[i]).actor); + }, + + _updateMounts: function() { + this._devBox.destroy_children(); + + let places = Main.placesManager.getMounts(); + for (let i = 0; i < places.length; i++) + this._devBox.add(new DashPlaceDisplayItem(places[i]).actor); + }, + + _updateBookmarks: function() { + this._dirsBox.destroy_children(); + + let places = Main.placesManager.getBookmarks(); + for (let i = 0; i < places.length; i ++) + this._dirsBox.add(new DashPlaceDisplayItem(places[i]).actor); + } +}; + +Signals.addSignalMethods(DashPlaceDisplay.prototype); + + +function PlaceDisplayItem(placeInfo) { + this._init(placeInfo); +} + +PlaceDisplayItem.prototype = { + __proto__: GenericDisplay.GenericDisplayItem.prototype, + + _init : function(placeInfo) { + GenericDisplay.GenericDisplayItem.prototype._init.call(this); + this._info = placeInfo; + + this._setItemInfo(placeInfo.name, ''); + }, + + //// Public method overrides //// + + // Opens an application represented by this display item. + launch : function() { + this._info.launch(); + }, + + shellWorkspaceLaunch: function() { + this._info.launch(); + }, + + //// Protected method overrides //// + + // Returns an icon for the item. + _createIcon: function() { + return this._info.iconFactory(GenericDisplay.ITEM_DISPLAY_ICON_SIZE); + }, + + // Returns a preview icon for the item. + _createPreviewIcon: function() { + return this._info.iconFactory(GenericDisplay.PREVIEW_ICON_SIZE); + } + +}; + +function PlaceDisplay(flags) { + this._init(flags); +} + +PlaceDisplay.prototype = { + __proto__: GenericDisplay.GenericDisplay.prototype, + + _init: function(flags) { + GenericDisplay.GenericDisplay.prototype._init.call(this, flags); + this._stale = true; + Main.placesManager.connect('places-updated', Lang.bind(this, function (e) { + this._stale = true; + })); + }, + + //// Protected method overrides //// + _refreshCache: function () { + if (!this._stale) + return true; + this._allItems = {}; + let array = Main.placesManager.getAllPlaces(); + for (let i = 0; i < array.length; i ++) { + // We are using an array id as placeInfo id because placeInfo doesn't have any + // other information piece that can be used as a unique id. There are different + // types of placeInfo, such as devices and directories that would result in differently + // structured ids. Also the home directory can show up in both the default places and in + // bookmarks which means its URI can't be used as a unique id. (This does mean it can + // appear twice in search results, though that doesn't happen at the moment because we + // name it "Home Folder" in default places and it's named with the user's system name + // if it appears as a bookmark.) + let placeInfo = array[i]; + placeInfo.id = i; + this._allItems[i] = placeInfo; + } + this._stale = false; + return false; + }, + + // Sets the list of the displayed items. + _setDefaultList: function() { + this._matchedItems = {}; + this._matchedItemKeys = []; + for (id in this._allItems) { + this._matchedItems[id] = 1; + this._matchedItemKeys.push(id); + } + this._matchedItemKeys.sort(Lang.bind(this, this._compareItems)); + }, + + // Checks if the item info can be a match for the search string by checking + // the name of the place. Item info is expected to be PlaceInfo. + // Returns a boolean flag indicating if itemInfo is a match. + _isInfoMatching: function(itemInfo, search) { + if (search == null || search == '') + return true; + + let name = itemInfo.name.toLowerCase(); + if (name.indexOf(search) >= 0) + return true; + + return false; + }, + + // Compares items associated with the item ids based on the alphabetical order + // of the item names. + // Returns an integer value indicating the result of the comparison. + _compareItems: function(itemIdA, itemIdB) { + let placeA = this._allItems[itemIdA]; + let placeB = this._allItems[itemIdB]; + return placeA.name.localeCompare(placeB.name); + }, + + // Creates a PlaceDisplayItem based on itemInfo, which is expected to be a PlaceInfo object. + _createDisplayItem: function(itemInfo) { + return new PlaceDisplayItem(itemInfo); + } +}; diff --git a/js/ui/places.js b/js/ui/places.js deleted file mode 100644 index 9aa604de5..000000000 --- a/js/ui/places.js +++ /dev/null @@ -1,321 +0,0 @@ -/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ - -const Big = imports.gi.Big; -const Clutter = imports.gi.Clutter; -const Pango = imports.gi.Pango; -const GLib = imports.gi.GLib; -const Gio = imports.gi.Gio; -const Shell = imports.gi.Shell; -const Lang = imports.lang; -const Mainloop = imports.mainloop; -const Signals = imports.signals; -const 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; - -/** - * An entry in the places menu. - * @name: String title - * @iconFactory: A JavaScript callback which will create an icon texture - * @onActivate: A JavaScript callback to launch the entry - */ -function PlaceDisplay(name, iconFactory, onActivate) { - this._init(name, iconFactory, onActivate); -} - -PlaceDisplay.prototype = { - _init : function(name, iconFactory, onActivate) { - this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, - reactive: true, - spacing: 4 }); - this.actor.connect('button-release-event', Lang.bind(this, function (b, e) { - onActivate(this); - Main.overview.hide(); - })); - let text = new Clutter.Text({ font_name: "Sans 14px", - ellipsize: Pango.EllipsizeMode.END, - color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR, - text: name }); - let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER }); - this._icon = iconFactory(); - iconBox.append(this._icon, Big.BoxPackFlags.NONE); - this.actor.append(iconBox, Big.BoxPackFlags.NONE); - this.actor.append(text, Big.BoxPackFlags.EXPAND); - - this._iconFactory = iconFactory; - this._onActivate = onActivate; - - this.actor._delegate = this; - let draggable = DND.makeDraggable(this.actor); - }, - - getDragActorSource: function() { - return this._icon; - }, - - getDragActor: function(stageX, stageY) { - return this._iconFactory(); - }, - - //// Drag and drop methods //// - - shellWorkspaceLaunch : function() { - this._onActivate(); - } -}; -Signals.addSignalMethods(PlaceDisplay.prototype); - -function Places() { - this._init(); -} - -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._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._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(); - let homeLabel = Shell.util_get_label_for_uri (homeUri); - let homeIcon = Shell.util_get_icon_for_uri (homeUri); - let home = new PlaceDisplay(homeLabel, - function() { - return Shell.TextureCache.get_default().load_gicon(homeIcon, PLACES_ICON_SIZE); - }, - function() { - Gio.app_info_launch_default_for_uri(homeUri, Main.createAppLaunchContext()); - }); - - 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._volumeMonitor = Gio.VolumeMonitor.get(); - this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices)); - this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices)); - this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices)); - this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices)); - this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices)); - this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices)); - this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices)); - this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices)); - this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices)); - this._updateDevices(); - - let networkApp = null; - try { - networkApp = Shell.AppSystem.get_default().load_from_desktop_file('gnome-network-scheme.desktop'); - } catch(e) { - try { - networkApp = Shell.AppSystem.get_default().load_from_desktop_file('network-scheme.desktop'); - } catch(e) { - log("Cannot create \"Network\" item, .desktop file not found or corrupt."); - } - } - - if (networkApp != null) { - let network = new PlaceDisplay(networkApp.get_name(), - function() { - return networkApp.create_icon_texture(PLACES_ICON_SIZE); - }, - function () { - networkApp.launch(); - }); - this._actionsBox.append(network.actor, Big.BoxPackFlags.NONE); - } - - 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._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); - let monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null); - this._bookmarkTimeoutId = 0; - monitor.connect('changed', Lang.bind(this, function () { - if (this._bookmarkTimeoutId > 0) - return; - /* Defensive event compression */ - this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () { - this._bookmarkTimeoutId = 0; - this._reloadBookmarks(); - return false; - })); - })); - - this._reloadBookmarks(); - }, - - _reloadBookmarks: function() { - - this._dirsBox.remove_all(); - - if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS)) - return; - - let [success, bookmarksContent, len] = GLib.file_get_contents(this._bookmarksPath); - - if (!success) - return; - - let bookmarks = bookmarksContent.split('\n'); - - let bookmarksToLabel = {}; - let bookmarksOrder = []; - for (let i = 0; i < bookmarks.length; i++) { - let bookmarkLine = bookmarks[i]; - let components = bookmarkLine.split(' '); - let bookmark = components[0]; - if (bookmark in bookmarksToLabel) - continue; - let label = null; - if (components.length > 1) - label = components.slice(1).join(' '); - bookmarksToLabel[bookmark] = label; - bookmarksOrder.push(bookmark); - } - - for (let i = 0; i < bookmarksOrder.length; i++) { - let bookmark = bookmarksOrder[i]; - let label = bookmarksToLabel[bookmark]; - let file = Gio.file_new_for_uri(bookmark); - if (!file.query_exists(null)) - continue; - if (label == null) - label = Shell.util_get_label_for_uri(bookmark); - if (label == null) - continue; - let icon = Shell.util_get_icon_for_uri(bookmark); - - let item = new PlaceDisplay(label, - function() { - return Shell.TextureCache.get_default().load_gicon(icon, PLACES_ICON_SIZE); - }, - function() { - Gio.app_info_launch_default_for_uri(bookmark, Main.createAppLaunchContext()); - }); - this._dirsBox.append(item.actor, Big.BoxPackFlags.NONE); - } - }, - - _updateDevices: function() { - this._devBox.remove_all(); - - /* first go through all connected drives */ - let drives = this._volumeMonitor.get_connected_drives(); - for (let i = 0; i < drives.length; i++) { - let volumes = drives[i].get_volumes(); - for(let j = 0; j < volumes.length; j++) { - let mount = volumes[j].get_mount(); - if(mount != null) { - this._addMount(mount); - } - } - } - - /* add all volumes that is not associated with a drive */ - let volumes = this._volumeMonitor.get_volumes(); - for(let i = 0; i < volumes.length; i++) { - if(volumes[i].get_drive() != null) - continue; - - let mount = volumes[i].get_mount(); - if(mount != null) { - this._addMount(mount); - } - } - - /* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */ - let mounts = this._volumeMonitor.get_mounts(); - for(let i = 0; i < mounts.length; i++) { - if(mounts[i].is_shadowed()) - continue; - - if(mounts[i].get_volume()) - continue; - - this._addMount(mounts[i]); - } - }, - - _addMount: function(mount) { - let mountLabel = mount.get_name(); - let mountIcon = mount.get_icon(); - let root = mount.get_root(); - let mountUri = root.get_uri(); - let devItem = new PlaceDisplay(mountLabel, - function() { - return Shell.TextureCache.get_default().load_gicon(mountIcon, PLACES_ICON_SIZE); - }, - function() { - 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); diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js index 1d998efff..8ff743ecc 100644 --- a/js/ui/shellDBus.js +++ b/js/ui/shellDBus.js @@ -48,6 +48,9 @@ GnomeShell.prototype = { let success; try { returnValue = JSON.stringify(eval(code)); + // A hack; DBus doesn't have null/undefined + if (returnValue == undefined) + returnValue = ""; success = true; } catch (e) { returnValue = JSON.stringify(e); diff --git a/js/ui/statusMenu.js b/js/ui/statusMenu.js new file mode 100644 index 000000000..3486ca0bc --- /dev/null +++ b/js/ui/statusMenu.js @@ -0,0 +1,297 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +const DBus = imports.dbus; +const Gdm = imports.gi.Gdm; +const GLib = imports.gi.GLib; +const Gtk = imports.gi.Gtk; +const Lang = imports.lang; +const Shell = imports.gi.Shell; +const St = imports.gi.St; +const Signals = imports.signals; +const Gettext = imports.gettext.domain('gnome-shell'); +const _ = Gettext.gettext; + +const Panel = imports.ui.panel; + +// Adapted from gdm/gui/user-switch-applet/applet.c +// +// Copyright (C) 2004-2005 James M. Cape . +// Copyright (C) 2008,2009 Red Hat, Inc. + +const SIDEBAR_VISIBLE_KEY = 'sidebar/visible'; + +function StatusMenu() { + this._init(); +} + +StatusMenu.prototype = { + _init: function() { + this._gdm = Gdm.UserManager.ref_default(); + this._user = this._gdm.get_user(GLib.get_user_name()); + this._presence = new GnomeSessionPresence(); + + this.actor = new St.BoxLayout({ name: 'StatusMenu' }); + this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); + + this._iconBox = new St.Bin(); + this.actor.add(this._iconBox, { y_align: St.Align.MIDDLE }); + + let textureCache = Shell.TextureCache.get_default(); + // FIXME: these icons are all wrong (likewise in createSubMenu) + this._availableIcon = textureCache.load_icon_name('gtk-yes', 16); + this._busyIcon = textureCache.load_icon_name('gtk-no', 16); + this._invisibleIcon = textureCache.load_icon_name('gtk-close', 16); + this._idleIcon = textureCache.load_icon_name('gtk-media-pause', 16); + + this._presence.connect('StatusChanged', Lang.bind(this, this._updatePresenceIcon)); + this._presence.getStatus(Lang.bind(this, this._updatePresenceIcon)); + + this._name = new St.Label({ text: this._user.get_real_name() }); + this.actor.add(this._name, { expand: true, y_align: St.Align.MIDDLE }); + this._userNameChangedId = this._user.connect('notify::display-name', Lang.bind(this, this._updateUserName)); + + this._createSubMenu(); + this._gdm.connect('users-loaded', Lang.bind(this, this._updateSwitchUser)); + this._gdm.connect('user-added', Lang.bind(this, this._updateSwitchUser)); + this._gdm.connect('user-removed', Lang.bind(this, this._updateSwitchUser)); + }, + + _onDestroy: function() { + this._user.disconnect(this._userNameChangedId); + }, + + _updateUserName: function() { + this._name.set_text(this._user.get_real_name()); + }, + + _updateSwitchUser: function() { + let users = this._gdm.list_users(); + if (users.length > 1) + this._loginScreenItem.show(); + else + this._loginScreenItem.hide(); + }, + + _updatePresenceIcon: function(presence, status) { + if (status == GnomeSessionPresenceStatus.AVAILABLE) + this._iconBox.child = this._availableIcon; + else if (status == GnomeSessionPresenceStatus.BUSY) + this._iconBox.child = this._busyIcon; + else if (status == GnomeSessionPresenceStatus.INVISIBLE) + this._iconBox.child = this._invisibleIcon; + else + this._iconBox.child = this._idleIcon; + }, + + // The menu + + _createImageMenuItem: function(label, iconName, forceIcon) { + let image = new Gtk.Image(); + let item = new Gtk.ImageMenuItem({ label: label, + image: image, + always_show_image: forceIcon == true }); + item.connect('style-set', Lang.bind(this, + function() { + image.set_from_icon_name(iconName, Gtk.IconSize.MENU); + })); + + return item; + }, + + _createSubMenu: function() { + this._menu = new Gtk.Menu(); + this._menu.connect('deactivate', Lang.bind(this, function() { this.emit('deactivated'); })); + + let item; + + item = this._createImageMenuItem(_('Available'), 'gtk-yes', true); + item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSessionPresenceStatus.AVAILABLE)); + this._menu.append(item); + item.show(); + + item = this._createImageMenuItem(_('Busy'), 'gtk-no', true); + item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSessionPresenceStatus.BUSY)); + this._menu.append(item); + item.show(); + + item = this._createImageMenuItem(_('Invisible'), 'gtk-close', true); + item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSessionPresenceStatus.INVISIBLE)); + this._menu.append(item); + item.show(); + + item = new Gtk.SeparatorMenuItem(); + this._menu.append(item); + item.show(); + + item = this._createImageMenuItem(_('Account Information...'), 'user-info'); + item.connect('activate', Lang.bind(this, this._onAccountInformationActivate)); + this._menu.append(item); + item.show(); + + let gconf = Shell.GConf.get_default(); + item = new Gtk.CheckMenuItem({ label: _('Sidebar'), + active: gconf.get_boolean(SIDEBAR_VISIBLE_KEY) }); + item.connect('activate', Lang.bind(this, + function() { + gconf.set_boolean(SIDEBAR_VISIBLE_KEY, this._sidebarItem.active); + })); + this._menu.append(item); + item.show(); + this._sidebarItem = item; + + item = this._createImageMenuItem(_('System Preferences...'), 'preferences-desktop'); + item.connect('activate', Lang.bind(this, this._onPreferencesActivate)); + this._menu.append(item); + item.show(); + + item = new Gtk.SeparatorMenuItem(); + this._menu.append(item); + item.show(); + + item = this._createImageMenuItem(_('Lock Screen'), 'system-lock-screen'); + item.connect('activate', Lang.bind(this, this._onLockScreenActivate)); + this._menu.append(item); + item.show(); + + item = this._createImageMenuItem(_('Switch User'), 'system-users'); + item.connect('activate', Lang.bind(this, this._onLoginScreenActivate)); + this._menu.append(item); + item.show(); + this._loginScreenItem = item; + + item = this._createImageMenuItem(_('Log Out...'), 'system-log-out'); + item.connect('activate', Lang.bind(this, this._onQuitSessionActivate)); + this._menu.append(item); + item.show(); + + item = this._createImageMenuItem(_('Shut Down...'), 'system-shutdown'); + item.connect('activate', Lang.bind(this, this._onShutDownActivate)); + this._menu.append(item); + item.show(); + }, + + _setPresenceStatus: function(item, status) { + this._presence.setStatus(status); + }, + + _onAccountInformationActivate: function() { + this._spawn(['gnome-about-me']); + }, + + _onPreferencesActivate: function() { + this._spawn(['gnome-control-center']); + }, + + _onLockScreenActivate: function() { + this._spawn(['gnome-screensaver-command', '--lock']); + }, + + _onLoginScreenActivate: function() { + this._gdm.goto_login_session(); + this._onLockScreenActivate(); + }, + + _onQuitSessionActivate: function() { + this._spawn(['gnome-session-save', '--logout-dialog']); + }, + + _onShutDownActivate: function() { + this._spawn(['gnome-session-save', '--shutdown-dialog']); + }, + + _spawn: function(args) { + // FIXME: once Shell.Process gets support for signalling + // errors we should pop up an error dialog or something here + // on failure + let p = new Shell.Process({'args' : args}); + p.run(); + }, + + // shell_status_menu_toggle: + // @event: event causing the toggle + // + // If the menu is not currently up, pops it up. Otherwise, hides it. + // Popping up may fail if another grab is already active; check with + // isActive(). + toggle: function(event) { + if (this._menu.visible) + this._menu.popdown(); + else { + // We don't want to overgrab a Mutter grab with the grab + // that GTK+ uses on menus. + if (global.display_is_grabbed()) + return; + + let [menuWidth, menuHeight] = this._menu.get_size_request (); + + let panel; + for (panel = this.actor; panel; panel = panel.get_parent()) { + if (panel._delegate instanceof Panel.Panel) + break; + } + + let [panelX, panelY] = panel.get_transformed_position(); + let [panelWidth, panelHeight] = panel.get_transformed_size(); + + let menuX = Math.round(panelX + panelWidth - menuWidth); + let menuY = Math.round(panelY + panelHeight); + + Shell.popup_menu(this._menu, event.get_button(), event.get_time(), + menuX, menuY); + } + }, + + // isActive: + // + // Gets whether the menu is currently popped up + // + // Return value: %true if the menu is currently popped up + isActive: function() { + return this._menu.visible; + } +}; +Signals.addSignalMethods(StatusMenu.prototype); + + +const GnomeSessionPresenceIface = { + name: 'org.gnome.SessionManager.Presence', + methods: [{ name: 'SetStatus', + inSignature: 'u' }], + properties: [{ name: 'status', + signature: 'u', + access: 'readwrite' }], + signals: [{ name: 'StatusChanged', + inSignature: 'u' }] +}; + +const GnomeSessionPresenceStatus = { + AVAILABLE: 0, + INVISIBLE: 1, + BUSY: 2, + IDLE: 3 +}; + +function GnomeSessionPresence() { + this._init(); +} + +GnomeSessionPresence.prototype = { + _init: function() { + DBus.session.proxifyObject(this, 'org.gnome.SessionManager', '/org/gnome/SessionManager/Presence', this); + this.connect('StatusChanged', Lang.bind(this, function (proxy, status) { this.status = status; })); + }, + + getStatus: function(callback) { + this.GetRemote('status', Lang.bind(this, + function(status, ex) { + if (!ex) + callback(this, status); + })); + }, + + setStatus: function(status) { + this.SetStatusRemote(status); + } +}; +DBus.proxifyPrototype(GnomeSessionPresence.prototype, GnomeSessionPresenceIface); + diff --git a/js/ui/workspaces.js b/js/ui/workspaces.js index 1cde8acce..8e4a60d97 100644 --- a/js/ui/workspaces.js +++ b/js/ui/workspaces.js @@ -575,6 +575,9 @@ Workspace.prototype = { this._lightbox.destroy(); this._lightbox = null; } + if (this._frame) { + this._frame.set_opacity(showLightbox ? 150 : 255); + } }, /** diff --git a/po/POTFILES.in b/po/POTFILES.in index a44a0f173..6e72d3962 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -4,7 +4,7 @@ js/ui/appIcon.js js/ui/dash.js js/ui/overview.js js/ui/panel.js -js/ui/places.js +js/ui/placeDisplay.js js/ui/runDialog.js js/ui/widget.js src/gdmuser/gdm-user.c diff --git a/po/ar.po b/po/ar.po index b593e3e3c..06cf8851a 100644 --- a/po/ar.po +++ b/po/ar.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: HEAD\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-10-13 15:24+0200\n" -"PO-Revision-Date: 2009-10-13 15:23+0300\n" +"POT-Creation-Date: 2009-11-08 20:55+0200\n" +"PO-Revision-Date: 2009-11-08 20:55+0300\n" "Last-Translator: Khaled Hosny \n" "Language-Team: Arabic \n" "MIME-Version: 1.0\n" @@ -16,7 +16,7 @@ msgstr "" "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" +"X-Generator: Virtaal 0.4.1\n" #: ../data/gnome-shell.desktop.in.in.h:1 msgid "GNOME Shell" @@ -26,68 +26,72 @@ msgstr "صدفة جنوم" msgid "Window management and application launching" msgstr "إدارة النوافذ وإطلاق التطبيقات" -#: ../js/ui/appDisplay.js:335 +#: ../js/ui/appDisplay.js:332 msgid "Frequent" msgstr "متكرر" -#: ../js/ui/appIcon.js:462 +#: ../js/ui/appDisplay.js:867 +msgid "Drag here to add favorites" +msgstr "اسحب إلى هنا ليضاف إلى المفضّلة" + +#: ../js/ui/appIcon.js:426 msgid "New Window" msgstr "نافذة جديدة" -#: ../js/ui/appIcon.js:475 +#: ../js/ui/appIcon.js:430 msgid "Remove from Favorites" msgstr "أزِل من المفضّلة" -#: ../js/ui/appIcon.js:476 +#: ../js/ui/appIcon.js:431 msgid "Add to Favorites" msgstr "أضِف إلى المفضّلة" -#: ../js/ui/dash.js:283 +#: ../js/ui/dash.js:267 msgid "Find..." msgstr "ابحث..." -#: ../js/ui/dash.js:400 +#: ../js/ui/dash.js:376 msgid "More" msgstr "المزيد" -#: ../js/ui/dash.js:543 +#: ../js/ui/dash.js:532 msgid "(see all)" msgstr "(انظر الكل)" #. **** Applications **** -#: ../js/ui/dash.js:763 ../js/ui/dash.js:825 +#: ../js/ui/dash.js:711 ../js/ui/dash.js:773 msgid "APPLICATIONS" msgstr "التطبيقات" #. **** Places **** #. Translators: This is in the sense of locations for documents, #. network locations, etc. -#: ../js/ui/dash.js:783 +#: ../js/ui/dash.js:731 msgid "PLACES" msgstr "الأماكن" #. **** Documents **** -#: ../js/ui/dash.js:790 ../js/ui/dash.js:835 +#: ../js/ui/dash.js:738 ../js/ui/dash.js:783 msgid "RECENT DOCUMENTS" msgstr "المستندات الحديثة" #. **** Search Results **** -#: ../js/ui/dash.js:815 ../js/ui/dash.js:955 +#: ../js/ui/dash.js:763 ../js/ui/dash.js:947 msgid "SEARCH RESULTS" msgstr "نتائج البحث" -#: ../js/ui/dash.js:830 +#: ../js/ui/dash.js:778 msgid "PREFERENCES" msgstr "التفضيلات" #. Button on the left side of the panel. #. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". -#: ../js/ui/panel.js:272 +#: ../js/ui/panel.js:274 msgid "Activities" msgstr "الأنشطة" #. Translators: This is a time format. -#: ../js/ui/panel.js:464 +#: ../js/ui/panel.js:491 msgid "%a %l:%M %p" msgstr "%A %Ol:%OM %p" @@ -105,23 +109,23 @@ msgid "Execution of '%s' failed:" msgstr "فشل تنفيذ '%s':‏" #. Translators: This is a time format. -#: ../js/ui/widget.js:162 +#: ../js/ui/widget.js:163 msgid "%H:%M" msgstr "%OH:%OM" -#: ../js/ui/widget.js:316 +#: ../js/ui/widget.js:317 msgid "Applications" msgstr "التطبيقات" -#: ../js/ui/widget.js:341 +#: ../js/ui/widget.js:339 msgid "Recent Documents" msgstr "المستندات الحديثة" -#: ../src/shell-global.c:812 +#: ../src/shell-global.c:821 msgid "Less than a minute ago" msgstr "منذ أقل من دقيقة" -#: ../src/shell-global.c:815 +#: ../src/shell-global.c:824 #, c-format msgid "%d minute ago" msgid_plural "%d minutes ago" @@ -132,7 +136,7 @@ msgstr[3] "منذ %d دقائق" msgstr[4] "منذ %d دقيقة" msgstr[5] "منذ %d دقيقة" -#: ../src/shell-global.c:818 +#: ../src/shell-global.c:827 #, c-format msgid "%d hour ago" msgid_plural "%d hours ago" @@ -143,7 +147,7 @@ msgstr[3] "منذ %d ساعات" msgstr[4] "منذ %d ساعة" msgstr[5] "منذ %d ساعة" -#: ../src/shell-global.c:821 +#: ../src/shell-global.c:830 #, c-format msgid "%d day ago" msgid_plural "%d days ago" @@ -154,7 +158,7 @@ msgstr[3] "منذ %d أيام" msgstr[4] "منذ %d يوما" msgstr[5] "منذ %d يوم" -#: ../src/shell-global.c:824 +#: ../src/shell-global.c:833 #, c-format msgid "%d week ago" msgid_plural "%d weeks ago" diff --git a/po/cs.po b/po/cs.po index d5b59f74a..76cd5e5a4 100644 --- a/po/cs.po +++ b/po/cs.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: gnome-shell\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-10-10 19:02+0200\n" -"PO-Revision-Date: 2009-10-10 19:02+0200\n" +"POT-Creation-Date: 2009-11-05 18:03+0100\n" +"PO-Revision-Date: 2009-11-05 18:03+0100\n" "Last-Translator: Petr Kovar \n" "Language-Team: Czech \n" "MIME-Version: 1.0\n" @@ -25,68 +25,72 @@ msgstr "Prostředí GNOME Shell" msgid "Window management and application launching" msgstr "Správa oken a spouštění aplikací" -#: ../js/ui/appDisplay.js:335 +#: ../js/ui/appDisplay.js:332 msgid "Frequent" msgstr "Časté" -#: ../js/ui/appIcon.js:462 +#: ../js/ui/appDisplay.js:867 +msgid "Drag here to add favorites" +msgstr "Oblíbené přidáte přetažením sem" + +#: ../js/ui/appIcon.js:426 msgid "New Window" msgstr "Nové okno" -#: ../js/ui/appIcon.js:475 +#: ../js/ui/appIcon.js:430 msgid "Remove from Favorites" msgstr "Odstranit z oblíbených" -#: ../js/ui/appIcon.js:476 +#: ../js/ui/appIcon.js:431 msgid "Add to Favorites" msgstr "Přidat mezi oblíbené" -#: ../js/ui/dash.js:283 +#: ../js/ui/dash.js:267 msgid "Find..." msgstr "Najít..." -#: ../js/ui/dash.js:400 +#: ../js/ui/dash.js:376 msgid "More" msgstr "Více" -#: ../js/ui/dash.js:543 +#: ../js/ui/dash.js:532 msgid "(see all)" msgstr "(zobrazit vše)" #. **** Applications **** -#: ../js/ui/dash.js:763 ../js/ui/dash.js:825 +#: ../js/ui/dash.js:711 ../js/ui/dash.js:773 msgid "APPLICATIONS" msgstr "APLIKACE" #. **** Places **** #. Translators: This is in the sense of locations for documents, #. network locations, etc. -#: ../js/ui/dash.js:783 +#: ../js/ui/dash.js:731 msgid "PLACES" msgstr "MÍSTA" #. **** Documents **** -#: ../js/ui/dash.js:790 ../js/ui/dash.js:835 +#: ../js/ui/dash.js:738 ../js/ui/dash.js:783 msgid "RECENT DOCUMENTS" msgstr "NEDÁVNÉ DOKUMENTY" #. **** Search Results **** -#: ../js/ui/dash.js:815 ../js/ui/dash.js:955 +#: ../js/ui/dash.js:763 ../js/ui/dash.js:947 msgid "SEARCH RESULTS" msgstr "VÝSLEDKY HLEDÁNÍ" -#: ../js/ui/dash.js:830 +#: ../js/ui/dash.js:778 msgid "PREFERENCES" msgstr "PŘEDVOLBY" #. Button on the left side of the panel. #. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". -#: ../js/ui/panel.js:272 +#: ../js/ui/panel.js:274 msgid "Activities" msgstr "Činnosti" #. Translators: This is a time format. -#: ../js/ui/panel.js:464 +#: ../js/ui/panel.js:491 msgid "%a %l:%M %p" msgstr "%a, %H:%M" @@ -104,23 +108,23 @@ msgid "Execution of '%s' failed:" msgstr "Vykonání \"%s\" selhalo:" #. Translators: This is a time format. -#: ../js/ui/widget.js:162 +#: ../js/ui/widget.js:163 msgid "%H:%M" msgstr "%H:%M" -#: ../js/ui/widget.js:316 +#: ../js/ui/widget.js:317 msgid "Applications" msgstr "Aplikace" -#: ../js/ui/widget.js:341 +#: ../js/ui/widget.js:339 msgid "Recent Documents" msgstr "Nedávné dokumenty" -#: ../src/shell-global.c:812 +#: ../src/shell-global.c:821 msgid "Less than a minute ago" msgstr "Před méně než minutou" -#: ../src/shell-global.c:815 +#: ../src/shell-global.c:824 #, c-format msgid "%d minute ago" msgid_plural "%d minutes ago" @@ -128,7 +132,7 @@ msgstr[0] "Před %d minutou" msgstr[1] "Před %d minutami" msgstr[2] "Před %d minutami" -#: ../src/shell-global.c:818 +#: ../src/shell-global.c:827 #, c-format msgid "%d hour ago" msgid_plural "%d hours ago" @@ -136,7 +140,7 @@ msgstr[0] "Před %d hodinou" msgstr[1] "Před %d hodinami" msgstr[2] "Před %d hodinami" -#: ../src/shell-global.c:821 +#: ../src/shell-global.c:830 #, c-format msgid "%d day ago" msgid_plural "%d days ago" @@ -144,7 +148,7 @@ msgstr[0] "Před %d dnem" msgstr[1] "Před %d dny" msgstr[2] "Před %d dny" -#: ../src/shell-global.c:824 +#: ../src/shell-global.c:833 #, c-format msgid "%d week ago" msgid_plural "%d weeks ago" diff --git a/po/fi.po b/po/fi.po new file mode 100644 index 000000000..9fb695280 --- /dev/null +++ b/po/fi.po @@ -0,0 +1,226 @@ +# gnome-shell Finnish translation +# Copyright (C) 2009 Timo Jyrinki +# This file is distributed under the same license as the gnome-shell package. +# Timo Jyrinki , 2009. +# +msgid "" +msgstr "" +"Project-Id-Version: gnome-shell\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-11-04 11:16+0200\n" +"PO-Revision-Date: 2009-11-04 11:16+0200\n" +"Last-Translator: Timo Jyrinki \n" +"Language-Team: Finnish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#: ../data/gnome-shell.desktop.in.in.h:1 +msgid "GNOME Shell" +msgstr "" + +#: ../data/gnome-shell.desktop.in.in.h:2 +msgid "Window management and application launching" +msgstr "Ikkunanhallinta ja sovelluksien käynnistäminen" + +#: ../js/ui/appDisplay.js:332 +msgid "Frequent" +msgstr "Usein käytetyt" + +#: ../js/ui/appDisplay.js:867 +msgid "Drag here to add favorites" +msgstr "Raahaa tähän lisätäksesi suosikkeihin" + +#: ../js/ui/appIcon.js:426 +msgid "New Window" +msgstr "Uusi ikkuna" + +#: ../js/ui/appIcon.js:430 +msgid "Remove from Favorites" +msgstr "Poista suosikeista" + +#: ../js/ui/appIcon.js:431 +msgid "Add to Favorites" +msgstr "Lisää suosikkeihin" + +#: ../js/ui/dash.js:283 +msgid "Find..." +msgstr "Etsi..." + +#: ../js/ui/dash.js:400 +msgid "More" +msgstr "Lisää" + +#: ../js/ui/dash.js:543 +msgid "(see all)" +msgstr "(näytä kaikki)" + +#. **** Applications **** +#: ../js/ui/dash.js:725 ../js/ui/dash.js:787 +msgid "APPLICATIONS" +msgstr "SOVELLUKSET" + +#. **** Places **** +#. Translators: This is in the sense of locations for documents, +#. network locations, etc. +#: ../js/ui/dash.js:745 +msgid "PLACES" +msgstr "SIJAINNIT" + +#. **** Documents **** +#: ../js/ui/dash.js:752 ../js/ui/dash.js:797 +msgid "RECENT DOCUMENTS" +msgstr "VIIMEISIMMÄT ASIAKIRJAT" + +#. **** Search Results **** +#: ../js/ui/dash.js:777 ../js/ui/dash.js:961 +msgid "SEARCH RESULTS" +msgstr "HAKUTULOKSET" + +#: ../js/ui/dash.js:792 +msgid "PREFERENCES" +msgstr "ASETUKSET" + +#. Button on the left side of the panel. +#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". +#: ../js/ui/panel.js:274 +msgid "Activities" +msgstr "Toiminnot" + +#. Translators: This is a time format. +#: ../js/ui/panel.js:491 +msgid "%a %l:%M %p" +msgstr "%a %I.%M" + +#: ../js/ui/places.js:178 +msgid "Connect to..." +msgstr "Yhdistä..." + +#: ../js/ui/runDialog.js:96 +msgid "Please enter a command:" +msgstr "Syötä komento:" + +#: ../js/ui/runDialog.js:173 +#, c-format +msgid "Execution of '%s' failed:" +msgstr "" + +#. Translators: This is a time format. +#: ../js/ui/widget.js:163 +msgid "%H:%M" +msgstr "%I.%M" + +#: ../js/ui/widget.js:317 +msgid "Applications" +msgstr "Sovellukset" + +#: ../js/ui/widget.js:339 +msgid "Recent Documents" +msgstr "Viimeisimmät asiakirjat" + +#: ../src/shell-global.c:821 +msgid "Less than a minute ago" +msgstr "Alle minuutti sitten" + +#: ../src/shell-global.c:824 +#, c-format +msgid "%d minute ago" +msgid_plural "%d minutes ago" +msgstr[0] "%d minuutti sitten" +msgstr[1] "%d minuuttia sitten" + +#: ../src/shell-global.c:827 +#, c-format +msgid "%d hour ago" +msgid_plural "%d hours ago" +msgstr[0] "%d tunti sitten" +msgstr[1] "%d tuntia sitten" + +#: ../src/shell-global.c:830 +#, c-format +msgid "%d day ago" +msgid_plural "%d days ago" +msgstr[0] "%d päivä sitten" +msgstr[1] "%d päivää sitten" + +#: ../src/shell-global.c:833 +#, c-format +msgid "%d week ago" +msgid_plural "%d weeks ago" +msgstr[0] "%d viikko sitten" +msgstr[1] "%d viikkoa sitten" + +#: ../src/shell-status-menu.c:156 +msgid "Unknown" +msgstr "Tuntematon" + +#: ../src/shell-status-menu.c:212 +#, c-format +msgid "Can't lock screen: %s" +msgstr "Näyttöä ei voi lukita: %s" + +#: ../src/shell-status-menu.c:227 +#, c-format +msgid "Can't temporarily set screensaver to blank screen: %s" +msgstr "" + +#: ../src/shell-status-menu.c:351 +#, c-format +msgid "Can't logout: %s" +msgstr "" + +#: ../src/shell-status-menu.c:492 +msgid "Account Information..." +msgstr "Käyttäjätilin tiedot..." + +#: ../src/shell-status-menu.c:502 +msgid "Sidebar" +msgstr "Sivupalkki" + +#: ../src/shell-status-menu.c:510 +msgid "System Preferences..." +msgstr "Järjestelmän asetukset" + +#: ../src/shell-status-menu.c:525 +msgid "Lock Screen" +msgstr "Lukitse näyttö" + +#: ../src/shell-status-menu.c:535 +msgid "Switch User" +msgstr "Vaihda käyttäjää" + +#. Only show switch user if there are other users +#. Log Out +#: ../src/shell-status-menu.c:546 +msgid "Log Out..." +msgstr "Kirjaudu ulos..." + +#. Shut down +#: ../src/shell-status-menu.c:557 +msgid "Shut Down..." +msgstr "Sammuta..." + +#: ../src/shell-uri-util.c:87 +msgid "Home Folder" +msgstr "Kotikansio" + +#. Translators: this is the same string as the one found in +#. * nautilus +#: ../src/shell-uri-util.c:102 +msgid "File System" +msgstr "Tiedostojärjestelmä" + +#: ../src/shell-uri-util.c:248 +msgid "Search" +msgstr "Haku" + +#. Translators: the first string is the name of a gvfs +#. * method, and the second string is a path. For +#. * example, "Trash: some-directory". It means that the +#. * directory called "some-directory" is in the trash. +#. +#: ../src/shell-uri-util.c:298 +#, c-format +msgid "%1$s: %2$s" +msgstr "%1$s: %2$s" diff --git a/po/it.po b/po/it.po index cdc36bb9f..ff145d7b7 100644 --- a/po/it.po +++ b/po/it.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: gnome-shell\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-10-10 12:20+0200\n" -"PO-Revision-Date: 2009-10-10 12:39+0200\n" +"POT-Creation-Date: 2009-11-03 22:26+0100\n" +"PO-Revision-Date: 2009-11-03 22:28+0100\n" "Last-Translator: Milo Casagrande \n" "Language-Team: Italian \n" "MIME-Version: 1.0\n" @@ -24,19 +24,23 @@ msgstr "GNOME Shell" msgid "Window management and application launching" msgstr "Gestione finestre e avvio applicazioni" -#: ../js/ui/appDisplay.js:335 +#: ../js/ui/appDisplay.js:332 msgid "Frequent" msgstr "Frequente" -#: ../js/ui/appIcon.js:462 +#: ../js/ui/appDisplay.js:867 +msgid "Drag here to add favorites" +msgstr "Trascinare qui per aggiungere ai preferiti" + +#: ../js/ui/appIcon.js:426 msgid "New Window" msgstr "Nuova finestra" -#: ../js/ui/appIcon.js:475 +#: ../js/ui/appIcon.js:430 msgid "Remove from Favorites" msgstr "Rimuovi dai preferiti" -#: ../js/ui/appIcon.js:476 +#: ../js/ui/appIcon.js:431 msgid "Add to Favorites" msgstr "Aggiungi ai preferiti" @@ -53,40 +57,40 @@ msgid "(see all)" msgstr "(vedi tutto)" #. **** Applications **** -#: ../js/ui/dash.js:763 ../js/ui/dash.js:825 +#: ../js/ui/dash.js:725 ../js/ui/dash.js:787 msgid "APPLICATIONS" msgstr "Applicazioni" #. **** Places **** #. Translators: This is in the sense of locations for documents, #. network locations, etc. -#: ../js/ui/dash.js:783 +#: ../js/ui/dash.js:745 msgid "PLACES" msgstr "Risorse" #. **** Documents **** -#: ../js/ui/dash.js:790 ../js/ui/dash.js:835 +#: ../js/ui/dash.js:752 ../js/ui/dash.js:797 msgid "RECENT DOCUMENTS" msgstr "Documenti recenti" #. **** Search Results **** -#: ../js/ui/dash.js:815 ../js/ui/dash.js:955 +#: ../js/ui/dash.js:777 ../js/ui/dash.js:961 msgid "SEARCH RESULTS" msgstr "Risultati ricerca" -#: ../js/ui/dash.js:830 +#: ../js/ui/dash.js:792 msgid "PREFERENCES" msgstr "Preferenze" #. Button on the left side of the panel. #. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". -#: ../js/ui/panel.js:272 +#: ../js/ui/panel.js:274 msgid "Activities" msgstr "Attività" # (ndt) proviamo col k, se non funge, sappiamo il perché... #. Translators: This is a time format. -#: ../js/ui/panel.js:464 +#: ../js/ui/panel.js:491 msgid "%a %l:%M %p" msgstr "%a %k.%M" @@ -104,44 +108,44 @@ msgid "Execution of '%s' failed:" msgstr "Esecuzione di «%s» non riuscita:" #. Translators: This is a time format. -#: ../js/ui/widget.js:162 +#: ../js/ui/widget.js:163 msgid "%H:%M" msgstr "%k.%M" -#: ../js/ui/widget.js:316 +#: ../js/ui/widget.js:317 msgid "Applications" msgstr "Applicazioni" -#: ../js/ui/widget.js:341 +#: ../js/ui/widget.js:339 msgid "Recent Documents" msgstr "Documenti recenti" -#: ../src/shell-global.c:812 +#: ../src/shell-global.c:821 msgid "Less than a minute ago" msgstr "Meno di un minuto fa" -#: ../src/shell-global.c:815 +#: ../src/shell-global.c:824 #, c-format msgid "%d minute ago" msgid_plural "%d minutes ago" msgstr[0] "%d minuto fa" msgstr[1] "%d minuti fa" -#: ../src/shell-global.c:818 +#: ../src/shell-global.c:827 #, c-format msgid "%d hour ago" msgid_plural "%d hours ago" msgstr[0] "%d ora fa" msgstr[1] "%d ore fa" -#: ../src/shell-global.c:821 +#: ../src/shell-global.c:830 #, c-format msgid "%d day ago" msgid_plural "%d days ago" msgstr[0] "%d giorno fa" msgstr[1] "%d giorni fa" -#: ../src/shell-global.c:824 +#: ../src/shell-global.c:833 #, c-format msgid "%d week ago" msgid_plural "%d weeks ago" @@ -225,18 +229,3 @@ msgstr "Cerca" #, c-format msgid "%1$s: %2$s" msgstr "%1$s: %2$s" - -#~ msgid "Browse" -#~ msgstr "Esplora" - -# (ndt) è da valutare se è troppo lunga, è in una casella di ricerca -#~ msgid "Find apps or documents" -#~ msgstr "Trova programmi e documenti" - -# (ndt) no idea... -#~ msgid "Manager" -#~ msgstr "Manager" - -# (ndt) no idea... -#~ msgid "The user manager object this user is controlled by." -#~ msgstr "L'oggetto user manager che controlla questo utente." diff --git a/po/sl.po b/po/sl.po index 6ae049c58..e55ddadff 100644 --- a/po/sl.po +++ b/po/sl.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: gnome-shell master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=general\n" -"POT-Creation-Date: 2009-10-23 17:29+0000\n" -"PO-Revision-Date: 2009-10-26 07:29+0100\n" +"POT-Creation-Date: 2009-11-12 21:33+0000\n" +"PO-Revision-Date: 2009-11-14 10:19+0100\n" "Last-Translator: Matej Urbančič \n" "Language-Team: Slovenian GNOME Translation Team \n" "MIME-Version: 1.0\n" @@ -27,60 +27,53 @@ msgstr "Gnome lupina" msgid "Window management and application launching" msgstr "Upravljanje oken in zaganjanje programov" -#: ../js/ui/appDisplay.js:332 -msgid "Frequent" -msgstr "Pogosto" +#: ../js/ui/appDisplay.js:696 +msgid "Drag here to add favorites" +msgstr "S potegom na to mesto se izbor doda med priljubljene" -#: ../js/ui/appIcon.js:426 +#: ../js/ui/appIcon.js:425 msgid "New Window" msgstr "Novo okno" -#: ../js/ui/appIcon.js:430 +#: ../js/ui/appIcon.js:429 msgid "Remove from Favorites" msgstr "Odstrani iz priljubljenih" -#: ../js/ui/appIcon.js:431 +#: ../js/ui/appIcon.js:430 msgid "Add to Favorites" msgstr "Dodaj med priljubljene" -#: ../js/ui/dash.js:283 +#: ../js/ui/dash.js:237 msgid "Find..." msgstr "Poišči ..." -#: ../js/ui/dash.js:400 -msgid "More" -msgstr "Več" - -#: ../js/ui/dash.js:543 -msgid "(see all)" -msgstr "(poglej vse)" - #. **** Applications **** -#: ../js/ui/dash.js:725 -#: ../js/ui/dash.js:787 +#: ../js/ui/dash.js:656 +#: ../js/ui/dash.js:718 msgid "APPLICATIONS" msgstr "Programi" #. **** Places **** #. Translators: This is in the sense of locations for documents, #. network locations, etc. -#: ../js/ui/dash.js:745 +#: ../js/ui/dash.js:676 +#: ../js/ui/dash.js:733 msgid "PLACES" msgstr "Mesta" #. **** Documents **** -#: ../js/ui/dash.js:752 -#: ../js/ui/dash.js:797 +#: ../js/ui/dash.js:683 +#: ../js/ui/dash.js:728 msgid "RECENT DOCUMENTS" msgstr "Nedavni dokumenti" #. **** Search Results **** -#: ../js/ui/dash.js:777 -#: ../js/ui/dash.js:961 +#: ../js/ui/dash.js:708 +#: ../js/ui/dash.js:898 msgid "SEARCH RESULTS" msgstr "Rezultati iskanja" -#: ../js/ui/dash.js:792 +#: ../js/ui/dash.js:723 msgid "PREFERENCES" msgstr "Lastnosti" @@ -95,13 +88,13 @@ msgstr "Dejavnosti" msgid "%a %l:%M %p" msgstr "%a, %H:%M" -#: ../js/ui/places.js:178 +#: ../js/ui/placeDisplay.js:84 msgid "Connect to..." msgstr "Povezava z ..." #: ../js/ui/runDialog.js:96 msgid "Please enter a command:" -msgstr "Prosim, vnesite ukaz:" +msgstr "Vnos ukaza:" #: ../js/ui/runDialog.js:173 #, c-format @@ -121,11 +114,11 @@ msgstr "Programi" msgid "Recent Documents" msgstr "Nedavni dokumenti" -#: ../src/shell-global.c:812 +#: ../src/shell-global.c:821 msgid "Less than a minute ago" msgstr "Pred manj kot eno minuto" -#: ../src/shell-global.c:815 +#: ../src/shell-global.c:824 #, c-format msgid "%d minute ago" msgid_plural "%d minutes ago" @@ -134,7 +127,7 @@ msgstr[1] "Pred %d minuto" msgstr[2] "Pred %d minutama" msgstr[3] "Pred %d minutami" -#: ../src/shell-global.c:818 +#: ../src/shell-global.c:827 #, c-format msgid "%d hour ago" msgid_plural "%d hours ago" @@ -143,7 +136,7 @@ msgstr[1] "Pred %d uro" msgstr[2] "Pred %d urama" msgstr[3] "Pred %d urami" -#: ../src/shell-global.c:821 +#: ../src/shell-global.c:830 #, c-format msgid "%d day ago" msgid_plural "%d days ago" @@ -152,7 +145,7 @@ msgstr[1] "Pred %d dnevom" msgstr[2] "Pred %d dnevoma" msgstr[3] "Pred %d dnevi" -#: ../src/shell-global.c:824 +#: ../src/shell-global.c:833 #, c-format msgid "%d week ago" msgid_plural "%d weeks ago" @@ -235,6 +228,12 @@ msgstr "Iskanje" msgid "%1$s: %2$s" msgstr "%1$s: %2$s" +#~ msgid "Frequent" +#~ msgstr "Pogosto" +#~ msgid "More" +#~ msgstr "Več" +#~ msgid "(see all)" +#~ msgstr "(poglej vse)" #~ msgid "Browse" #~ msgstr "Prebrskaj" diff --git a/src/Makefile.am b/src/Makefile.am index 3ed60b43c..4b11e83f3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -87,8 +87,6 @@ libgnome_shell_la_SOURCES = \ shell-global.c \ shell-global.h \ shell-global-private.h \ - shell-status-menu.c \ - shell-status-menu.h \ shell-stack.c \ shell-stack.h \ shell-tray-manager.c \ @@ -165,7 +163,7 @@ libgnome_shell_la_LIBADD = \ libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags) typelibdir = $(pkglibdir) -typelib_DATA = Shell-0.1.typelib Big-1.0.typelib St-1.0.typelib +typelib_DATA = Shell-0.1.typelib Big-1.0.typelib St-1.0.typelib Gdm-1.0.typelib Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir St-1.0.gir libgnome-shell.la Makefile $(AM_V_GEN) $(G_IR_SCANNER) \ @@ -237,3 +235,22 @@ St-1.0.typelib: St-1.0.gir $< -o $@ CLEANFILES += St-1.0.typelib + +Gdm-1.0.gir: $(mutter) $(G_IR_SCANNER) libgdmuser-1.0.la Makefile + $(AM_V_GEN) $(G_IR_SCANNER) \ + --namespace=Gdm \ + --nsversion=1.0 \ + --include=GObject-2.0 \ + --include=GdkPixbuf-2.0 \ + --libtool="$(LIBTOOL)" \ + --library=libgdmuser-1.0.la \ + $(addprefix $(srcdir)/,$(gdmuser_source_h)) \ + $(addprefix $(srcdir)/,$(gdmuser_source_c)) \ + $(gdmuser_cflags) \ + -o $@ +CLEANFILES += Gdm-1.0.gir + +Gdm-1.0.typelib: libbig-1.0.la Gdm-1.0.gir + $(AM_V_GEN) $(G_IR_COMPILER) Gdm-1.0.gir -o $@ +CLEANFILES += Gdm-1.0.typelib + diff --git a/src/gdmuser/gdm-user-manager.c b/src/gdmuser/gdm-user-manager.c index 167ca7d55..e636b3564 100644 --- a/src/gdmuser/gdm-user-manager.c +++ b/src/gdmuser/gdm-user-manager.c @@ -998,7 +998,7 @@ gdm_user_manager_get_user (GdmUserManager *manager, GdmUser * gdm_user_manager_get_user_by_uid (GdmUserManager *manager, - uid_t uid) + gulong uid) { GdmUser *user; struct passwd *pwent; diff --git a/src/gdmuser/gdm-user-manager.h b/src/gdmuser/gdm-user-manager.h index 6c696e585..bb1118090 100644 --- a/src/gdmuser/gdm-user-manager.h +++ b/src/gdmuser/gdm-user-manager.h @@ -75,7 +75,7 @@ GSList * gdm_user_manager_list_users (GdmUserManager *mana GdmUser * gdm_user_manager_get_user (GdmUserManager *manager, const char *user_name); GdmUser * gdm_user_manager_get_user_by_uid (GdmUserManager *manager, - uid_t uid); + gulong uid); gboolean gdm_user_manager_activate_user_session (GdmUserManager *manager, GdmUser *user); diff --git a/src/gdmuser/gdm-user.c b/src/gdmuser/gdm-user.c index 4accb56ed..940c6877b 100644 --- a/src/gdmuser/gdm-user.c +++ b/src/gdmuser/gdm-user.c @@ -516,7 +516,7 @@ _gdm_user_icon_changed (GdmUser *user) * Since: 1.0 **/ -uid_t +gulong gdm_user_get_uid (GdmUser *user) { g_return_val_if_fail (GDM_IS_USER (user), -1); diff --git a/src/gdmuser/gdm-user.h b/src/gdmuser/gdm-user.h index 4edd3baf4..ee5dc509b 100644 --- a/src/gdmuser/gdm-user.h +++ b/src/gdmuser/gdm-user.h @@ -39,7 +39,7 @@ typedef struct _GdmUser GdmUser; GType gdm_user_get_type (void) G_GNUC_CONST; -uid_t gdm_user_get_uid (GdmUser *user); +gulong gdm_user_get_uid (GdmUser *user); G_CONST_RETURN char *gdm_user_get_user_name (GdmUser *user); G_CONST_RETURN char *gdm_user_get_real_name (GdmUser *user); G_CONST_RETURN char *gdm_user_get_home_directory (GdmUser *user); diff --git a/src/gnome-shell.in b/src/gnome-shell.in old mode 100644 new mode 100755 index 2acc967ed..b009ec495 --- a/src/gnome-shell.in +++ b/src/gnome-shell.in @@ -53,9 +53,7 @@ def start_xephyr(): time.sleep(1) # Start some windows in our session. - subprocess.Popen(["xterm", "-geometry", "+30+30"]) - subprocess.Popen(["xlogo", "-geometry", "-0-0"]) - subprocess.Popen(["xeyes", "-geometry", "-0+30"]) + subprocess.Popen(["gnome-terminal"]) return xephyr; @@ -212,6 +210,8 @@ parser.add_option("", "--geometry", metavar="GEOMETRY", default="1024x768"); parser.add_option("-w", "--wide", action="store_true", help="Use widescreen (1280x800) with Xephyr") +parser.add_option("", "--eval-file", metavar="EVAL_FILE", + help="Evaluate the contents of the given JavaScript file") options, args = parser.parse_args() @@ -219,6 +219,20 @@ if args: parser.print_usage() sys.exit(1) +if options.eval_file: + import dbus + + f = open(options.eval_file) + contents = f.read() + f.close() + + session = dbus.SessionBus() + shell = session.get_object('org.gnome.Shell', '/org/gnome/Shell') + shell = dbus.Interface(shell, 'org.gnome.Shell') + result = shell.Eval(contents) + print result + sys.exit(0) + if options.debug_command: options.debug = True elif options.debug: diff --git a/src/shell-app-system.c b/src/shell-app-system.c index 898fd0f95..2ec877472 100644 --- a/src/shell-app-system.c +++ b/src/shell-app-system.c @@ -740,6 +740,8 @@ shell_app_info_get_name (ShellAppInfo *info) { char *title; g_object_get (info->window, "title", &title, NULL); + if (!title) + title = g_strdup (""); return title; } } diff --git a/src/shell-global.c b/src/shell-global.c index b07d0905f..5bb7e69d4 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -1026,3 +1026,36 @@ shell_get_event_state (ClutterEvent *event) ClutterModifierType state = clutter_event_get_state (event); return state & CLUTTER_MODIFIER_MASK; } + +static void +shell_popup_menu_position_func (GtkMenu *menu, + int *x, + int *y, + gboolean *push_in, + gpointer user_data) +{ + *x = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu), "shell-menu-x")); + *y = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu), "shell-menu-y")); +} + +/** + * shell_popup_menu: + * @menu: a #GtkMenu + * @button: mouse button that triggered the menu + * @time: timestamp of event that triggered the menu + * @menu_x: x coordinate to display the menu at + * @menu_y: y coordinate to display the menu at + * + * Wraps gtk_menu_popup(), but using @menu_x, @menu_y for the location + * rather than needing a callback. + **/ +void +shell_popup_menu (GtkMenu *menu, int button, guint32 time, + int menu_x, int menu_y) +{ + g_object_set_data (G_OBJECT (menu), "shell-menu-x", GINT_TO_POINTER (menu_x)); + g_object_set_data (G_OBJECT (menu), "shell-menu-y", GINT_TO_POINTER (menu_y)); + + gtk_menu_popup (menu, NULL, NULL, shell_popup_menu_position_func, NULL, + button, time); +} diff --git a/src/shell-global.h b/src/shell-global.h index 55844a3a4..60b9cb552 100644 --- a/src/shell-global.h +++ b/src/shell-global.h @@ -79,6 +79,10 @@ GdkModifierType shell_global_get_modifier_keys (ShellGlobal *global); ClutterModifierType shell_get_event_state (ClutterEvent *event); +void shell_popup_menu (GtkMenu *menu, int button, guint32 time, + int menu_x, int menu_y); + + G_END_DECLS #endif /* __SHELL_GLOBAL_H__ */ diff --git a/src/shell-status-menu.c b/src/shell-status-menu.c deleted file mode 100644 index 4018faf3c..000000000 --- a/src/shell-status-menu.c +++ /dev/null @@ -1,732 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* Adapted from gdm/gui/user-switch-applet/applet.c */ -/* - * - * Copyright (C) 2004-2005 James M. Cape . - * Copyright (C) 2008,2009 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "shell-status-menu.h" - -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include - -#define GDMUSER_I_KNOW_THIS_IS_UNSTABLE -#include - -#include "shell-global.h" -#include "shell-gconf.h" - -#define LOCKDOWN_DIR "/desktop/gnome/lockdown" -#define LOCKDOWN_KEY LOCKDOWN_DIR "/disable_user_switching" - -#define SIDEBAR_VISIBLE_KEY SHELL_GCONF_DIR "/sidebar/visible" - -struct _ShellStatusMenuPrivate { - GConfClient *client; - GdmUserManager *manager; - GdmUser *user; - - ClutterTexture *user_icon; - BigBox *name_box; - ClutterText *name; - - GtkWidget *menu; - GtkWidget *account_item; - GtkWidget *sidebar_item; - GtkWidget *control_panel_item; - GtkWidget *lock_screen_item; - GtkWidget *login_screen_item; - GtkWidget *quit_session_item; - GtkWidget *shut_down_item; - - guint client_notify_lockdown_id; - - guint current_status_state; - - guint user_icon_changed_id; - guint user_notify_id; - - gboolean has_other_users; - - GtkIconSize icon_size; - guint pixel_size; -}; - -enum { - PROP_0 -}; - -G_DEFINE_TYPE(ShellStatusMenu, shell_status_menu, BIG_TYPE_BOX); - -/* Signals */ -enum -{ - DEACTIVATED, - LAST_SIGNAL -}; - -static guint shell_status_menu_signals [LAST_SIGNAL] = { 0 }; - -static void -reset_icon (ShellStatusMenu *status) -{ - ShellStatusMenuPrivate *priv = status->priv; - GdkPixbuf *pixbuf; - - if (priv->user == NULL) - return; - - if (priv->user_icon != NULL) - { - pixbuf = gdm_user_render_icon (priv->user, 24); - - if (pixbuf == NULL) - return; - - shell_clutter_texture_set_from_pixbuf (priv->user_icon, pixbuf); - - g_object_unref (pixbuf); - } -} - -static void -update_name_text (ShellStatusMenu *status) -{ - ShellStatusMenuPrivate *priv = status->priv; - - clutter_text_set_text (priv->name, - gdm_user_get_real_name (GDM_USER (priv->user))); -} - -static void -on_user_icon_changed (GdmUser *user, - ShellStatusMenu *status) -{ - g_debug ("User icon changed"); - reset_icon (status); -} - -static void -user_notify_display_name_cb (GObject *object, - GParamSpec *pspec, - ShellStatusMenu *status) -{ - update_name_text (status); -} - -static void -setup_current_user (ShellStatusMenu *status) -{ - ShellStatusMenuPrivate *priv = status->priv; - const char *name; - - priv->user = gdm_user_manager_get_user_by_uid (priv->manager, getuid ()); - if (priv->user != NULL) - { - g_object_ref (priv->user); - name = gdm_user_get_real_name (priv->user); - } - else - { - name = _("Unknown"); - } - - update_name_text (status); - - if (priv->user != NULL) - { - reset_icon (status); - - priv->user_icon_changed_id = - g_signal_connect (priv->user, - "icon-changed", - G_CALLBACK (on_user_icon_changed), - status); - priv->user_notify_id = - g_signal_connect (priv->user, - "notify::display-name", - G_CALLBACK (user_notify_display_name_cb), - status); - } -} - -static void -maybe_lock_screen (ShellStatusMenu *status) -{ - char *args[3]; - GError *err; - GdkScreen *screen; - gboolean use_gscreensaver = TRUE; - gboolean res; - - g_debug ("Attempting to lock screen"); - - args[0] = g_find_program_in_path ("gnome-screensaver-command"); - if (args[0] == NULL) - { - args[0] = g_find_program_in_path ("xscreensaver-command"); - use_gscreensaver = FALSE; - } - - if (args[0] == NULL) - return; - - if (use_gscreensaver) - args[1] = "--lock"; - else - args[1] = "-lock"; - args[2] = NULL; - - screen = gdk_screen_get_default (); - - err = NULL; - res = gdk_spawn_on_screen (screen, g_get_home_dir (), args, NULL, 0, NULL, - NULL, NULL, &err); - if (!res) - { - g_warning (_("Can't lock screen: %s"), err->message); - g_error_free (err); - } - - if (use_gscreensaver) - args[1] = "--throttle"; - else - args[1] = "-throttle"; - - err = NULL; - res = gdk_spawn_on_screen (screen, g_get_home_dir (), args, NULL, - (G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL), NULL, NULL, - NULL, &err); - if (!res) - { - g_warning (_("Can't temporarily set screensaver to blank screen: %s"), - err->message); - g_error_free (err); - } - - g_free (args[0]); -} - -static void -on_lock_screen_activate (GtkMenuItem *item, - ShellStatusMenu *status) -{ - maybe_lock_screen (status); -} - -static void -do_switch (ShellStatusMenu *status, - GdmUser *user) -{ - ShellStatusMenuPrivate *priv = status->priv; - guint num_sessions; - - g_debug ("Do user switch"); - - if (user == NULL) - { - gdm_user_manager_goto_login_session (priv->manager); - goto out; - } - - num_sessions = gdm_user_get_num_sessions (user); - if (num_sessions > 0) - gdm_user_manager_activate_user_session (priv->manager, user); - else - gdm_user_manager_goto_login_session (priv->manager); -out: - maybe_lock_screen (status); -} - -static void -on_login_screen_activate (GtkMenuItem *item, - ShellStatusMenu *status) -{ - GdmUser *user; - - user = NULL; - - do_switch (status, user); -} - -static void -spawn_external (ShellStatusMenu *status, const char *program) -{ - char *args[2]; - GError *error; - GdkScreen *screen; - gboolean res; - - args[0] = g_find_program_in_path (program); - if (args[0] == NULL) - return; - args[1] = NULL; - - screen = gdk_screen_get_default (); - - error = NULL; - res = gdk_spawn_on_screen (screen, g_get_home_dir (), args, NULL, 0, NULL, - NULL, NULL, &error); - if (!res) - { - g_warning ("Failed to exec %s: %s", program, error->message); - g_clear_error (&error); - } - - g_free (args[0]); - -} - -static void -on_control_panel_activate (GtkMenuItem *item, - ShellStatusMenu *status) -{ - spawn_external (status, "gnome-control-center"); -} - -static void -on_account_activate (GtkMenuItem *item, - ShellStatusMenu *status) -{ - spawn_external (status, "gnome-about-me"); -} - -static void -on_sidebar_toggled (GtkCheckMenuItem *item, - ShellStatusMenu *status) -{ - gconf_client_set_bool (status->priv->client, SIDEBAR_VISIBLE_KEY, - gtk_check_menu_item_get_active (item), NULL); -} - - -/* Calls 'gnome-session-save arg' */ -static void -gnome_session_save_command (const char *arg) -{ - char *args[3]; - GError *error; - GdkScreen *screen; - gboolean res; - - args[0] = g_find_program_in_path ("gnome-session-save"); - if (args[0] == NULL) - return; - - args[1] = (char *)arg; - args[2] = NULL; - - screen = gdk_screen_get_default (); - - error = NULL; - res = gdk_spawn_on_screen (screen, g_get_home_dir (), args, NULL, 0, NULL, - NULL, NULL, &error); - if (!res) - { - g_warning (_("Can't logout: %s"), error->message); - g_error_free (error); - } - - g_free (args[0]); -} - - -static void -on_quit_session_activate (GtkMenuItem *item, - ShellStatusMenu *status) -{ - gnome_session_save_command ("--logout-dialog"); -} - -static void -on_shut_down_activate (GtkMenuItem *item, - ShellStatusMenu *status) -{ - gnome_session_save_command ("--shutdown-dialog"); -} - -static void -update_switch_user (ShellStatusMenu *status) -{ - ShellStatusMenuPrivate *priv = status->priv; - GSList *users; - - users = gdm_user_manager_list_users (priv->manager); - priv->has_other_users = FALSE; - if (users != NULL) - priv->has_other_users = g_slist_length (users) > 1; - g_slist_free (users); - - if (priv->has_other_users) - gtk_widget_show (priv->login_screen_item); - else - gtk_widget_hide (priv->login_screen_item); -} - -static void -on_manager_user_added (GdmUserManager *manager, - GdmUser *user, - ShellStatusMenu *status) -{ - update_switch_user (status); -} - -static void -on_manager_user_removed (GdmUserManager *manager, - GdmUser *user, - ShellStatusMenu *status) -{ - update_switch_user (status); -} - -static void -on_manager_users_loaded (GdmUserManager *manager, - ShellStatusMenu *status) -{ - update_switch_user (status); -} - -static void -menu_style_set_cb (GtkWidget *menu, GtkStyle *old_style, - ShellStatusMenu *status) -{ - ShellStatusMenuPrivate *priv = status->priv; - GtkSettings *settings; - int width; - int height; - - priv->icon_size = gtk_icon_size_from_name ("panel-menu"); - if (priv->icon_size == GTK_ICON_SIZE_INVALID) - priv->icon_size = gtk_icon_size_register ("panel-menu", 24, 24); - - if (gtk_widget_has_screen (menu)) - settings = gtk_settings_get_for_screen (gtk_widget_get_screen (menu)); - else - settings = gtk_settings_get_default (); - - if (!gtk_icon_size_lookup_for_settings (settings, priv->icon_size, &width, - &height)) - priv->pixel_size = -1; - else - priv->pixel_size = MAX(width, height); -} - -static void -menuitem_style_set_cb (GtkWidget *menuitem, - GtkStyle *old_style, - ShellStatusMenu *status) -{ - GtkWidget *image; - const char *icon_name; - ShellStatusMenuPrivate *priv = status->priv; - - if (menuitem == priv->login_screen_item) - icon_name = "system-users"; - else if (menuitem == priv->lock_screen_item) - icon_name = "system-lock-screen"; - else if (menuitem == priv->quit_session_item) - icon_name = "system-log-out"; - else if (menuitem == priv->account_item) - icon_name = "user-info"; - else if (menuitem == priv->control_panel_item) - icon_name = "preferences-desktop"; - else if (menuitem == priv->shut_down_item) - icon_name = "system-shutdown"; - else - icon_name = GTK_STOCK_MISSING_IMAGE; - - image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (menuitem)); - gtk_image_set_pixel_size (GTK_IMAGE (image), priv->pixel_size); - gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, priv->icon_size); -} - -static void -on_deactivate (GtkMenuShell *menushell, gpointer user_data) -{ - ShellStatusMenu *status = SHELL_STATUS_MENU (user_data); - g_signal_emit (G_OBJECT (status), shell_status_menu_signals[DEACTIVATED], 0); -} - -static void -create_sub_menu (ShellStatusMenu *status) -{ - ShellStatusMenuPrivate *priv = status->priv; - GtkWidget *item; - - priv->menu = gtk_menu_new (); - g_signal_connect (priv->menu, "style-set", G_CALLBACK (menu_style_set_cb), - status); - - g_signal_connect (priv->manager, "users-loaded", - G_CALLBACK (on_manager_users_loaded), status); - g_signal_connect (priv->manager, "user-added", - G_CALLBACK (on_manager_user_added), status); - g_signal_connect (priv->manager, "user-removed", - G_CALLBACK (on_manager_user_removed), status); - - priv->account_item = gtk_image_menu_item_new_with_label (_("Account Information...")); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->account_item), - gtk_image_new ()); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->account_item); - g_signal_connect (priv->account_item, "style-set", - G_CALLBACK (menuitem_style_set_cb), status); - g_signal_connect (priv->account_item, "activate", - G_CALLBACK (on_account_activate), status); - gtk_widget_show (priv->account_item); - - priv->sidebar_item = gtk_check_menu_item_new_with_label (_("Sidebar")); - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->sidebar_item), - gconf_client_get_bool (priv->client, SIDEBAR_VISIBLE_KEY, NULL)); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->sidebar_item); - g_signal_connect (priv->sidebar_item, "toggled", - G_CALLBACK (on_sidebar_toggled), status); - gtk_widget_show (priv->sidebar_item); - - priv->control_panel_item = gtk_image_menu_item_new_with_label (_("System Preferences...")); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->control_panel_item), - gtk_image_new ()); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->control_panel_item); - g_signal_connect (priv->control_panel_item, "style-set", - G_CALLBACK (menuitem_style_set_cb), status); - g_signal_connect (priv->control_panel_item, "activate", - G_CALLBACK (on_control_panel_activate), status); - gtk_widget_show (priv->control_panel_item); - - item = gtk_separator_menu_item_new (); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item); - gtk_widget_show (item); - - priv->lock_screen_item - = gtk_image_menu_item_new_with_label (_("Lock Screen")); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->lock_screen_item), - gtk_image_new ()); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->lock_screen_item); - g_signal_connect (priv->lock_screen_item, "style-set", - G_CALLBACK (menuitem_style_set_cb), status); - g_signal_connect (priv->lock_screen_item, "activate", - G_CALLBACK (on_lock_screen_activate), status); - gtk_widget_show (priv->lock_screen_item); - - priv->login_screen_item = gtk_image_menu_item_new_with_label (_("Switch User")); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->login_screen_item), - gtk_image_new ()); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->login_screen_item); - g_signal_connect (priv->login_screen_item, "style-set", - G_CALLBACK (menuitem_style_set_cb), status); - g_signal_connect (priv->login_screen_item, "activate", - G_CALLBACK (on_login_screen_activate), status); - /* Only show switch user if there are other users */ - - /* Log Out */ - priv->quit_session_item = gtk_image_menu_item_new_with_label (_("Log Out...")); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->quit_session_item), - gtk_image_new ()); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->quit_session_item); - g_signal_connect (priv->quit_session_item, "style-set", - G_CALLBACK (menuitem_style_set_cb), status); - g_signal_connect (priv->quit_session_item, "activate", - G_CALLBACK (on_quit_session_activate), status); - gtk_widget_show (priv->quit_session_item); - - /* Shut down */ - priv->shut_down_item = gtk_image_menu_item_new_with_label (_("Shut Down...")); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (priv->shut_down_item), - gtk_image_new ()); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), priv->shut_down_item); - g_signal_connect (priv->shut_down_item, "style-set", - G_CALLBACK (menuitem_style_set_cb), status); - g_signal_connect (priv->shut_down_item, "activate", - G_CALLBACK (on_shut_down_activate), status); - gtk_widget_show (priv->shut_down_item); - - g_signal_connect (G_OBJECT (priv->menu), "deactivate", - G_CALLBACK (on_deactivate), status); -} - -static void -shell_status_menu_init (ShellStatusMenu *status) -{ - ShellStatusMenuPrivate *priv; - - status->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (status, SHELL_TYPE_STATUS_MENU, - ShellStatusMenuPrivate); - - g_object_set (G_OBJECT (status), - "orientation", BIG_BOX_ORIENTATION_HORIZONTAL, - NULL); - priv->client = gconf_client_get_default (); - - priv->user_icon = CLUTTER_TEXTURE (clutter_texture_new ()); - big_box_append (BIG_BOX (status), CLUTTER_ACTOR (status->priv->user_icon), 0); - - priv->name_box = BIG_BOX (big_box_new (BIG_BOX_ORIENTATION_VERTICAL)); - g_object_set (G_OBJECT (priv->name_box), "y-align", BIG_BOX_ALIGNMENT_CENTER, NULL); - big_box_append (BIG_BOX (status), CLUTTER_ACTOR (priv->name_box), BIG_BOX_PACK_EXPAND); - priv->name = CLUTTER_TEXT (clutter_text_new ()); - big_box_append (BIG_BOX (priv->name_box), CLUTTER_ACTOR (priv->name), BIG_BOX_PACK_EXPAND); - - priv->manager = gdm_user_manager_ref_default (); - setup_current_user (status); - - create_sub_menu (status); -} - -static void -shell_status_menu_finalize (GObject *object) -{ - ShellStatusMenu *status = SHELL_STATUS_MENU (object); - ShellStatusMenuPrivate *priv = status->priv; - - gconf_client_notify_remove (priv->client, priv->client_notify_lockdown_id); - - g_signal_handler_disconnect (priv->user, priv->user_notify_id); - g_signal_handler_disconnect (priv->user, priv->user_icon_changed_id); - - if (priv->user != NULL) { - g_object_unref (priv->user); - } - g_object_unref (priv->client); - g_object_unref (priv->manager); - - G_OBJECT_CLASS (shell_status_menu_parent_class)->finalize (object); -} - -static void -shell_status_menu_class_init (ShellStatusMenuClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (ShellStatusMenuPrivate)); - - gobject_class->finalize = shell_status_menu_finalize; - - shell_status_menu_signals[DEACTIVATED] = - g_signal_new ("deactivated", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ShellStatusMenuClass, deactivated), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -static void -position_menu (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data) -{ - ShellStatusMenu *status = SHELL_STATUS_MENU (user_data); - ClutterActor *parent; - float src_x, src_y; - float width, height; - int menu_width; - - gtk_widget_get_size_request (GTK_WIDGET (menu), &menu_width, NULL); - - /* Encapsulation breakage: it looks better if the menu is - * aligned with the bottom of the actor's grandparent - the - * panel, rather than with the bottom of the actor. We just - * assume what the hierarchy is and where we are positioned - * in the panel. - */ - parent = clutter_actor_get_parent (CLUTTER_ACTOR (status)); - parent = clutter_actor_get_parent (parent); - clutter_actor_get_transformed_position (parent, &src_x, &src_y); - clutter_actor_get_transformed_size (parent, &width, &height); - *x = (gint)(0.5 + src_x + width - menu_width); - *y = (gint)(0.5 + src_y + height); -} - -/** - * shell_status_menu_toggle: - * @menu: a #ShellStatusMenu - * - * If the menu is not currently up, pops it up. Otherwise, hides it. - * Popping up may fail if another grab is already active; check with - * shell_status_menu_is_active(). - */ -void -shell_status_menu_toggle (ShellStatusMenu *status, ClutterEvent *event) -{ - ShellStatusMenuPrivate *priv = status->priv; - - if (GTK_WIDGET_VISIBLE (priv->menu)) - { - gtk_menu_popdown (GTK_MENU (priv->menu)); - } - else - { - /* We don't want to overgrab a Mutter grab with the grab that GTK+ - * uses on menus. - */ - ShellGlobal *global = shell_global_get (); - if (shell_global_display_is_grabbed (global)) - return; - - gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL, position_menu, - status, 1, event->button.time); - } -} - -/** - * shell_status_menu_is_active: - * @menu: a #ShellStatusMenu - * - * Gets whether the menu is currently popped up - * - * Return value: %TRUE if the menu is currently popped up - */ -gboolean -shell_status_menu_is_active (ShellStatusMenu *status) -{ - ShellStatusMenuPrivate *priv = status->priv; - - return GTK_WIDGET_VISIBLE (priv->menu); -} - -/** - * shell_status_menu_get_name: - * @menu: a #ShellStatusMenu - * - * Return value: (transfer none): the #ClutterText actor with the user's name. - */ -ClutterText * -shell_status_menu_get_name (ShellStatusMenu *menu) -{ - return menu->priv->name; -} - -/** - * shell_status_menu_get_icon: - * @menu: a #ShellStatusMenu - * - * Return value: (transfer none): the #ClutterTexture actor with the user icon. - */ -ClutterTexture * -shell_status_menu_get_icon (ShellStatusMenu *menu) -{ - return menu->priv->user_icon; -} diff --git a/src/shell-status-menu.h b/src/shell-status-menu.h deleted file mode 100644 index 9a7c2969f..000000000 --- a/src/shell-status-menu.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -#ifndef __SHELL_STATUS_MENU_H__ -#define __SHELL_STATUS_MENU_H__ - -#include -#include "big/box.h" - -G_BEGIN_DECLS - -#define SHELL_TYPE_STATUS_MENU (shell_status_menu_get_type ()) -#define SHELL_STATUS_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_STATUS_MENU, ShellStatusMenu)) -#define SHELL_STATUS_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_STATUS_MENU, ShellStatusMenuClass)) -#define SHELL_IS_STATUS_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_STATUS_MENU)) -#define SHELL_IS_STATUS_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_STATUS_MENU)) -#define SHELL_STATUS_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_STATUS_MENU, ShellStatusMenuClass)) - -typedef struct _ShellStatusMenu ShellStatusMenu; -typedef struct _ShellStatusMenuPrivate ShellStatusMenuPrivate; -typedef struct _ShellStatusMenuClass ShellStatusMenuClass; - -struct _ShellStatusMenu -{ - BigBox parent_instance; - - ShellStatusMenuPrivate *priv; -}; - -struct _ShellStatusMenuClass -{ - BigBoxClass parent_class; - - void (*deactivated) (ShellStatusMenu *status, gpointer user_data); -}; - -GType shell_status_menu_get_type (void); - -void shell_status_menu_toggle (ShellStatusMenu *menu, ClutterEvent *event); -gboolean shell_status_menu_is_active (ShellStatusMenu *menu); -ClutterText *shell_status_menu_get_name (ShellStatusMenu *menu); -ClutterTexture *shell_status_menu_get_icon (ShellStatusMenu *menu); - -G_END_DECLS - -#endif /* __SHELL_STATUS_MENU_H__ */ diff --git a/src/st/st-bin.c b/src/st/st-bin.c index 4c862b62a..48347b6ba 100644 --- a/src/st/st-bin.c +++ b/src/st/st-bin.c @@ -545,7 +545,7 @@ st_bin_new (void) /** * st_bin_set_child: * @bin: a #StBin - * @child: a #ClutterActor, or %NULL + * @child: (allow-none): a #ClutterActor, or %NULL * * Sets @child as the child of @bin. * diff --git a/src/st/st-box-layout.c b/src/st/st-box-layout.c index 180d893c4..88c2d9309 100644 --- a/src/st/st-box-layout.c +++ b/src/st/st-box-layout.c @@ -21,6 +21,20 @@ * */ +/* Portions copied from Clutter: + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * Copyright (C) 2006 OpenedHand + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + */ + /** * SECTION:st-box-layout * @short_description: a layout container arranging children in a single line @@ -246,6 +260,8 @@ st_box_container_add_actor (ClutterContainer *container, priv->children = g_list_append (priv->children, actor); + clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); + g_signal_emit_by_name (container, "actor-added", actor); } @@ -294,8 +310,42 @@ st_box_container_lower (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { - /* XXX: not yet implemented */ - g_warning ("%s() not yet implemented", __FUNCTION__); + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (container)->priv; + + /* copied from clutter/clutter/clutter-group.c */ + + priv->children = g_list_remove (priv->children, actor); + + /* Push to bottom */ + if (!sibling) + { + GList *last_item; + + last_item = g_list_first (priv->children); + + if (last_item) + sibling = last_item->data; + + priv->children = g_list_prepend (priv->children, actor); + } + else + { + gint pos; + + pos = g_list_index (priv->children, sibling); + + priv->children = g_list_insert (priv->children, actor, pos); + } + + /* See comment in group_raise for this */ + if (sibling && + clutter_actor_get_depth (sibling) != clutter_actor_get_depth (actor)) + { + clutter_actor_set_depth (actor, clutter_actor_get_depth (sibling)); + } + + if (CLUTTER_ACTOR_IS_VISIBLE (container)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void @@ -303,8 +353,47 @@ st_box_container_raise (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { - /* XXX: not yet implemented */ - g_warning ("%s() not yet implemented", __FUNCTION__); + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (container)->priv; + + priv->children = g_list_remove (priv->children, actor); + + /* copied from clutter/clutter/clutter-group.c */ + + /* Raise at the top */ + if (!sibling) + { + GList *last_item; + + last_item = g_list_last (priv->children); + + if (last_item) + sibling = last_item->data; + + priv->children = g_list_append (priv->children, actor); + } + else + { + gint pos; + + pos = g_list_index (priv->children, sibling) + 1; + + priv->children = g_list_insert (priv->children, actor, pos); + } + + /* set Z ordering a value below, this will then call sort + * as values are equal ordering shouldn't change but Z + * values will be correct. + * + * FIXME: optimise + */ + if (sibling && + clutter_actor_get_depth (sibling) != clutter_actor_get_depth (actor)) + { + clutter_actor_set_depth (actor, clutter_actor_get_depth (sibling)); + } + + if (CLUTTER_ACTOR_IS_VISIBLE (container)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void @@ -714,6 +803,8 @@ st_box_layout_allocate (ClutterActor *actor, gint n_expand_children = 0, i; gfloat expand_amount, shrink_amount; BoxChildShrink *shrinks = NULL; + gboolean flip = (st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) + && (!priv->is_vertical); CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->allocate (actor, box, flags); @@ -808,6 +899,8 @@ st_box_layout_allocate (ClutterActor *actor, if (priv->is_vertical) position = content_box.y1; + else if (flip) + position = content_box.x2; else position = content_box.x1; @@ -865,7 +958,10 @@ st_box_layout_allocate (ClutterActor *actor, else if (shrink_amount > 0) child_allocated -= shrinks[i].shrink_amount; - next_position = position + child_allocated; + if (flip) + next_position = position - child_allocated; + else + next_position = position + child_allocated; if (priv->is_vertical) { @@ -883,8 +979,17 @@ st_box_layout_allocate (ClutterActor *actor, } else { - child_box.x1 = (int)(0.5 + position); - child_box.x2 = (int)(0.5 + next_position); + if (flip) + { + child_box.x1 = (int)(0.5 + next_position); + child_box.x2 = (int)(0.5 + position); + } + else + { + child_box.x1 = (int)(0.5 + position); + child_box.x2 = (int)(0.5 + next_position); + } + child_box.y1 = content_box.y1; if (priv->vadjustment) child_box.y2 = content_box.y1 + MAX (avail_height, natural_height); @@ -895,7 +1000,10 @@ st_box_layout_allocate (ClutterActor *actor, clutter_actor_allocate (child, &child_box, flags); } - position = next_position + priv->spacing; + if (flip) + position = next_position - priv->spacing; + else + position = next_position + priv->spacing; next_child: if (priv->is_pack_start) @@ -1251,3 +1359,62 @@ st_box_layout_get_pack_start (StBoxLayout *box) return box->priv->is_pack_start; } + +static void +st_box_layout_internal_remove_all (StBoxLayout *self, + gboolean destroy) +{ + StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (self)->priv; + ClutterActor *child; + + while (priv->children) + { + child = priv->children->data; + + g_object_ref (child); + priv->children = g_list_delete_link (priv->children, priv->children); + clutter_actor_unparent (child); + g_signal_emit_by_name (self, "actor-removed", child); + if (destroy) + clutter_actor_destroy (child); + g_object_unref (child); + } + + clutter_actor_queue_relayout ((ClutterActor*) self); +} + +/** + * st_box_layout_remove_all: + * @self: + * + * Efficiently unparent all children currently in this box. + */ +void +st_box_layout_remove_all (StBoxLayout *self) +{ + st_box_layout_internal_remove_all (self, FALSE); +} + +/** + * st_box_layout_destroy_children: + * @self: + * + * Efficiently unparent and destroy all children currently in this box. + */ +void +st_box_layout_destroy_children (StBoxLayout *self) +{ + st_box_layout_internal_remove_all (self, TRUE); +} + +/** + * st_box_layout_get_n_children: + * @self: a #StBoxLayout + * + * Returns the number of children in this box. + */ +guint +st_box_layout_get_n_children (StBoxLayout *self) +{ + return g_list_length (self->priv->children); +} diff --git a/src/st/st-box-layout.h b/src/st/st-box-layout.h index 30954a2ee..21cd0b757 100644 --- a/src/st/st-box-layout.h +++ b/src/st/st-box-layout.h @@ -89,6 +89,12 @@ void st_box_layout_set_pack_start (StBoxLayout *box, gboolean pack_start); gboolean st_box_layout_get_pack_start (StBoxLayout *box); +void st_box_layout_remove_all (StBoxLayout *box); + +void st_box_layout_destroy_children (StBoxLayout *box); + +guint st_box_layout_get_n_children (StBoxLayout *box); + G_END_DECLS #endif /* _ST_BOX_LAYOUT_H */ diff --git a/src/st/st-table.c b/src/st/st-table.c index e844ff6fb..42723a36c 100644 --- a/src/st/st-table.c +++ b/src/st/st-table.c @@ -290,7 +290,8 @@ st_table_allocate_fill (ClutterActor *child, gdouble x_align, gdouble y_align, gboolean x_fill, - gboolean y_fill) + gboolean y_fill, + gboolean ltr) { gfloat natural_width, natural_height; gfloat min_width, min_height; @@ -310,8 +311,16 @@ st_table_allocate_fill (ClutterActor *child, if (x_fill) { - allocation.x1 = childbox->x1; - allocation.x2 = (int)(allocation.x1 + available_width); + if (ltr) + { + allocation.x1 = childbox->x1; + allocation.x2 = (int)(allocation.x1 + available_width); + } + else + { + allocation.x2 = childbox->x2; + allocation.x1 = (int)(allocation.x2 - available_width); + } } if (y_fill) @@ -361,8 +370,16 @@ st_table_allocate_fill (ClutterActor *child, if (!x_fill) { - allocation.x1 = childbox->x1 + (int)((available_width - child_width) * x_align); - allocation.x2 = allocation.x1 + (int) child_width; + if (ltr) + { + allocation.x1 = childbox->x1 + (int)((available_width - child_width) * x_align); + allocation.x2 = allocation.x1 + (int) child_width; + } + else + { + allocation.x2 = childbox->x2 - (int)((available_width - child_width) * x_align); + allocation.x1 = allocation.x2 - (int) child_width; + } } if (!y_fill) @@ -384,6 +401,7 @@ st_table_homogeneous_allocate (ClutterActor *self, gfloat col_width, row_height; gint row_spacing, col_spacing; StTablePrivate *priv = ST_TABLE (self)->priv; + gboolean ltr = st_widget_get_direction (ST_WIDGET (self)) == ST_TEXT_DIRECTION_LTR; col_spacing = priv->col_spacing; row_spacing = priv->row_spacing; @@ -421,13 +439,21 @@ st_table_homogeneous_allocate (ClutterActor *self, x_fill = meta->x_fill; y_fill = meta->y_fill; - childbox.x1 = content_box->x1 + (col_width + col_spacing) * col; - childbox.x2 = childbox.x1 + (col_width * col_span) + (col_spacing * (col_span - 1)); + if (ltr) + { + childbox.x1 = content_box->x1 + (col_width + col_spacing) * col; + childbox.x2 = childbox.x1 + (col_width * col_span) + (col_spacing * (col_span - 1)); + } + else + { + childbox.x2 = content_box->x2 - (col_width + col_spacing) * col; + childbox.x1 = childbox.x2 - (col_width * col_span) - (col_spacing * (col_span - 1)); + } childbox.y1 = content_box->y1 + (row_height + row_spacing) * row; childbox.y2 = childbox.y1 + (row_height * row_span) + (row_spacing * (row_span - 1)); - st_table_allocate_fill (child, &childbox, x_align, y_align, x_fill, y_fill); + st_table_allocate_fill (child, &childbox, x_align, y_align, x_fill, y_fill, ltr); clutter_actor_allocate (child, &childbox, flags); } @@ -725,6 +751,7 @@ st_table_preferred_allocate (ClutterActor *self, gint *col_widths, *row_heights; StTable *table; StTablePrivate *priv; + gboolean ltr; table = ST_TABLE (self); priv = ST_TABLE (self)->priv; @@ -741,6 +768,8 @@ st_table_preferred_allocate (ClutterActor *self, (int) (content_box->y2 - content_box->y1), col_widths); + ltr = (st_widget_get_direction (ST_WIDGET (self)) == ST_TEXT_DIRECTION_LTR); + for (list = priv->children; list; list = g_slist_next (list)) { @@ -808,10 +837,20 @@ st_table_preferred_allocate (ClutterActor *self, } /* calculate child x */ - child_x = (int) content_box->x1 - + col_spacing * col; - for (i = 0; i < col; i++) - child_x += col_widths[i]; + if (ltr) + { + child_x = (int) content_box->x1 + + col_spacing * col; + for (i = 0; i < col; i++) + child_x += col_widths[i]; + } + else + { + child_x = (int) content_box->x2 + - col_spacing * col; + for (i = 0; i < col; i++) + child_x -= col_widths[i]; + } /* calculate child y */ child_y = (int) content_box->y1 @@ -820,14 +859,22 @@ st_table_preferred_allocate (ClutterActor *self, child_y += row_heights[i]; /* set up childbox */ - childbox.x1 = (float) child_x; - childbox.x2 = (float) MAX (0, child_x + col_width); + if (ltr) + { + childbox.x1 = (float) child_x; + childbox.x2 = (float) MAX (0, child_x + col_width); + } + else + { + childbox.x2 = (float) child_x; + childbox.x1 = (float) MAX (0, child_x - col_width); + } childbox.y1 = (float) child_y; childbox.y2 = (float) MAX (0, child_y + row_height); - st_table_allocate_fill (child, &childbox, x_align, y_align, x_fill, y_fill); + st_table_allocate_fill (child, &childbox, x_align, y_align, x_fill, y_fill, ltr); clutter_actor_allocate (child, &childbox, flags); } diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c index e81e35ac5..cebd1afde 100644 --- a/src/st/st-theme-node.c +++ b/src/st/st-theme-node.c @@ -457,7 +457,7 @@ get_color_from_term (StThemeNode *node, * 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. + * @color: location to store the color that was determined. * If the property is not found, the value in this location * will not be changed. * @@ -513,7 +513,7 @@ st_theme_node_get_color (StThemeNode *node, * 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. + * @value: location to store the value that was determined. * If the property is not found, the value in this location * will not be changed. * @@ -740,7 +740,7 @@ get_length_internal (StThemeNode *node, * 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. + * @length: 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. @@ -2199,7 +2199,7 @@ st_theme_node_adjust_preferred_height (StThemeNode *node, * st_theme_node_get_content_box: * @node: a #StThemeNode * @allocation: the box allocated to a #ClutterAlctor - * @content_box: (out): computed box occupied by the actor's content + * @content_box: computed box occupied by the actor's content * * Gets the box within an actor's allocation that contents the content * of an actor (excluding borders and padding). This is a convenience function @@ -2211,14 +2211,33 @@ st_theme_node_get_content_box (StThemeNode *node, const ClutterActorBox *allocation, ClutterActorBox *content_box) { + double noncontent_left, noncontent_top, noncontent_right, noncontent_bottom; + double avail_width, avail_height, content_width, content_height; + g_return_if_fail (ST_IS_THEME_NODE (node)); ensure_geometry (node); - 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]); + avail_width = allocation->x2 - allocation->x1; + avail_height = allocation->y2 - allocation->y1; + + noncontent_left = node->border_width[ST_SIDE_LEFT] + node->padding[ST_SIDE_LEFT]; + noncontent_top = node->border_width[ST_SIDE_TOP] + node->padding[ST_SIDE_TOP]; + noncontent_right = node->border_width[ST_SIDE_RIGHT] + node->padding[ST_SIDE_RIGHT]; + noncontent_bottom = node->border_width[ST_SIDE_BOTTOM] + node->padding[ST_SIDE_BOTTOM]; + + content_box->x1 = (int)(0.5 + noncontent_left); + content_box->y1 = (int)(0.5 + noncontent_top); + + content_width = avail_width - noncontent_left - noncontent_right; + if (content_width < 0) + content_width = 0; + content_height = avail_height - noncontent_top - noncontent_bottom; + if (content_height < 0) + content_height = 0; + + content_box->x2 = (int)(0.5 + content_box->x1 + content_width); + content_box->y2 = (int)(0.5 + content_box->y1 + content_height); } diff --git a/src/st/st-tooltip.c b/src/st/st-tooltip.c index 5232dbc41..1fbec22e3 100644 --- a/src/st/st-tooltip.c +++ b/src/st/st-tooltip.c @@ -148,7 +148,6 @@ st_tooltip_get_preferred_width (ClutterActor *self, { StTooltipPrivate *priv = ST_TOOLTIP (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); - gfloat min_label_w, natural_label_w; gfloat label_height, arrow_height; ClutterActor *arrow_image; @@ -180,13 +179,8 @@ st_tooltip_get_preferred_width (ClutterActor *self, { clutter_actor_get_preferred_width (priv->label, label_height, - &min_label_w, - &natural_label_w); - } - else - { - min_label_w = 0; - natural_label_w = 0; + min_width_p, + natural_width_p); } st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); diff --git a/src/st/st-widget.c b/src/st/st-widget.c index 2570ac62c..482d5c04a 100644 --- a/src/st/st-widget.c +++ b/src/st/st-widget.c @@ -63,8 +63,11 @@ struct _StWidgetPrivate gboolean has_tooltip : 1; gboolean is_style_dirty : 1; gboolean draw_bg_color : 1; + gboolean draw_border_internal : 1; StTooltip *tooltip; + + StTextDirection direction; }; /** @@ -356,25 +359,25 @@ static void st_widget_real_draw_background (StWidget *self) { StWidgetPrivate *priv = self->priv; + ClutterActor *actor = CLUTTER_ACTOR (self); + ClutterActorBox allocation = { 0, }; + gfloat w, h; + guint8 opacity; + + clutter_actor_get_allocation_box (actor, &allocation); + w = allocation.x2 - allocation.x1; + h = allocation.y2 - allocation.y1; + + opacity = clutter_actor_get_paint_opacity (actor); /* Default implementation just draws the background * colour and the image on top */ if (priv->draw_bg_color) { - ClutterActor *actor = CLUTTER_ACTOR (self); - ClutterActorBox allocation = { 0, }; ClutterColor bg_color = priv->bg_color; - gfloat w, h; - bg_color.alpha = clutter_actor_get_paint_opacity (actor) - * bg_color.alpha - / 255; - - clutter_actor_get_allocation_box (actor, &allocation); - - w = allocation.x2 - allocation.x1; - h = allocation.y2 - allocation.y1; + bg_color.alpha = opacity * bg_color.alpha / 255; cogl_set_source_color4ub (bg_color.red, bg_color.green, @@ -383,6 +386,76 @@ st_widget_real_draw_background (StWidget *self) cogl_rectangle (0, 0, w, h); } + if (priv->draw_border_internal) + { + StThemeNode *node = st_widget_get_theme_node (self); + int side; + double border_top, border_right, border_bottom, border_left; + + border_top = st_theme_node_get_border_width (node, ST_SIDE_TOP); + border_right = st_theme_node_get_border_width (node, ST_SIDE_RIGHT); + border_bottom = st_theme_node_get_border_width (node, ST_SIDE_BOTTOM); + border_left = st_theme_node_get_border_width (node, ST_SIDE_LEFT); + + for (side = 0; side < 4; side++) + { + ClutterColor color; + + switch (side) + { + case ST_SIDE_TOP: + if (border_top <= 0) + continue; + break; + case ST_SIDE_RIGHT: + if (border_right <= 0) + continue; + break; + case ST_SIDE_BOTTOM: + if (border_bottom <= 0) + continue; + break; + case ST_SIDE_LEFT: + if (border_left <= 0) + continue; + break; + } + + st_theme_node_get_border_color (node, side, &color); + + color.alpha = (color.alpha * opacity) / 0xff; + + cogl_set_source_color4ub (color.red, + color.green, + color.blue, + color.alpha); + + /* Note top and bottom extend to the ends, left/right + * are constrained by them. See comment above about CSS + * conformance. + */ + switch (side) + { + case ST_SIDE_TOP: + cogl_rectangle (0, 0, + w, border_top); + break; + case ST_SIDE_RIGHT: + cogl_rectangle (w - border_right, border_top, + w, h - border_bottom); + break; + case ST_SIDE_BOTTOM: + cogl_rectangle (0, h - border_bottom, + w, h); + break; + case ST_SIDE_LEFT: + cogl_rectangle (0, border_top, + border_left, h - border_bottom); + break; + } + } + } + if (priv->border_image) clutter_actor_paint (priv->border_image); } @@ -491,6 +564,7 @@ st_widget_real_style_changed (StWidget *self) ClutterColor border_color = { 0, }; StSide side; StCorner corner; + gboolean uniform_border_width; /* application has request this widget is not stylable */ if (!priv->is_stylable) @@ -520,33 +594,6 @@ st_widget_real_style_changed (StWidget *self) texture_cache = st_texture_cache_get_default (); - /* StThemeNode supports different widths and colors for different sides - * of the border, and different radii for the different corners. We take - * the different border widths into account when positioning, but our current - * drawing code (using BigRectangle) can only handle a single width, color, - * and radius, so we arbitrarily pick the first non-zero width and radius, - * and use that. - */ - for (side = ST_SIDE_TOP; side <= ST_SIDE_LEFT; side++) - { - double width = st_theme_node_get_border_width (theme_node, side); - if (width > 0.5) - { - border_width = (int)(0.5 + width); - st_theme_node_get_border_color (theme_node, side, &border_color); - break; - } - } - - for (corner = ST_CORNER_TOPLEFT; corner <= ST_CORNER_BOTTOMLEFT; corner++) - { - double radius = st_theme_node_get_border_radius (theme_node, corner); - if (radius > 0.5) - { - border_radius = (int)(0.5 + radius); - break; - } - } /* Rough notes about the relationship of borders and backgrounds in CSS3; * see http://www.w3.org/TR/css3-background/ for more accurate details. @@ -565,6 +612,8 @@ st_widget_real_style_changed (StWidget *self) * zero width or a border image is being used. * * Deviations from the above as implemented here: + * - Nonuniform border widths combined with a non-zero border radius result + * in the border radius being ignored * - The combination of border image and a non-zero border radius is * not supported; the background color will be drawn with square * corners. @@ -576,6 +625,44 @@ st_widget_real_style_changed (StWidget *self) * and a single background image above it. */ + /* Check whether all border widths are the same. Also, acquire the + * first nonzero border width as well as the border color. + */ + uniform_border_width = TRUE; + border_width = st_theme_node_get_border_width (theme_node, ST_SIDE_TOP); + if (border_width > 0.5) + border_width = (int)(0.5 + border_width); + for (side = 0; side < 4; side++) + { + double width = st_theme_node_get_border_width (theme_node, side); + if (width > 0.5) + width = (int)(0.5 + width); + if (width > 0) + { + border_width = width; + st_theme_node_get_border_color (theme_node, side, &border_color); + } + if ((int)width != border_width) + { + uniform_border_width = FALSE; + break; + } + } + + /* Pick the first nonzero border radius, but only if we have a uniform border. */ + if (uniform_border_width) + { + for (corner = 0; corner < 4; corner++) + { + double radius = st_theme_node_get_border_radius (theme_node, corner); + if (radius > 0.5) + { + border_radius = (int)(0.5 + radius); + break; + } + } + } + border_image = st_theme_node_get_border_image (theme_node); if (border_image) { @@ -607,9 +694,9 @@ st_widget_real_style_changed (StWidget *self) has_changed = TRUE; relayout_needed = TRUE; } - else if ((border_width > 0 && border_color.alpha != 0) || - (border_radius > 0 && priv->bg_color.alpha != 0)) + else if (border_radius > 0) { + priv->draw_border_internal = FALSE; priv->draw_bg_color = FALSE; priv->border_image = g_object_new (BIG_TYPE_RECTANGLE, "color", &priv->bg_color, @@ -623,6 +710,19 @@ st_widget_real_style_changed (StWidget *self) has_changed = TRUE; relayout_needed = TRUE; } + else if (border_width > 0 && border_color.alpha != 0) + { + priv->draw_bg_color = TRUE; + priv->draw_border_internal = TRUE; + has_changed = TRUE; + relayout_needed = TRUE; + } + else if (priv->draw_border_internal) + { + priv->draw_border_internal = FALSE; + has_changed = TRUE; + relayout_needed = TRUE; + } bg_file = st_theme_node_get_background_image (theme_node); if (bg_file != NULL) @@ -1052,7 +1152,7 @@ st_widget_get_style_pseudo_class (StWidget *actor) /** * st_widget_set_style_pseudo_class: * @actor: a #StWidget - * @pseudo_class: a new pseudo class string + * @pseudo_class: (allow-none): a new pseudo class string * * Set the style pseudo class */ @@ -1174,6 +1274,40 @@ st_widget_ensure_style (StWidget *widget) st_widget_recompute_style (widget, NULL); } +static StTextDirection default_direction = ST_TEXT_DIRECTION_LTR; + +StTextDirection +st_widget_get_default_direction (void) +{ + return default_direction; +} + +void +st_widget_set_default_direction (StTextDirection dir) +{ + g_return_if_fail (dir != ST_TEXT_DIRECTION_NONE); + + default_direction = dir; +} + +StTextDirection +st_widget_get_direction (StWidget *self) +{ + g_return_val_if_fail (ST_IS_WIDGET (self), ST_TEXT_DIRECTION_LTR); + + if (self->priv->direction != ST_TEXT_DIRECTION_NONE) + return self->priv->direction; + else + return default_direction; +} + +void +st_widget_set_direction (StWidget *self, StTextDirection dir) +{ + g_return_if_fail (ST_IS_WIDGET (self)); + self->priv->direction = dir; +} + /** * st_widget_get_border_image: * @actor: A #StWidget diff --git a/src/st/st-widget.h b/src/st/st-widget.h index 66f7b0698..bebe28620 100644 --- a/src/st/st-widget.h +++ b/src/st/st-widget.h @@ -35,6 +35,12 @@ G_BEGIN_DECLS +typedef enum { + ST_TEXT_DIRECTION_NONE, + ST_TEXT_DIRECTION_LTR, + ST_TEXT_DIRECTION_RTL +} StTextDirection; + #define ST_TYPE_WIDGET (st_widget_get_type ()) #define ST_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_WIDGET, StWidget)) #define ST_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_WIDGET)) @@ -103,6 +109,11 @@ void st_widget_hide_tooltip (StWidget *widget); void st_widget_ensure_style (StWidget *widget); +StTextDirection st_widget_get_default_direction (void); +void st_widget_set_default_direction (StTextDirection dir); + +StTextDirection st_widget_get_direction (StWidget *self); +void st_widget_set_direction (StWidget *self, StTextDirection dir); /* Only to be used by sub-classes of StWidget */ void st_widget_style_changed (StWidget *widget); diff --git a/tools/build/gnome-shell-build-setup.sh b/tools/build/gnome-shell-build-setup.sh index 1f5ddd474..223500353 100755 --- a/tools/build/gnome-shell-build-setup.sh +++ b/tools/build/gnome-shell-build-setup.sh @@ -53,12 +53,10 @@ fi # # Non-devel packages needed by gnome-shell and its deps: # glxinfo, gstreamer-plugins-base, gstreamer-plugins-good, -# python, pygobject, gnome-python (gconf), -# Xephyr, xeyes*, xlogo*, xterm*, zenity +# python, pygobject, gnome-python (gconf), gnome-terminal* +# Xephyr*, zenity # -# (*)ed packages are only needed because gnome-shell launches them -# when running in Xephyr mode, and we should probably change it to use -# less lame things. +# (*) only needed for --xephyr # Can this be simplified? Obvious ways don't handle handle packages # that have been installed then removed. ('purged' status, e.g.) @@ -82,7 +80,7 @@ if test x$system = xUbuntu -o x$system = xDebian -o x$system = xLinuxMint ; 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 libcroco3-dev \ + xulrunner-dev xserver-xephyr gnome-terminal libcroco3-dev \ libgstreamer0.10-dev gstreamer0.10-plugins-base gstreamer0.10-plugins-good \ ; do if ! dpkg_is_installed $pkg; then @@ -106,7 +104,7 @@ if test x$system = xFedora ; then gnome-desktop-devel librsvg2-devel libwnck-devel mesa-libGL-devel python-devel pygobject2 \ 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 \ + glx-utils xorg-x11-server-Xephyr gnome-terminal zenity \ ; do if ! rpm -q $pkg > /dev/null 2>&1; then reqd="$pkg $reqd" @@ -124,7 +122,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 \ - libcroco-devel xorg-x11-devel xterm xorg-x11 xorg-x11-server-extra \ + libcroco-devel xorg-x11-devel xorg-x11 xorg-x11-server-extra \ ; do if ! rpm -q $pkg > /dev/null 2>&1; then reqd="$pkg $reqd" @@ -144,7 +142,7 @@ if test x$system = xMandrivaLinux ; then bison flex gnome-common gnome-doc-utils gtk-doc intltool \ libGConf2-devel ffi5-devel libgnomeui2-devel librsvg2-devel \ libwnck-1-devel GL-devel readline-devel libxulrunner-devel \ - libxdamage-devel mesa-demos x11-server-xephyr x11-apps xterm zenity \ + libxdamage-devel mesa-demos x11-server-xephyr zenity \ libcroco0.6-devel \ ; do if ! rpm -q --whatprovides $pkg > /dev/null 2>&1; then