515 lines
22 KiB
JavaScript
515 lines
22 KiB
JavaScript
/*
|
|
* Copyright 2022 zhrexl
|
|
* Copyright 2024 Dave Prowse
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
* SPDX-FileCopyrightText: 2024 Dave Prowse
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
* SPDX-FileContributor: Modified by Dave Prowse
|
|
*/
|
|
|
|
/* eslint version: 9.16 (2024) */
|
|
|
|
import Adw from 'gi://Adw';
|
|
import Gdk from 'gi://Gdk';
|
|
import GLib from 'gi://GLib';
|
|
import Gtk from 'gi://Gtk';
|
|
import GObject from 'gi://GObject';
|
|
import Gio from 'gi://Gio';
|
|
|
|
import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
|
|
|
import * as GimpPaletteParser from '../gimpPaletteParser.js';
|
|
import { CURATED_UUID as UUID } from '../utils.js';
|
|
|
|
|
|
const MARGIN = 10;
|
|
const ROWBOX_MARGIN_PARAMS = { margin_top: MARGIN / 2, margin_bottom: MARGIN / 2, margin_start: MARGIN, margin_end: MARGIN, spacing: 4 };
|
|
|
|
const DrawingPage = GObject.registerClass({
|
|
GTypeName: `${UUID}-Drawing`
|
|
}, class DrawingPage extends Adw.PreferencesPage {
|
|
|
|
constructor(extensionPreferences, window) {
|
|
super({});
|
|
|
|
this.window = window;
|
|
this.set_title("Drawing Page");
|
|
this.set_name('drawing');
|
|
this.set_icon_name("applications-graphics-symbolic");
|
|
|
|
// Use cached drawingSettings from extensionPreferences
|
|
this.settings = window._drawingSettings;
|
|
|
|
this.schema = this.settings.settings_schema;
|
|
|
|
this.palettes = this.settings.get_value('palettes').deep_unpack();
|
|
|
|
// New constructor code above, below is the same
|
|
this.palettes_adw_group = Adw.PreferencesGroup.new()
|
|
this.palettes_adw_group.set_title(_("Palettes"));
|
|
|
|
this.PalettesActionRow = Adw.ActionRow.new();
|
|
|
|
this.PalettesActionRow.set_title(_("New Palette"));
|
|
this.PalettesActionRow.set_subtitle(_("Create or Import palette"));
|
|
let addButton = Gtk.Button.new_from_icon_name('list-add-symbolic');
|
|
addButton.set_tooltip_text(_("Add a new palette"));
|
|
addButton.valign = Gtk.Align.CENTER;
|
|
addButton.connect('clicked', this._addNewPalette.bind(this));
|
|
|
|
let importButton = Gtk.Button.new_from_icon_name('document-open-symbolic');
|
|
importButton.set_tooltip_text(_("Select a File"));
|
|
importButton.valign = Gtk.Align.CENTER;
|
|
importButton.connect('clicked', this._importPalette.bind(this));
|
|
|
|
this.PalettesActionRow.add_suffix(addButton);
|
|
this.PalettesActionRow.add_suffix(importButton);
|
|
this.palettes_adw_group.add(this.PalettesActionRow);
|
|
|
|
this.nPalettes = [];
|
|
this.settings.connect('changed::palettes', this._updatePalettes.bind(this));
|
|
this._updatePalettes();
|
|
|
|
/* Area Group */
|
|
let adw_groupArea = Adw.PreferencesGroup.new()
|
|
adw_groupArea.set_title(_("Area"));
|
|
|
|
let squareAreaRow = Adw.ActionRow.new();
|
|
squareAreaRow.set_title(this.schema.get_key('square-area-size').get_summary());
|
|
let squareAreaAutoButton = new Gtk.CheckButton({ label: _("Auto"),
|
|
name: this.schema.get_key('square-area-auto').get_summary(),
|
|
tooltip_text: this.schema.get_key('square-area-auto').get_description() });
|
|
let squareAreaSizeButton = new PixelSpinButton({ width_chars: 5, digits: 0, step: 1,
|
|
range: this.schema.get_key('square-area-size').get_range(),
|
|
name: this.schema.get_key('square-area-size').get_summary(),
|
|
tooltip_text: this.schema.get_key('square-area-size').get_description() });
|
|
this.settings.bind('square-area-auto', squareAreaAutoButton, 'active', 0);
|
|
this.settings.bind('square-area-size', squareAreaSizeButton, 'value', 0);
|
|
squareAreaAutoButton.bind_property('active', squareAreaSizeButton, 'sensitive', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
|
|
|
|
squareAreaAutoButton.valign = Gtk.Align.CENTER;
|
|
squareAreaSizeButton.valign = Gtk.Align.CENTER;
|
|
|
|
squareAreaRow.add_suffix(squareAreaAutoButton);
|
|
squareAreaRow.add_suffix(squareAreaSizeButton);
|
|
|
|
|
|
let backgroundColorRow = Adw.ActionRow.new();
|
|
backgroundColorRow.set_title(this.schema.get_key('background-color').get_summary());
|
|
|
|
let backgroundColorButton = new ColorStringButton({ use_alpha: true, show_editor: true,
|
|
name: this.schema.get_key('background-color').get_summary(),
|
|
tooltip_text: this.schema.get_key('background-color').get_description() });
|
|
this.settings.bind('background-color', backgroundColorButton, 'color-string', 0);
|
|
|
|
backgroundColorButton.valign = Gtk.Align.CENTER;
|
|
backgroundColorRow.add_suffix(backgroundColorButton);
|
|
|
|
let gridLineRow = Adw.ActionRow.new();
|
|
gridLineRow.set_title( _("Grid overlay line"));
|
|
|
|
let gridLineAutoButton = new Gtk.CheckButton({ label: _("Auto"),
|
|
name: this.schema.get_key('grid-line-auto').get_summary(),
|
|
tooltip_text: this.schema.get_key('grid-line-auto').get_description() });
|
|
let gridLineWidthButton = new PixelSpinButton({ width_chars: 5, digits: 1, step: 0.1,
|
|
range: this.schema.get_key('grid-line-width').get_range(),
|
|
name: this.schema.get_key('grid-line-width').get_summary(),
|
|
tooltip_text: this.schema.get_key('grid-line-width').get_description() });
|
|
let gridLineSpacingButton = new PixelSpinButton({ width_chars: 5, digits: 1, step: 1,
|
|
range: this.schema.get_key('grid-line-spacing').get_range(),
|
|
name: this.schema.get_key('grid-line-spacing').get_summary(),
|
|
tooltip_text: this.schema.get_key('grid-line-spacing').get_description() });
|
|
|
|
this.settings.bind('grid-line-auto', gridLineAutoButton, 'active', 0);
|
|
this.settings.bind('grid-line-width', gridLineWidthButton, 'value', 0);
|
|
this.settings.bind('grid-line-spacing', gridLineSpacingButton, 'value', 0);
|
|
|
|
gridLineAutoButton.bind_property('active', gridLineWidthButton, 'sensitive', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
|
|
gridLineAutoButton.bind_property('active', gridLineSpacingButton, 'sensitive', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
|
|
|
|
gridLineAutoButton.valign = Gtk.Align.CENTER;
|
|
gridLineWidthButton.valign = Gtk.Align.CENTER;
|
|
gridLineSpacingButton.valign = Gtk.Align.CENTER;
|
|
|
|
gridLineRow.add_suffix(gridLineAutoButton);
|
|
gridLineRow.add_suffix(gridLineWidthButton);
|
|
gridLineRow.add_suffix(gridLineSpacingButton);
|
|
|
|
let gridColorRow = Adw.ActionRow.new();
|
|
gridColorRow.set_title(this.schema.get_key('grid-color').get_summary());
|
|
|
|
let gridColorButton = new ColorStringButton({ use_alpha: true, show_editor: true,
|
|
name: this.schema.get_key('grid-color').get_summary(),
|
|
tooltip_text: this.schema.get_key('grid-color').get_description() });
|
|
this.settings.bind('grid-color', gridColorButton, 'color-string', 0);
|
|
gridColorRow.add_suffix(gridColorButton);
|
|
gridColorButton.valign = Gtk.Align.CENTER;
|
|
|
|
adw_groupArea.add(squareAreaRow);
|
|
adw_groupArea.add(backgroundColorRow);
|
|
adw_groupArea.add(gridLineRow);
|
|
adw_groupArea.add(gridColorRow);
|
|
|
|
/* End of Area Group */
|
|
|
|
/* Tools Group */
|
|
let adw_groupTools = Adw.PreferencesGroup.new()
|
|
adw_groupTools.set_title(_("Tools"));
|
|
|
|
|
|
let dashArrayRow = Adw.ActionRow.new();
|
|
dashArrayRow.set_title(_("Dash array"));
|
|
|
|
let dashArrayAutoButton = new Gtk.CheckButton({ label: _("Auto"),
|
|
name: this.schema.get_key('dash-array-auto').get_summary(),
|
|
tooltip_text: this.schema.get_key('dash-array-auto').get_description() });
|
|
let dashArrayOnButton = new PixelSpinButton({ width_chars: 5, digits: 1, step: 0.1,
|
|
range: this.schema.get_key('dash-array-on').get_range(),
|
|
name: this.schema.get_key('dash-array-on').get_summary(),
|
|
tooltip_text: this.schema.get_key('dash-array-on').get_description() });
|
|
let dashArrayOffButton = new PixelSpinButton({ width_chars: 5, digits: 1, step: 0.1,
|
|
range: this.schema.get_key('dash-array-off').get_range(),
|
|
name: this.schema.get_key('dash-array-off').get_summary(),
|
|
tooltip_text: this.schema.get_key('dash-array-off').get_description() });
|
|
this.settings.bind('dash-array-auto', dashArrayAutoButton, 'active', 0);
|
|
this.settings.bind('dash-array-on', dashArrayOnButton, 'value', 0);
|
|
this.settings.bind('dash-array-off', dashArrayOffButton, 'value', 0);
|
|
dashArrayAutoButton.bind_property('active', dashArrayOnButton, 'sensitive', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
|
|
dashArrayAutoButton.bind_property('active', dashArrayOffButton, 'sensitive', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
|
|
dashArrayAutoButton.valign = Gtk.Align.CENTER;
|
|
dashArrayOnButton.valign = Gtk.Align.CENTER;
|
|
dashArrayOffButton.valign = Gtk.Align.CENTER;
|
|
dashArrayRow.add_suffix(dashArrayAutoButton);
|
|
dashArrayRow.add_suffix(dashArrayOnButton);
|
|
dashArrayRow.add_suffix(dashArrayOffButton);
|
|
|
|
|
|
let dashOffsetRow = Adw.ActionRow.new();
|
|
dashOffsetRow.set_title(this.schema.get_key('dash-offset').get_summary());
|
|
|
|
let dashOffsetButton = new PixelSpinButton({ width_chars: 5, digits: 1, step: 0.1,
|
|
range: this.schema.get_key('dash-offset').get_range(),
|
|
name: this.schema.get_key('dash-offset').get_summary(),
|
|
tooltip_text: this.schema.get_key('dash-offset').get_description() });
|
|
this.settings.bind('dash-offset', dashOffsetButton, 'value', 0);
|
|
dashOffsetButton.valign = Gtk.Align.CENTER;
|
|
dashOffsetRow.add_suffix(dashOffsetButton);
|
|
|
|
let imageLocationRow = Adw.ActionRow.new();
|
|
imageLocationRow.set_title(this.schema.get_key('image-location').get_summary());
|
|
|
|
let imageLocationButton = new FileChooserButton({ action: Gtk.FileChooserAction.SELECT_FOLDER,
|
|
name: this.schema.get_key('image-location').get_summary(),
|
|
tooltip_text: this.schema.get_key('image-location').get_description() });
|
|
this.settings.bind('image-location', imageLocationButton, 'location', 0);
|
|
imageLocationButton.valign = Gtk.Align.CENTER;
|
|
imageLocationRow.add_suffix(imageLocationButton);
|
|
|
|
|
|
adw_groupTools.add(dashArrayRow);
|
|
adw_groupTools.add(dashOffsetRow);
|
|
adw_groupTools.add(imageLocationRow);
|
|
|
|
let resetButton = new Gtk.Button({ label: _("Reset settings"), halign: Gtk.Align.CENTER });
|
|
resetButton.get_style_context().add_class('destructive-action');
|
|
resetButton.connect('clicked', () => this.schema.list_keys().forEach(key => this.settings.reset(key)));
|
|
|
|
resetButton.set_margin_top(12);
|
|
adw_groupTools.add(resetButton);
|
|
/* End of Tools Group */
|
|
|
|
this.add(this.palettes_adw_group);
|
|
this.add(adw_groupArea);
|
|
this.add(adw_groupTools);
|
|
};
|
|
_updatePalettes() {
|
|
this.palettes = this.settings.get_value('palettes').deep_unpack();
|
|
/*TODO: I Should probably reutilize these widgets instead of just removing it */
|
|
this.nPalettes.forEach((actionrow) => {
|
|
if (actionrow){
|
|
this.palettes_adw_group.remove(actionrow);
|
|
}
|
|
});
|
|
this.nPalettes = [];
|
|
|
|
this.palettes.forEach((palette,index) => {
|
|
let tmpAction = Adw.ActionRow.new();
|
|
tmpAction.set_title(palette[0]);
|
|
this.nPalettes.push(tmpAction);
|
|
|
|
let EditButton = Gtk.Button.new_from_icon_name('edit-symbolic');
|
|
EditButton.set_tooltip_text(_("Edit this palette colors"));
|
|
EditButton.valign = Gtk.Align.CENTER;
|
|
EditButton.connect('clicked', () => {this._editPalette(palette, index)});
|
|
|
|
let RemoveButton = Gtk.Button.new_from_icon_name('edit-delete-symbolic');
|
|
RemoveButton.set_tooltip_text(_("Remove this palette"));
|
|
RemoveButton.valign = Gtk.Align.CENTER;
|
|
RemoveButton.connect('clicked', () => {
|
|
this.palettes.splice(index,1);
|
|
this._savePalettes();
|
|
});
|
|
|
|
tmpAction.add_suffix(EditButton);
|
|
tmpAction.add_suffix(RemoveButton);
|
|
|
|
this.palettes_adw_group.add(this.nPalettes[index]);
|
|
});
|
|
|
|
}
|
|
|
|
_savePalettes() {
|
|
this.settings.set_value('palettes', new GLib.Variant('a(sas)', this.palettes));
|
|
}
|
|
|
|
_onPaletteNameChanged(index, text) {
|
|
this.palettes[index][0] = text;
|
|
this._savePalettes();
|
|
}
|
|
|
|
_onPaletteColorChanged(paletteIndex, colorIndex, colorButton) {
|
|
this.palettes[paletteIndex][1][colorIndex] = colorButton.get_rgba().to_string();
|
|
if (colorButton.tooltip_text)
|
|
this.palettes[paletteIndex][1][colorIndex] += `:${colorButton.tooltip_text}`;
|
|
this._savePalettes();
|
|
}
|
|
|
|
_addNewPalette() {
|
|
let colors = Array(9).fill('Black');
|
|
// Translators: default name of a new palette
|
|
this.palettes.push([_("New palette"), colors]);
|
|
this._savePalettes();
|
|
}
|
|
|
|
_importPalette() {
|
|
let dialog = new Gtk.FileChooserDialog({
|
|
title: _("Select a File"),
|
|
action: Gtk.FileChooserAction.OPEN,
|
|
modal: true,
|
|
});
|
|
dialog.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL);
|
|
dialog.add_button(_("_Open"), Gtk.ResponseType.ACCEPT);
|
|
|
|
let filter = new Gtk.FileFilter();
|
|
filter.set_name("GIMP Palette (*.gpl)");
|
|
filter.add_pattern('*.gpl');
|
|
dialog.add_filter(filter);
|
|
|
|
dialog.connect('response', (dialog, response) => {
|
|
if (response == Gtk.ResponseType.ACCEPT) {
|
|
let file = dialog.get_file();
|
|
let palettes = GimpPaletteParser.parseFile(file);
|
|
palettes.forEach(palette => this.palettes.push(palette));
|
|
this._savePalettes();
|
|
}
|
|
dialog.destroy();
|
|
});
|
|
|
|
dialog.show();
|
|
}
|
|
_editPalette(palette, index)
|
|
{
|
|
let modal = new Gtk.Dialog({
|
|
default_height: 400,
|
|
default_width: 500,
|
|
modal: true,
|
|
transient_for: this.window,
|
|
title: 'Editing ' + palette[0] + ' palette',
|
|
use_header_bar: true
|
|
});
|
|
|
|
let contentArea = modal.get_content_area();
|
|
let preferencesPage = Adw.PreferencesPage.new();
|
|
let editor_palettes_group = Adw.PreferencesGroup.new()
|
|
editor_palettes_group.set_title(_("Customize Palette"));
|
|
|
|
let PalettesNameActionRow = Adw.ActionRow.new();
|
|
PalettesNameActionRow.set_title(_("Name"));
|
|
let EntryName = Gtk.EditableLabel.new(palette[0]);
|
|
|
|
EntryName.connect('changed', () => {
|
|
this._onPaletteNameChanged(index, EntryName.get_text());
|
|
});
|
|
|
|
EntryName.set_alignment(1);
|
|
PalettesNameActionRow.add_suffix(EntryName);
|
|
editor_palettes_group.add(PalettesNameActionRow);
|
|
|
|
palette[1].forEach((color, colorindex)=>{
|
|
let pActionRow = Adw.ActionRow.new();
|
|
let [cname, title] = color.split(":");
|
|
|
|
let colortitle = Gtk.EditableLabel.new(title || cname);
|
|
colortitle.connect('changed', () => {
|
|
this.palettes[index][1][colorindex] = cname+':'+colortitle.get_text();
|
|
this._savePalettes();
|
|
});
|
|
|
|
pActionRow.add_prefix(colortitle);
|
|
let colorEntry = new ColorStringButton({ use_alpha: true, show_editor: true,
|
|
name: cname,
|
|
tooltip_text: title || cname});
|
|
colorEntry.color_string = cname;
|
|
colorEntry.valign = Gtk.Align.CENTER;
|
|
colorEntry.connect('notify::color-string', () => {
|
|
this._onPaletteColorChanged(index,colorindex, colorEntry);
|
|
});
|
|
|
|
|
|
pActionRow.add_suffix(colorEntry);
|
|
editor_palettes_group.add(pActionRow);
|
|
});
|
|
|
|
preferencesPage.add(editor_palettes_group);
|
|
contentArea.append(preferencesPage);
|
|
modal.show();
|
|
}
|
|
_removePalette(paletteIndex) {
|
|
this.palettes.splice(paletteIndex, 1);
|
|
this._savePalettes();
|
|
}
|
|
});
|
|
|
|
|
|
const PixelSpinButton = new GObject.Class({
|
|
Name: `${UUID}-PixelSpinButton2`,
|
|
Extends: Gtk.SpinButton,
|
|
Properties: {
|
|
'range': GObject.param_spec_variant('range', 'range', 'GSettings range',
|
|
GLib.VariantType.new('(sv)'), null, GObject.ParamFlags.WRITABLE),
|
|
|
|
'step': GObject.ParamSpec.double('step', 'step', 'step increment',
|
|
GObject.ParamFlags.WRITABLE,
|
|
0, 1000, 1)
|
|
},
|
|
|
|
set range(range) {
|
|
let [type, variant] = range.deep_unpack();
|
|
if (type == 'range') {
|
|
let [min, max] = variant.deep_unpack();
|
|
this.adjustment.set_lower(min);
|
|
this.adjustment.set_upper(max);
|
|
}
|
|
},
|
|
|
|
set step(step) {
|
|
this.adjustment.set_step_increment(step);
|
|
this.adjustment.set_page_increment(step * 10);
|
|
},
|
|
|
|
on_output: function() {
|
|
this.text = _("%f px").format(Number(this.value).toFixed(2));
|
|
return true;
|
|
},
|
|
|
|
// Prevent accidental scrolling (GTK 3).
|
|
on_scroll_event: function(event) {
|
|
if (this.has_focus) {
|
|
try {
|
|
GObject.signal_chain_from_overridden([this, event], false);
|
|
} catch(e) { }
|
|
|
|
return Gdk.EVENT_STOP;
|
|
}
|
|
|
|
return Gdk.EVENT_PROPAGATE;
|
|
}
|
|
});
|
|
|
|
|
|
// A color button that can be easily bound with a color string setting.
|
|
const ColorStringButton = new GObject.Class({
|
|
Name: `${UUID}-ColorStringButton2`,
|
|
Extends: Gtk.ColorButton,
|
|
Properties: {
|
|
'color-string': GObject.ParamSpec.string('color-string', 'colorString', 'A string that describes the color',
|
|
GObject.ParamFlags.READWRITE, 'black')
|
|
},
|
|
|
|
get color_string() {
|
|
return this._color_string || 'black';
|
|
},
|
|
|
|
set color_string(colorString) {
|
|
this._color_string = colorString;
|
|
|
|
let newRgba = new Gdk.RGBA();
|
|
newRgba.parse(colorString);
|
|
this.set_rgba(newRgba);
|
|
},
|
|
|
|
on_color_set: function() {
|
|
let oldRgba = new Gdk.RGBA();
|
|
oldRgba.parse(this.color_string);
|
|
|
|
// Do nothing if the new color is equivalent to the old color (e.g. "black" and "rgb(0,0,0)").
|
|
if (!this.rgba.equal(oldRgba)) {
|
|
this._color_string = this.rgba.to_string();
|
|
this.notify('color-string');
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
const FileChooserButton = new GObject.Class({
|
|
Name: `${UUID}-FileChooserButton2`,
|
|
Extends: Gtk.Button,
|
|
Properties: {
|
|
'action': GObject.ParamSpec.enum('action', 'action', 'action',
|
|
GObject.ParamFlags.READWRITE,
|
|
Gtk.FileChooserAction.$gtype,
|
|
Gtk.FileChooserAction.SELECT_FOLDER),
|
|
|
|
'location': GObject.ParamSpec.string('location', 'location', 'location',
|
|
GObject.ParamFlags.READWRITE, '')
|
|
},
|
|
|
|
get location() {
|
|
return this._location || "";
|
|
},
|
|
|
|
set location(location) {
|
|
if (!this._location || this._location != location) {
|
|
this._location = location;
|
|
this.label = location ?
|
|
Gio.File.new_for_commandline_arg(location).query_info('standard::display-name', Gio.FileQueryInfoFlags.NONE, null).get_display_name() :
|
|
_("(None)");
|
|
|
|
this.notify('location');
|
|
}
|
|
},
|
|
|
|
vfunc_clicked: function() {
|
|
let dialog = new Gtk.FileChooserDialog({
|
|
title: _(this.name),
|
|
action: this.action,
|
|
modal: true,
|
|
});
|
|
dialog.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL);
|
|
dialog.add_button(_("_Select"), Gtk.ResponseType.ACCEPT);
|
|
|
|
if (this.location)
|
|
dialog.set_file(Gio.File.new_for_commandline_arg(this.location));
|
|
|
|
dialog.connect('response', (dialog, response) => {
|
|
if (response == Gtk.ResponseType.ACCEPT)
|
|
this.location = dialog.get_file().get_path();
|
|
dialog.destroy();
|
|
});
|
|
|
|
dialog.show();
|
|
}
|
|
});
|
|
|
|
export default DrawingPage; |