%PDF- %PDF-
Direktori : /usr/share/org.gnome.Characters/ |
Current File : //usr/share/org.gnome.Characters/org.gnome.Characters.src.gresource |
GVariant � ( Ե ����� L � � �Xp� � v � � KFv� � L � � {W�y � v � 8( KP� 8( L <( @( ��$0 @( L H( L( Q�8\ L( v `( )y ���� )y L 4y 8y �l~ 8y v Py 0� �(� 0� v 8� V� H9 V� v h� J� ж�U J� v X� � �� � v � �� / characterDialog.js � /* exported CharacterDialog */ // -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- // // Copyright (C) 2014-2015 Daiki Ueno <dueno@src.gnome.org> // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. const { Adw, Gc, Gdk, Gio, GObject, Gtk, Pango } = imports.gi; const Util = imports.util; var CharacterDialog = GObject.registerClass({ Signals: { 'character-copied': { param_types: [GObject.TYPE_STRING] }, }, Template: 'resource:///org/gnome/Characters/character_dialog.ui', InternalChildren: [ 'characterStack', 'navigationView', 'characterLabel', 'missingLabel', 'detailRow', 'detailLabel', 'seeAlsoRow', 'relatedPage', 'relatedListbox', 'toastOverlay', ], }, class CharacterDialog extends Adw.Window { _init(character, fontDescription) { super._init(); this._cancellable = new Gio.Cancellable(); this._fontDescription = fontDescription; const actions = new Gio.SimpleActionGroup(); Util.initActions(actions, [ { name: 'copy', activate: this._copyCharacter.bind(this) }, ]); this.insert_action_group('character', actions); this._relatedPage.connect('hidden', () => { this._populateRelatedPage(); }); this._setCharacter(character); } _finishSearch(result) { this._related = result; this._seeAlsoRow.visible = result.len > 0; if (!this._relatedPage.get_mapped()) this._populateRelatedPage(); } _populateRelatedPage() { this._relatedListbox.remove_all(); for (let index = 0; index < this._related.len; index++) { let uc = Gc.search_result_get(this._related, index); let name = Gc.character_name(uc); if (name === null) continue; let row = new Adw.ActionRow(); row.set_title(Util.capitalize(name)); row.add_prefix(new Gtk.Label({ label: uc, valign: Gtk.Align.CENTER, halign: Gtk.Align.CENTER, width_request: 45, })); row.connect('activated', () => { this._setCharacter(uc); this._navigationView.pop(); }); row.set_activatable(true); this._relatedListbox.append(row); } } _setCharacter(uc) { this._character = uc; let name = Gc.character_name(this._character); if (name !== null) name = Util.capitalize(name); if (Gc.character_is_composite(uc)) { this._detailRow.hide(); if (name === null) name = _('Unknown character name'); } else { let codePoint = Util.toCodePoint(this._character); let codePointHex = codePoint.toString(16).toUpperCase(); this._detailLabel.label = 'U+%04s'.format(codePointHex); this._detailRow.show(); if (name === null) name = 'U+%04s'.format(codePointHex); } this.title = name ? name : ''; this._characterLabel.label = this._character; let pangoContext = this._characterLabel.get_pango_context(); pangoContext.set_font_description(this._fontDescription); var pangoLayout = Pango.Layout.new(pangoContext); pangoLayout.set_text(this._character, -1); if (pangoLayout.get_unknown_glyphs_count() === 0) { this._characterStack.visible_child_name = 'character'; } else { var fontFamily = this._fontDescription.get_family(); // TRANSLATORS: the first variable is a character, the second is a font this._missingLabel.label = _('%s is not included in %s').format(name, fontFamily); this._characterStack.visible_child_name = 'missing'; } this._cancellable.cancel(); this._cancellable.reset(); let criteria = Gc.SearchCriteria.new_related(this._character); let context = new Gc.SearchContext({ criteria }); context.search( -1, this._cancellable, (ctx, res) => { try { let result = ctx.search_finish(res); this._finishSearch(result); } catch (e) { log(`Failed to search related: ${e.message}`); } }); this._seeAlsoRow.visible = false; } _copyCharacter() { let display = Gdk.Display.get_default(); let clipboard = display.get_clipboard(); clipboard.set(this._character); this.emit('character-copied', this._character); const toast = new Adw.Toast({ title: _('Character copied to clipboard'), timeout: 2, }); this._toastOverlay.add_toast(toast); } }); (uuay)js/ main.js p /* exported main settings */ // -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- // // Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com> // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the GNOME Foundation nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pkg.initGettext(); pkg.initFormat(); pkg.require({ 'Gdk': '4.0', 'Gio': '2.0', 'GLib': '2.0', 'GObject': '2.0', 'Gtk': '4.0', 'Adw': '1', 'GnomeDesktop': '4.0', }); const { GLib, Gio, GObject, Adw } = imports.gi; const { CharactersView } = imports.charactersView; const { Sidebar } = imports.sidebar; const { MainWindow } = imports.window; const Util = imports.util; const SearchProvider = imports.searchProvider; var settings = null; var MyApplication = GObject.registerClass({ }, class MyApplication extends Adw.Application { _init() { super._init({ application_id: pkg.name, flags: Gio.ApplicationFlags.FLAGS_NONE, resource_base_path: '/org/gnome/Characters', }); GLib.set_application_name(_('Characters')); this._searchProvider = new SearchProvider.SearchProvider(this); } get window() { return this._appwindow; } _onQuit() { this.quit(); } vfunc_startup() { super.vfunc_startup(); this.get_style_manager().set_color_scheme(Adw.ColorScheme.PREFER_LIGHT); Util.initActions(this, [ { name: 'quit', activate: this._onQuit }, ]); this.set_accels_for_action('app.quit', ['<Primary>q']); this.set_accels_for_action('win.find', ['<Primary>f']); this.set_accels_for_action('win.show-help-overlay', ['<Primary>question']); settings = Util.getSettings('org.gnome.Characters', '/org/gnome/Characters/'); if (this.get_flags() & Gio.ApplicationFlags.IS_SERVICE) this.set_inactivity_timeout(10000); log('Characters Application started'); } vfunc_dbus_register(connection, path) { const searchProviderPath = `${path}/SearchProvider`; super.vfunc_dbus_register(connection, searchProviderPath); this._searchProvider.export(connection, searchProviderPath); return true; } vfunc_activate() { if (!this._appwindow) this._appwindow = new MainWindow(this); if (pkg.name.endsWith('Devel')) this._appwindow.add_css_class('devel'); this._appwindow.present(); log('Characters Application activated'); } vfunc_shutdown() { log('Characters Application exiting'); super.vfunc_shutdown(); } }); function main(argv) { GObject.type_ensure(CharactersView.$gtype); GObject.type_ensure(Sidebar.$gtype); return new MyApplication().run(argv); } (uuay)org/ gnome/ charactersView.js �P /* exported CharactersView */ // -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- // // Copyright (C) 2014-2015 Daiki Ueno <dueno@src.gnome.org> // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. const { Gc, Gdk, Gio, GLib, GnomeDesktop, GObject, Gtk, Pango, Graphene } = imports.gi; Gio._promisify(Gc.SearchContext.prototype, 'search', 'search_finish'); const Util = imports.util; const BASELINE_OFFSET = 0.85; const CELLS_PER_ROW = 5; const NUM_ROWS = 5; const NUM_COLUMNS = 3; const CELL_SIZE = 50; function getCellSize(fontDescription) { if (fontDescription === null || fontDescription.get_size() === 0) return CELL_SIZE; return fontDescription.get_size() * 2 / Pango.SCALE; } const CharacterListRow = GObject.registerClass({ }, class CharacterListRow extends GObject.Object { _init(characters, fontDescription, overlayFontDescription) { super._init({}); this._baseGlyphRect = null; this._characters = characters; this._fontDescription = fontDescription; this._overlayFontDescription = overlayFontDescription; } snapshot(snapshot, x, y, pangoContext, styleContext) { let layout = Pango.Layout.new(pangoContext); layout.set_font_description(this._fontDescription); // Draw characters. Do centering and attach to the baseline. let cellSize = getCellSize(this._fontDescription); for (let i in this._characters) { let character = this._characters[i]; let cellRect = new Gdk.Rectangle({ x: x + cellSize * i, y, width: cellSize, height: cellSize, }); layout.set_text(character, -1); snapshot.save(); if (Gc.character_is_invisible(character)) { this._drawBoundingBox(snapshot, layout, styleContext, cellRect); this._drawCharacterName(snapshot, pangoContext, styleContext, cellRect, character); } else if (layout.get_unknown_glyphs_count() === 0) { let layoutBaseline = layout.get_baseline(); let logicalRect = layout.get_extents()[0]; snapshot.translate(new Graphene.Point({ x: x + cellSize * i - logicalRect.x / Pango.SCALE + (cellSize - logicalRect.width / Pango.SCALE) / 2, y: y + BASELINE_OFFSET * cellSize - layoutBaseline / Pango.SCALE, })); let textColor = styleContext.get_color(); snapshot.append_layout(layout, textColor); } else { this._drawBoundingBox(snapshot, layout, styleContext, cellRect); this._drawCharacterName(snapshot, pangoContext, styleContext, cellRect, character); } snapshot.restore(); } } _computeBoundingBox(layout, cellRect) { let shapeRect; let layoutBaseline; if (layout.get_unknown_glyphs_count() === 0) { let inkRect = layout.get_extents()[1]; layoutBaseline = layout.get_baseline(); shapeRect = inkRect; } else { // If the character cannot be rendered with the current // font settings, show a rectangle calculated from the // base glyphs ('AA'). if (this._baseGlyphRect === null) { layout.set_text('AA', -1); let baseInkRect = layout.get_extents()[1]; this._baseGlyphLayoutBaseline = layout.get_baseline(); this._baseGlyphRect = baseInkRect; } layoutBaseline = this._baseGlyphLayoutBaseline; shapeRect = new Pango.Rectangle({ x: this._baseGlyphRect.x, y: this._baseGlyphRect.y, width: this._baseGlyphRect.width, height: this._baseGlyphRect.height, }); } shapeRect.x = cellRect.x - shapeRect.x / Pango.SCALE + (cellRect.width - shapeRect.width / Pango.SCALE) / 2; shapeRect.y = cellRect.y + BASELINE_OFFSET * cellRect.height - layoutBaseline / Pango.SCALE; shapeRect.width /= Pango.SCALE; shapeRect.height /= Pango.SCALE; return shapeRect; } _drawBoundingBox(snapshot, pangoLayout, styleContext, cellRect) { snapshot.save(); let shapeRect = this._computeBoundingBox(pangoLayout, cellRect); let borderWidth = 1; let boxBgColor = styleContext.get_color(); boxBgColor.alpha = 0.05; snapshot.append_color(boxBgColor, new Graphene.Rect({ origin: new Graphene.Point({ x: shapeRect.x - borderWidth * 2, y: shapeRect.y - borderWidth * 2, }), size: new Graphene.Size({ width: shapeRect.width + borderWidth * 2, height: shapeRect.height + borderWidth * 2, }), }), ); snapshot.restore(); } _drawCharacterName(snapshot, pangoContext, styleContext, cellRect, uc) { snapshot.save(); let layout = Pango.Layout.new(pangoContext); layout.set_width(cellRect.width * Pango.SCALE * 0.8); layout.set_height(cellRect.height * Pango.SCALE * 0.8); layout.set_wrap(Pango.WrapMode.WORD); layout.set_ellipsize(Pango.EllipsizeMode.END); layout.set_alignment(Pango.Alignment.CENTER); layout.set_font_description(this._overlayFontDescription); let name = Gc.character_name(uc); let text = name === null ? _('Unassigned') : Util.capitalize(name); layout.set_text(text, -1); let logicalRect = layout.get_extents()[0]; snapshot.translate(new Graphene.Point({ x: cellRect.x - logicalRect.x / Pango.SCALE + (cellRect.width - logicalRect.width / Pango.SCALE) / 2, y: cellRect.y - logicalRect.y / Pango.SCALE + (cellRect.height - logicalRect.height / Pango.SCALE) / 2, })); let textColor = styleContext.get_color(); snapshot.append_layout(layout, textColor); snapshot.restore(); } }); var CharactersView = GObject.registerClass({ Signals: { 'character-selected': { param_types: [GObject.TYPE_STRING] }, }, Properties: { 'vscroll-policy': GObject.ParamSpec.override('vscroll-policy', Gtk.Scrollable), 'vadjustment': GObject.ParamSpec.override('vadjustment', Gtk.Scrollable), 'hscroll-policy': GObject.ParamSpec.override('hscroll-policy', Gtk.Scrollable), 'hadjustment': GObject.ParamSpec.override('hadjustment', Gtk.Scrollable), 'loading': GObject.ParamSpec.boolean( 'loading', 'Loading', 'Whether the category is still loading', GObject.ParamFlags.READWRITE, false, ), 'baseline': GObject.ParamSpec.boolean( 'baseline', 'Baseline', 'Whether to draw a baseline or not', GObject.ParamFlags.READWRITE, true, ), }, Implements: [Gtk.Scrollable], }, class CharactersView extends Gtk.Widget { _init() { super._init({ vadjustment: new Gtk.Adjustment(), hadjustment: new Gtk.Adjustment(), overflow: Gtk.Overflow.HIDDEN, }); this._scripts = []; this._scriptsLoaded = false; let context = this.get_pango_context(); this._fontDescription = context.get_font_description(); this._fontDescription.set_size(CELL_SIZE * Pango.SCALE); this._selectedCharacter = null; this._characters = []; this._searchContext = null; this._cancellable = new Gio.Cancellable(); this._cancellable.connect(() => { this._searchContext = null; this._characters = []; }); this._cellsPerRow = CELLS_PER_ROW; this._numRows = NUM_ROWS; this._rows = []; /* this.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, null, Gdk.DragAction.COPY); this.drag_source_add_text_targets(); */ const gestureClick = new Gtk.GestureClick(); gestureClick.connect('pressed', this.onButtonPress.bind(this)); gestureClick.connect('released', this.onButtonRelease.bind(this)); this.add_controller(gestureClick); } get fontDescription() { return this._fontDescription; } get vadjustment() { return this._vadjustment; } set vadjustment(adj) { adj.connect('value-changed', () => { this.queue_draw(); }); this._vadjustment = adj; } get hadjustment() { return this._hadjustment; } set hadjustment(adj) { adj.connect('value-changed', () => { this.queue_draw(); }); this._hadjustment = adj; } /* vfunc_drag_begin(context) { let cellSize = getCellSize(this._fontDescription); this._dragSurface = new Cairo.ImageSurface(Cairo.Format.ARGB32, cellSize, cellSize); let cr = new Cairo.Context(this._dragSurface); cr.setSourceRGBA(1.0, 1.0, 1.0, 1.0); cr.paint(); cr.setSourceRGBA(0.0, 0.0, 0.0, 1.0); let row = this._createCharacterListRow([this._character]); row.draw(cr, 0, 0, cellSize, cellSize, this.get_style_context()); Gtk.drag_set_icon_surface(context, this._dragSurface, 0, 0); } vfunc_drag_data_get(context, data, info, time) { if (this._character !== null) data.set_text(this._character, -1); } */ onButtonPress(_gesture, _nPress, x, y) { let hadj = this.get_hadjustment(); let vadj = this.get_vadjustment(); let cellSize = getCellSize(this._fontDescription); x = Math.min(this._cellsPerRow - 1, Math.floor((x + hadj.get_value() - this._offsetX) / cellSize)); y = Math.floor((y + vadj.get_value()) / cellSize); let index = y * this._cellsPerRow + Math.max(0, x); if (index < this._characters.length) this._selectedCharacter = this._characters[index]; else this._selectedCharacter = null; } onButtonRelease() { if (this._selectedCharacter) this.emit('character-selected', this._selectedCharacter); } vfunc_measure(orientation, _forSize) { if (orientation === Gtk.Orientation.HORIZONTAL) { let cellSize = getCellSize(this._fontDescription); let minWidth = NUM_COLUMNS * cellSize; let natWidth = Math.max(this._cellsPerRow, NUM_COLUMNS) * cellSize; return [minWidth, natWidth, -1, -1]; } else { let height = Math.max(this._rows.length, this._numRows) * getCellSize(this._fontDescription); return [height, height, -1, -1]; } } vfunc_snapshot(snapshot) { let vadj = this.get_vadjustment(); let styleContext = this.get_style_context(); let pangoContext = this.get_pango_context(); let cellSize = getCellSize(this._fontDescription); let scrollPos = Math.floor(vadj.get_value()); let start = Math.max(0, Math.floor(scrollPos / cellSize)); let end = Math.min(this._rows.length, Math.ceil((scrollPos + vadj.get_page_size()) / cellSize)); let offsetY = scrollPos % cellSize; snapshot.translate(new Graphene.Point({ x: 0, y: -offsetY })); let borderColor = styleContext.lookup_color('borders')[1]; let allocatedWidth = this.get_allocation().width; this._offsetX = (allocatedWidth - cellSize * this._cellsPerRow) / 2; for (let index = start; index < end; index++) { let y = (index - start) * cellSize; // Draw baseline. if (this.baseline) { snapshot.append_color(borderColor, new Graphene.Rect({ origin: new Graphene.Point({ x: 0, y: y + BASELINE_OFFSET * cellSize }), size: new Graphene.Size({ width: allocatedWidth, height: 1.0 }), })); } this._rows[index].snapshot(snapshot, this._offsetX, y, pangoContext, styleContext); } } vfunc_size_allocate(width, height, baseline) { super.vfunc_size_allocate(width, height, baseline); let cellSize = getCellSize(this._fontDescription); let cellsPerRow = Math.floor(width / cellSize); if (cellsPerRow !== this._cellsPerRow) { // Reflow if the number of cells per row has changed. this._cellsPerRow = cellsPerRow; this._reflow(); } let maxHeight = Math.floor((this._rows.length + BASELINE_OFFSET) * cellSize); let maxWidth = cellsPerRow * cellSize; let hadj = this.get_hadjustment(); let vadj = this.get_vadjustment(); vadj.configure(vadj.get_value(), 0.0, maxHeight, 0.1 * height, 0.9 * height, Math.min(height, maxHeight)); hadj.configure(hadj.get_value(), 0.0, maxWidth, 0.1 * width, 0.9 * width, Math.min(width, maxWidth)); } _createCharacterListRow(characters) { var context = this.get_pango_context(); var overlayFontDescription = context.get_font_description(); overlayFontDescription.set_size(overlayFontDescription.get_size() * 0.8); let row = new CharacterListRow(characters, this._fontDescription, overlayFontDescription); return row; } setFontDescription(fontDescription) { this._fontDescription = fontDescription; } _reflow() { this._rows = []; let start = 0, stop = 1; for (; stop <= this._characters.length; stop++) { if (stop % this._cellsPerRow === 0) { let rowCharacters = this._characters.slice(start, stop); let row = this._createCharacterListRow(rowCharacters); this._rows.push(row); start = stop; } } if (start !== stop - 1) { let rowCharacters = this._characters.slice(start, stop); let row = this._createCharacterListRow(rowCharacters); this._rows.push(row); } } setCharacters(characters) { this._characters = characters; this._reflow(); this.queue_resize(); } _addSearchResult(result) { const characters = Util.searchResultToArray(result); this.setCharacters(this._characters.concat(characters)); } async _searchWithContext(context) { try { let result = await context.search(Number.MAX_SAFE_INTEGER, this._cancellable); this._addSearchResult(result); } catch (e) { log(`Failed to search: ${e.message}`); } } async searchByCategory(category) { this._characters = []; // whether to draw a baseline or not this.baseline = category <= Gc.Category.LETTER_LATIN; if (category === Gc.Category.LETTER_LATIN) { if (!this._scriptsLoaded) await this.populateScripts(); // we run the search once the scripts are loaded await this._searchByScripts(); return; } let criteria = Gc.SearchCriteria.new_category(category); this._searchContext = new Gc.SearchContext({ criteria }); await this._searchWithContext(this._searchContext); } async searchByKeywords(keywords) { const criteria = Gc.SearchCriteria.new_keywords(keywords); this._searchContext = new Gc.SearchContext({ criteria, flags: Gc.SearchFlag.WORD, }); await this._searchWithContext(this._searchContext); return this._characters.length; } async _searchByScripts() { var criteria = Gc.SearchCriteria.new_scripts(this.scripts); this._searchContext = new Gc.SearchContext({ criteria }); await this._searchWithContext(this._searchContext); } cancelSearch() { this._cancellable.cancel(); this._cancellable.reset(); } // / Specific to GC_CATEGORY_LETTER_LATIN get scripts() { return this._scripts; } // / Populate the "scripts" based on the current locale // / and the input-sources settings. async populateScripts() { this.loading = true; let settings = Util.getSettings('org.gnome.desktop.input-sources', '/org/gnome/desktop/input-sources/'); if (settings) { let sources = settings.get_value('sources').deep_unpack(); let hasIBus = sources.some((current, _index, _array) => { return current[0] === 'ibus'; }); if (hasIBus) await this._ensureIBusLanguageList(sources); else this._finishBuildScriptList(sources); } } async _ensureIBusLanguageList(sources) { if (this._ibusLanguageList !== null) return; this._ibusLanguageList = {}; // Don't assume IBus is always available. let ibus; try { ibus = imports.gi.IBus; } catch (e) { this._finishBuildScriptList(sources); return; } Gio._promisify(ibus.Bus.prototype, 'list_engines_async', 'list_engines_async_finish'); ibus.init(); let bus = new ibus.Bus(); if (bus.is_connected()) { let engines = await bus.list_engines_async(-1, null); try { for (let j in engines) { let engine = engines[j]; let language = engine.get_language(); if (language !== null) this._ibusLanguageList[engine.get_name()] = language; } } catch (e) { log(`Failed to list engines: ${e.message}`); } } this._finishBuildScriptList(sources); } _finishBuildScriptList(sources) { let xkbInfo = new GnomeDesktop.XkbInfo(); let languages = []; for (let i in sources) { let [type, id] = sources[i]; switch (type) { case 'xkb': // FIXME: Remove this check once gnome-desktop gets the // support for that. if (xkbInfo.get_languages_for_layout) { languages = languages.concat( xkbInfo.get_languages_for_layout(id)); } break; case 'ibus': if (id in this._ibusLanguageList) languages.push(this._ibusLanguageList[id]); break; } } // Add current locale language to languages. languages.push(Gc.get_current_language()); let allScripts = []; for (let i in languages) { let language = GnomeDesktop.normalize_locale(languages[i]); if (language === null) continue; let scripts = Gc.get_scripts_for_language(languages[i]); for (let j in scripts) { let script = scripts[j]; // Exclude Latin and Han, since Latin is always added // at the top and Han contains too many characters. if ([GLib.UnicodeScript.LATIN, GLib.UnicodeScript.HAN].indexOf(script) >= 0) continue; if (allScripts.indexOf(script) >= 0) continue; allScripts.push(script); } } allScripts.unshift(GLib.UnicodeScript.LATIN); this._scripts = allScripts; this._scriptsLoaded = true; this.loading = false; this._searchByScripts(); } }); (uuay)Characters/ searchProvider.js � /* exported SearchProvider */ // -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- // // Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com> // Copyright (C) 2015 Daiki Ueno <dueno@src.gnome.org> // // Gnome Weather 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. // // Gnome Weather 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 Gnome Weather; if not, write to the Free Software Foundation, // Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA const { Gc, Gio, GLib, GObject } = imports.gi; const ByteArray = imports.byteArray; const Util = imports.util; const MAX_SEARCH_RESULTS = 20; const SearchProviderInterface = ByteArray.toString(Gio.resources_lookup_data('/org/gnome/shell/ShellSearchProvider2.xml', 0).toArray()); var SearchProvider = GObject.registerClass({ Name: 'CharactersSearchProvider', }, class SearchProvider extends GObject.Object { _init(application) { this._app = application; this._impl = Gio.DBusExportedObject.wrapJSObject(SearchProviderInterface, this); this._cancellable = new Gio.Cancellable(); } export(connection, path) { return this._impl.export(connection, path); } unexport(connection) { return this._impl.unexport_from_connection(connection); } _runQuery(keywords, invocation) { this._app.hold(); this._cancellable.cancel(); this._cancellable.reset(); let upper = keywords.map(x => x.toUpperCase()); let criteria = Gc.SearchCriteria.new_keywords(upper); let context = new Gc.SearchContext({ criteria, flags: Gc.SearchFlag.WORD }); context.search( MAX_SEARCH_RESULTS, this._cancellable, (_source, res, _userData) => { let characters = []; try { let result = context.search_finish(res); characters = Util.searchResultToArray(result); } catch (e) { log(`Failed to search by keywords: ${e.message}`); } invocation.return_value(new GLib.Variant('(as)', [characters])); this._app.release(); }); } GetInitialResultSetAsync(params, invocation) { this._runQuery(params[0], invocation); } GetSubsearchResultSetAsync(params, invocation) { this._runQuery(params[1], invocation); } GetResultMetas(identifiers) { this._app.hold(); let ret = []; for (let i = 0; i < identifiers.length; i++) { let character = identifiers[i]; let name = Gc.character_name(character); if (name === null) name = _('Unknown character name'); else name = Util.capitalize(name); let summary = ''; if (!Gc.character_is_composite(character)) { let codePoint = Util.toCodePoint(character); let codePointHex = codePoint.toString(16).toUpperCase(); summary = _('U+%s').format(codePointHex); } let iconData = Util.characterToIconData(character); if (!iconData) continue; ret.push({ name: new GLib.Variant('s', name), id: new GLib.Variant('s', identifiers[i]), description: new GLib.Variant('s', summary), clipboardText: new GLib.Variant('s', character), 'icon-data': iconData, }); } this._app.release(); return ret; } ActivateResult(_id, _terms, _timestamp) { log('activating result'); const notification = Gio.Notification.new(_('Character copied')); notification.set_body(_('Character was copied successfully')); this._app.send_notification(null, notification); } LaunchSearch(terms, timestamp) { this._app.activate(); const window = this._app.window; window.setSearchKeywords(terms); window.present_with_time(timestamp); } }); (uuay)util.js /* exported capitalize getSettings initActions searchResultToArray toCodePoint characterToIconData */ // -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- // // Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com> // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the GNOME Foundation nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. const { Gc, Gio, Gdk, GLib, Graphene, Gsk, Gtk, PangoCairo, Pango } = imports.gi; const System = imports.system; function initActions(actionMap, simpleActionEntries, context) { simpleActionEntries.forEach(({ name, parameterType, state, activate }) => { let action = new Gio.SimpleAction({ name, parameter_type: parameterType || null, state: state || null, }); context = context || actionMap; if (activate) action.connect('activate', activate.bind(context)); actionMap.add_action(action); }); } function getSettings(schemaId, path) { const GioSSS = Gio.SettingsSchemaSource; let schemaSource; if (!pkg.moduledir.startsWith('resource://')) { // Running from the source tree schemaSource = GioSSS.new_from_directory(pkg.pkgdatadir, GioSSS.get_default(), false); } else { schemaSource = GioSSS.get_default(); } let schemaObj = schemaSource.lookup(schemaId, true); if (!schemaObj) { log(`Missing GSettings schema ${schemaId}`); System.exit(1); } if (path === undefined) return new Gio.Settings({ settings_schema: schemaObj }); else return Gio.Settings.new_full(schemaObj, null, path); } function capitalizeWord(w) { if (w.length > 0) return w[0].toUpperCase() + w.slice(1).toLowerCase(); return w; } function capitalize(s) { return s.split(/\s+/).map(w => { let acronyms = ['CJK']; if (acronyms.indexOf(w) > -1) return w; let prefixes = ['IDEOGRAPH-', 'SELECTOR-']; for (let index in prefixes) { let prefix = prefixes[index]; if (w.startsWith(prefix)) return capitalizeWord(prefix) + w.slice(prefix.length); } return capitalizeWord(w); }).join(' '); } function toCodePoint(s) { let codePoint = s.charCodeAt(0); if (codePoint >= 0xD800 && codePoint <= 0xDBFF) { let high = codePoint; let low = s.charCodeAt(1); codePoint = 0x10000 + (high - 0xD800) * 0x400 + (low - 0xDC00); } return codePoint; } function searchResultToArray(result) { let characters = []; for (let index = 0; index < result.len; index++) { const c = Gc.search_result_get(result, index); if (c.trim().length) characters.push(c); } return characters; } function characterToIconData(character) { let size = 48.0; if (!character || !character.trim().length) return null; const fontMap = PangoCairo.FontMap.get_default(); const context = fontMap.create_context(); const layout = Pango.Layout.new(context); layout.set_text(character, -1); let white = new Gdk.RGBA({ red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 }); let [textWidth, textHeight] = layout.get_pixel_size(); let textSize = Math.max(textWidth, textHeight); const snapshot = Gtk.Snapshot.new(); let originX = (textSize - textWidth) / 2.0; let originY = (textSize - textHeight) / 2.0; let origin = new Graphene.Point({ x: originX, y: originY }); let ratio = size / textSize; snapshot.scale(ratio, ratio); snapshot.save(); snapshot.translate(origin); snapshot.append_layout(layout, white); snapshot.restore(); const node = snapshot.to_node(); // The snapshot may contain no nodes if there's nothing to render, like in // case of a layout that only contains invisible chars: // https://gitlab.gnome.org/GNOME/gtk/-/issues/5747 if (!node) return null; let renderer = Gsk.GLRenderer.new(); try { renderer.realize(null); } catch (e) { renderer = new Gsk.CairoRenderer(); renderer.realize(null); } let rect = new Graphene.Rect({ origin: new Graphene.Point({ x: 0.0, y: 0.0 }), size: new Graphene.Size({ width: size, height: size }), }); const texture = renderer.render_texture(node, rect); renderer.unrealize(); const textureDownloader = new Gdk.TextureDownloader(texture); textureDownloader.set_format(Gdk.MemoryFormat.R8G8B8A8); const [bytes, stride] = textureDownloader.download_bytes(); return GLib.Variant.new_tuple([ new GLib.Variant('i', texture.get_width()), new GLib.Variant('i', texture.get_height()), new GLib.Variant('i', stride), new GLib.Variant('b', /* has alpha */ true), new GLib.Variant('i', /* bits per sample */ 8), new GLib.Variant('i', /* channels */ 4), GLib.Variant.new_from_bytes( GLib.VariantType.new('ay'), bytes, true), ]); } (uuay)sidebarRow.js � /* exported SidebarRow */ // -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- // // Copyright (C) 2014-2017 Daiki Ueno <dueno@src.gnome.org> // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. const { Gc, GObject, Gtk } = imports.gi; var SidebarRow = GObject.registerClass({ Properties: { 'title': GObject.ParamSpec.string( 'title', 'Category title', 'Category title', GObject.ParamFlags.READWRITE, '', ), 'category': GObject.ParamSpec.enum( 'category', 'Category', 'Category', GObject.ParamFlags.READWRITE, Gc.Category.$gtype, Gc.Category.NONE, ), 'icon-name': GObject.ParamSpec.string( 'icon-name', 'Category Icon Name', 'Category Icon Name', GObject.ParamFlags.READWRITE, '', ), }, }, class SidebarRow extends Gtk.ListBoxRow { _init() { super._init({ accessible_role: Gtk.AccessibleRole.ROW, }); let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, margin_top: 12, margin_bottom: 12, margin_start: 6, margin_end: 6, spacing: 12, }); let image = new Gtk.Image(); this.bind_property('icon-name', image, 'icon-name', GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE, ); image.set_icon_size(Gtk.IconSize.LARGE_TOOLBAR); hbox.append(image); let label = new Gtk.Label({ halign: Gtk.Align.START }); this.bind_property('title', label, 'label', GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE, ); // Because bind_property doesn't work with transform functions // TODO: is this really needed? this.connect('notify::title', row => { row.tooltip_text = _('%s Sidebar Row').format(row.title); }); hbox.append(label); this.set_child(hbox); } get title() { return this._title || ''; } set title(title) { this._title = title; } get category() { return this._category || Gc.Category.NONE; } set category(value) { this._category = value; } }); (uuay)sidebar.js � /* exported Sidebar */ // -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- // // Copyright (C) 2014-2017 Daiki Ueno <dueno@src.gnome.org> // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. const { Adw, GObject } = imports.gi; const { SidebarRow } = imports.sidebarRow; var Sidebar = GObject.registerClass({ Template: 'resource:///org/gnome/Characters/sidebar.ui', InternalChildren: [ 'list', 'recentRow', 'emojiSmileysRow', 'emojiPeopleRow', 'emojiAnimalsRow', 'emojiFoodRow', 'emojiActivitiesRow', 'emojiTravelRow', 'emojiObjectsRow', 'emojiSymbolsRow', 'emojiFlagsRow', 'lettersPunctuationRow', 'lettersArrowsRow', 'lettersBulletsRow', 'lettersPicturesRow', 'lettersCurrencyRow', 'lettersMathRow', 'lettersLatinRow', ], }, class Sidebar extends Adw.Bin { _init() { GObject.type_ensure(SidebarRow.$gtype); super._init({}); this.lastSelectedRow = null; } /** * Restore the latest selected item */ restoreSelection() { if (this.lastSelectedRow) this._list.select_row(this.lastSelectedRow); } rowByName(name) { switch (name) { case 'smileys': return this._emojiSmileysRow; case 'people': return this._emojiPeopleRow; case 'animals': return this._emojiAnimalsRow; case 'food': return this._emojiFoodRow; case 'activities': return this._emojiActivitesRow; case 'travel': return this._emojiTravelRow; case 'objects': return this._emojiObjectsRow; case 'symbols': return this._emojiSymbolsRow; case 'flags': return this._emojiFlagsRow; case 'punctuation': return this._lettersPunctuationRow; case 'arrows': return this._lettersArrowsRow; case 'bullets': return this._lettersBulletsRow; case 'pictures': return this._lettersPicturesRow; case 'currency': return this._lettersCurrencyRow; case 'math': return this._lettersMathRow; case 'latin': return this._lettersLatinRow; default: return this._recentRow; } } selectRowByName(name) { let row = this.rowByName(name); this._list.select_row(row); } unselectAll() { this._list.unselect_all(); } get list() { return this._list; } }); (uuay)window.js�&