Skip to content
Snippets Groups Projects
Commit 9e4f5a19 authored by Andreas Traczyk's avatar Andreas Traczyk Committed by Sébastien Blin
Browse files

misc: regenerate qml.qrc at configure time

Our qml.qrc is:
- commonly in conflict and prone to erroneous conflict resolution
- platform dependant (generation is used anyway for removing
  webengine dependant resources)
- a poorly maintained hodge-podge with resources in subdirectories
  that aren't grouped
- requires alteration when moving resources (if not done correctly,
  without tests, results in uncaught runtime failures)

This patch uses a python script at configure time to generate the
qml.qrc resource file, and removes it from versioning.

GitLab: #749
Change-Id: Ia2b81bb5b2c29d0bf6f5a5302e76795864e93e40
parent a230365a
No related branches found
No related tags found
No related merge requests found
Showing with 135 additions and 282 deletions
......@@ -26,8 +26,8 @@ install-local/
.deploy.stamp
# auto-gen files
src/app/qml_without_webengine.qrc
src/app/resources.qrc
src/app/qml.qrc
src/app/constant/JamiResources.qml
# macOS
......
......@@ -125,17 +125,32 @@ include(FindPython3)
find_package(Python3 3.6 REQUIRED COMPONENTS Interpreter)
set(PYTHON_EXEC ${Python3_EXECUTABLE})
set(QML_RESOURCES ${APP_SRC_DIR}/resources.qrc)
# Resource auto-gen
# QML and related code files
# Check files in the app's src directory and force a reconfigure if it
# changes.
# Only include webengine resources if specified.
if(WITH_WEBENGINE)
set(QML_RESOURCES_QML ${APP_SRC_DIR}/qml.qrc)
else()
execute_process(
COMMAND
${PYTHON_EXEC} ${SCRIPTS_DIR}/gen-qrc-without-webengine.py
WORKING_DIRECTORY ${APP_SRC_DIR})
set(QML_RESOURCES_QML
${APP_SRC_DIR}/qml_without_webengine.qrc)
set(GEN_QML_QRC_ARGS "--with-webengine")
endif()
file(GLOB_RECURSE
QML_FILES CONFIGURE_DEPENDS
${APP_SRC_DIR}/*)
execute_process(
COMMAND
${PYTHON_EXEC} ${SCRIPTS_DIR}/gen_qml_qrc.py ${GEN_QML_QRC_ARGS}
WORKING_DIRECTORY ${APP_SRC_DIR})
set(QML_RESOURCES_QML ${APP_SRC_DIR}/qml.qrc)
# Image and misc. resources
# check files in the resources directory and force a reconfigure if it
# changes
file(GLOB_RECURSE
RES_FILES CONFIGURE_DEPENDS
${PROJECT_SOURCE_DIR}/resources/*)
execute_process(
COMMAND ${PYTHON_EXEC} ${SCRIPTS_DIR}/gen_resources_qrc.py
WORKING_DIRECTORY ${APP_SRC_DIR})
set(QML_RESOURCES ${APP_SRC_DIR}/resources.qrc)
if (APPLE)
include(FetchContent)
......@@ -148,16 +163,6 @@ if (APPLE)
include_directories(${libqrencode_SOURCE_DIR})
endif()
# Resource auto-gen
# check files in the resources directory and force a reconfigure if it
# changes
file(GLOB_RECURSE
RES_FILES CONFIGURE_DEPENDS
${PROJECT_SOURCE_DIR}/resources/*)
execute_process(
COMMAND ${PYTHON_EXEC} ${SCRIPTS_DIR}/gen_resources_qrc.py
WORKING_DIRECTORY ${APP_SRC_DIR})
# library compatibility (boost, libnotify, etc.)
add_definitions(-DQT_NO_KEYWORDS)
......
#!/usr/bin/env python3
# Copyright (C) 2022 Savoir-faire Linux Inc.
#
# Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
# Author: Amin Bandali <amin.bandali@savoirfairelinux.com>
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
with open('qml_without_webengine.qrc', 'w') as outfile:
with open('qml.qrc', 'r') as infile:
line = infile.readline()
while line:
if 'EmojiPicker.qml' in line:
outfile.write('\t<file>nowebengine/EmojiPicker.qml</file>\n')
elif 'MediaPreviewBase.qml' in line:
outfile.write('\t<file>nowebengine/MediaPreviewBase.qml</file>\n')
else:
outfile.write(line)
line = infile.readline()
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2022 Savoir-faire Linux 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 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
# USA.
"""
Generate qrc file for qml and related code files recursively within the source
directory.
"""
import os
# These paths should be relative to the working directory of the
# script as set in the project CMakeLists, which should in turn be
# where the resources.qrc will be located (currently 'src/app').
app_src_dir = os.path.join('..', '..', 'src', 'app')
resfile = os.path.join('qml.qrc')
def path_contains_dir(filepath, dir_str):
""" Return True if the given filepath contains the given directory. """
# Split the filepath into its components
path_components = os.path.normpath(filepath).split(os.sep)
# Return True if the given directory is in the path
return dir_str in path_components
def posix_path(path):
"""
Force the use of POSIX path separators for the resource prefixes
and paths (useful only if versioning the qml.qrc file).
"""
return path.replace(os.sep, '/')
def gen_qml_qrc(with_webengine):
""" Generate the qml.qrc file. """
print("Generating qml.qrc file ...")
with open(resfile, 'w', encoding='utf-8') as qrc:
qrc.write('<RCC>\n')
for root, _, files in os.walk(app_src_dir):
# Skip the nowebengine directory if we can use webengine
if with_webengine and path_contains_dir(root, 'nowebengine'):
continue
# Skip the webengine directory if we can't use webengine
if not with_webengine and path_contains_dir(root, 'webengine'):
continue
filtered = [k for k in files if k.endswith('.qml') or
k.endswith('.js') or k.endswith('.html') or
k.endswith('.css') or k.endswith('.conf')]
# if there are no files of interest in this directory, skip it
if not filtered:
continue
# For now, get the relative resource prefix for this directory,
# remove the leading slash, and add it as a comment to the line.
# Ideally, we should use the actual resource prefix instead of /,
# but this will require some refactoring of the QML code.
prefix = root.split(app_src_dir)[-1][1:]
qrc.write(
f'\t<qresource prefix="/"> <!--{posix_path(prefix)}-->\n')
for file in filtered:
relpath = os.path.relpath(
os.path.join(root, file), app_src_dir)
qrc.write(f'\t\t<file>{posix_path(relpath)}</file>\n')
qrc.write('\t</qresource>\n')
qrc.write('</RCC>')
if __name__ == '__main__':
# We can't use webengine if we're building for macOS app store
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--with-webengine', action='store_true',
default=False, help='Include webengine resources')
args = parser.parse_args()
gen_qml_qrc(args.with_webengine)
......@@ -274,7 +274,9 @@ Loader {
id: avComp
Loader {
Component.onCompleted: {
var qml = WITH_WEBENGINE ? "qrc:/commoncomponents/MediaPreviewBase.qml" : "qrc:/nowebengine/MediaPreviewBase.qml"
var qml = WITH_WEBENGINE ?
"qrc:/webengine/MediaPreviewBase.qml" :
"qrc:/nowebengine/MediaPreviewBase.qml"
setSource( qml, { isVideo: mediaInfo.isVideo, html:mediaInfo.html } )
}
}
......
......@@ -104,14 +104,16 @@ Rectangle {
}
Loader {
id: empjiLoader
source: WITH_WEBENGINE ? "qrc:/commoncomponents/emojipicker/EmojiPicker.qml" : "qrc:/nowebengine/EmojiPicker.qml"
id: emojiPickerLoader
source: WITH_WEBENGINE ?
"qrc:/webengine/emojipicker/EmojiPicker.qml" :
"qrc:/nowebengine/EmojiPicker.qml"
function openEmojiPicker() {
item.openEmojiPicker()
}
Connections {
target: empjiLoader.item
target: emojiPickerLoader.item
function onEmojiIsPicked(content) {
messageBar.textAreaObj.insertText(content)
}
......@@ -168,20 +170,20 @@ Rectangle {
onEmojiButtonClicked: {
JamiQmlUtils.updateMessageBarButtonsPoints()
empjiLoader.parent = JamiQmlUtils.mainViewRectObj
emojiPickerLoader.parent = JamiQmlUtils.mainViewRectObj
empjiLoader.x = Qt.binding(function() {
emojiPickerLoader.x = Qt.binding(function() {
var buttonX = JamiQmlUtils.emojiPickerButtonInMainViewPoint.x +
JamiQmlUtils.emojiPickerButtonObj.width
return buttonX - empjiLoader.width
return buttonX - emojiPickerLoader.width
})
empjiLoader.y = Qt.binding(function() {
emojiPickerLoader.y = Qt.binding(function() {
var buttonY = JamiQmlUtils.audioRecordMessageButtonInMainViewPoint.y
return buttonY - empjiLoader.height - messageBar.marginSize
return buttonY - emojiPickerLoader.height - messageBar.marginSize
- JamiTheme.chatViewHairLineSize
})
empjiLoader.openEmojiPicker()
emojiPickerLoader.openEmojiPicker()
}
onSendFileButtonClicked: jamiFileDialog.open()
onSendMessageButtonClicked: {
......
<RCC>
<qresource prefix="/">
<file>MainApplicationWindow.qml</file>
<file>DaemonReconnectWindow.qml</file>
<file>constant/JamiQmlUtils.qml</file>
<file>constant/JamiStrings.qml</file>
<file>constant/JamiTheme.qml</file>
<file>commoncomponents/VideoView.qml</file>
<file>commoncomponents/LocalVideo.qml</file>
<file>commoncomponents/SettingParaCombobox.qml</file>
<file>commoncomponents/PreferenceItemDelegate.qml</file>
<file>commoncomponents/PasswordDialog.qml</file>
<file>commoncomponents/EditableLineEdit.qml</file>
<file>commoncomponents/MaterialLineEdit.qml</file>
<file>commoncomponents/PhotoboothView.qml</file>
<file>commoncomponents/JamiListView.qml</file>
<file>commoncomponents/DeleteAccountDialog.qml</file>
<file>commoncomponents/ConfirmDialog.qml</file>
<file>commoncomponents/CustomBorder.qml</file>
<file>commoncomponents/PushButton.qml</file>
<file>commoncomponents/JamiFileDialog.qml</file>
<file>commoncomponents/MaterialButton.qml</file>
<file>commoncomponents/ElidedTextLabel.qml</file>
<file>commoncomponents/SpinnerButton.qml</file>
<file>commoncomponents/UsernameLineEdit.qml</file>
<file>commoncomponents/Scaffold.qml</file>
<file>commoncomponents/LineEditContextMenu.qml</file>
<file>commoncomponents/BaseModalDialog.qml</file>
<file>commoncomponents/SimpleMessageDialog.qml</file>
<file>commoncomponents/ResponsiveImage.qml</file>
<file>commoncomponents/PresenceIndicator.qml</file>
<file>commoncomponents/DaemonReconnectPopup.qml</file>
<file>commoncomponents/SpinningAnimation.qml</file>
<file>commoncomponents/MediaPreviewBase.qml</file>
<file>settingsview/SettingsView.qml</file>
<file>settingsview/components/ChatviewSettings.qml</file>
<file>settingsview/components/FileTransferSettings.qml</file>
<file>settingsview/components/SettingsMenu.qml</file>
<file>settingsview/components/SettingsMenuButton.qml</file>
<file>settingsview/components/SettingsHeader.qml</file>
<file>settingsview/components/SystemSettings.qml</file>
<file>settingsview/components/RecordingSettings.qml</file>
<file>settingsview/components/UpdateSettings.qml</file>
<file>settingsview/components/AvSettingPage.qml</file>
<file>settingsview/components/AudioSettings.qml</file>
<file>settingsview/components/VideoSettings.qml</file>
<file>settingsview/components/GeneralSettingsPage.qml</file>
<file>settingsview/components/PluginSettingsPage.qml</file>
<file>settingsview/components/PluginListView.qml</file>
<file>settingsview/components/PluginPreferencesView.qml</file>
<file>settingsview/components/PluginPreferencesListView.qml</file>
<file>settingsview/components/CurrentAccountSettings.qml</file>
<file>settingsview/components/UserIdentity.qml</file>
<file>settingsview/components/JamiUserIdentity.qml</file>
<file>settingsview/components/SIPUserIdentity.qml</file>
<file>settingsview/components/AccountProfile.qml</file>
<file>settingsview/components/LinkedDevices.qml</file>
<file>settingsview/components/BannedContacts.qml</file>
<file>settingsview/components/AdvancedSettings.qml</file>
<file>settingsview/components/AdvancedJamiSecuritySettings.qml</file>
<file>settingsview/components/AdvancedSIPSecuritySettings.qml</file>
<file>settingsview/components/AdvancedMediaSettings.qml</file>
<file>settingsview/components/MediaSettings.qml</file>
<file>settingsview/components/AdvancedSDPSettings.qml</file>
<file>settingsview/components/AdvancedNameServerSettings.qml</file>
<file>settingsview/components/AdvancedVoiceMailSettings.qml</file>
<file>settingsview/components/AdvancedOpenDHTSettings.qml</file>
<file>settingsview/components/AdvancedPublicAddressSettings.qml</file>
<file>settingsview/components/AdvancedConnectivitySettings.qml</file>
<file>settingsview/components/AdvancedCallSettings.qml</file>
<file>settingsview/components/AdvancedChatSettings.qml</file>
<file>settingsview/components/SettingMaterialButton.qml</file>
<file>settingsview/components/ToggleSwitch.qml</file>
<file>settingsview/components/SettingSpinBox.qml</file>
<file>settingsview/components/SettingsComboBox.qml</file>
<file>settingsview/components/SettingsMaterialLineEdit.qml</file>
<file>settingsview/components/LevelMeter.qml</file>
<file>settingsview/components/DeviceItemDelegate.qml</file>
<file>settingsview/components/PluginItemDelegate.qml</file>
<file>settingsview/components/ContactItemDelegate.qml</file>
<file>settingsview/components/MediaCodecDelegate.qml</file>
<file>settingsview/components/NameRegistrationDialog.qml</file>
<file>settingsview/components/LinkDeviceDialog.qml</file>
<file>settingsview/components/RevokeDevicePasswordDialog.qml</file>
<file>wizardview/WizardView.qml</file>
<file>wizardview/components/WelcomePage.qml</file>
<file>wizardview/components/CreateAccountPage.qml</file>
<file>wizardview/components/CreateSIPAccountPage.qml</file>
<file>wizardview/components/ImportFromBackupPage.qml</file>
<file>wizardview/components/ImportFromDevicePage.qml</file>
<file>wizardview/components/ConnectToAccountManagerPage.qml</file>
<file>wizardview/components/ProfilePage.qml</file>
<file>wizardview/components/AccountCreationStepIndicator.qml</file>
<file>mainview/MainView.qml</file>
<file>mainview/components/PluginHandlerItemDelegate.qml</file>
<file>mainview/components/AboutPopUp.qml</file>
<file>mainview/components/SidePanel.qml</file>
<file>mainview/components/WelcomePage.qml</file>
<file>mainview/components/ChatView.qml</file>
<file>mainview/components/ConversationErrorsRow.qml</file>
<file>mainview/components/NewSwarmPage.qml</file>
<file>mainview/components/ChatViewHeader.qml</file>
<file>mainview/components/AccountComboBox.qml</file>
<file>mainview/components/CallStackView.qml</file>
<file>mainview/components/InitialCallPage.qml</file>
<file>mainview/components/CallOverlay.qml</file>
<file>mainview/components/ContactSearchBar.qml</file>
<file>mainview/components/OngoingCallPage.qml</file>
<file>mainview/components/ParticipantOverlay.qml</file>
<file>mainview/components/ProjectCreditsScrollView.qml</file>
<file>mainview/components/AccountComboBoxPopup.qml</file>
<file>mainview/components/SidePanelTabBar.qml</file>
<file>mainview/components/WelcomePageQrDialog.qml</file>
<file>mainview/components/ConversationSmartListContextMenu.qml</file>
<file>mainview/components/SwarmParticipantContextMenu.qml</file>
<file>mainview/components/CallViewContextMenu.qml</file>
<file>mainview/components/UserProfile.qml</file>
<file>mainview/components/SwarmDetailsPanel.qml</file>
<file>mainview/components/SwarmDetailsItem.qml</file>
<file>mainview/components/AddMemberPanel.qml</file>
<file>mainview/components/SelectScreen.qml</file>
<file>mainview/components/ScreenRubberBand.qml</file>
<file>mainview/components/ContactPicker.qml</file>
<file>mainview/components/PluginHandlerPicker.qml</file>
<file>mainview/components/ContactPickerItemDelegate.qml</file>
<file>mainview/components/RecordBox.qml</file>
<file>mainview/components/SipInputPanel.qml</file>
<file>mainview/components/ParticipantOverlayMenu.qml</file>
<file>mainview/js/selectscreenwindowcreation.js</file>
<file>mainview/js/screenrubberbandcreation.js</file>
<file>mainview/js/contactpickercreation.js</file>
<file>mainview/js/pluginhandlerpickercreation.js</file>
<file>mainview/components/FilterTabButton.qml</file>
<file>mainview/components/AccountItemDelegate.qml</file>
<file>mainview/components/ConversationListView.qml</file>
<file>mainview/components/SmartListItemDelegate.qml</file>
<file>mainview/components/BadgeNotifier.qml</file>
<file>mainview/components/ParticipantsLayer.qml</file>
<file>mainview/components/ParticipantsLayoutVertical.qml</file>
<file>mainview/components/ParticipantsLayoutHorizontal.qml</file>
<file>mainview/components/MainOverlay.qml</file>
<file>mainview/components/CallButtonDelegate.qml</file>
<file>mainview/components/CallActionBar.qml</file>
<file>commoncomponents/HalfPill.qml</file>
<file>commoncomponents/EditedPopup.qml</file>
<file>commoncomponents/MaterialToolTip.qml</file>
<file>mainview/components/ParticipantCallInStatusDelegate.qml</file>
<file>mainview/components/ParticipantCallInStatusView.qml</file>
<file>settingsview/components/TroubleshootSettings.qml</file>
<file>settingsview/components/LogsView.qml</file>
<file>commoncomponents/contextmenu/ContextMenuAutoLoader.qml</file>
<file>commoncomponents/contextmenu/BaseContextMenu.qml</file>
<file>commoncomponents/contextmenu/GeneralMenuItem.qml</file>
<file>commoncomponents/contextmenu/GeneralMenuSeparator.qml</file>
<file>mainview/components/ParticipantOverlayButton.qml</file>
<file>mainview/components/ParticipantControlLayout.qml</file>
<file>mainview/components/ChatViewFooter.qml</file>
<file>commoncomponents/emojipicker/EmojiPicker.qml</file>
<file>commoncomponents/emojipicker/emojiPickerLoader.js</file>
<file>commoncomponents/emojipicker/emojiPickerLoader.html</file>
<file>commoncomponents/emojipicker/emoji.js</file>
<file>mainview/components/MessageBarTextArea.qml</file>
<file>mainview/components/FilesToSendDelegate.qml</file>
<file>mainview/components/MessageBar.qml</file>
<file>mainview/components/FilesToSendContainer.qml</file>
<file>mainview/components/ReplyingContainer.qml</file>
<file>mainview/components/EditContainer.qml</file>
<file>commoncomponents/Avatar.qml</file>
<file>mainview/components/ConversationAvatar.qml</file>
<file>mainview/components/InvitationView.qml</file>
<file>commoncomponents/GeneralWebEngineView.qml</file>
<file>constant/JamiResources.qml</file>
<file>commoncomponents/BubbleLabel.qml</file>
<file>commoncomponents/BackButton.qml</file>
<file>commoncomponents/JamiSwitch.qml</file>
<file>mainview/components/UpdateToSwarm.qml</file>
<file>commoncomponents/TextMessageDelegate.qml</file>
<file>mainview/components/MessageListView.qml</file>
<file>commoncomponents/MessageBubble.qml</file>
<file>constant/MsgSeq.qml</file>
<file>commoncomponents/SBSContextMenu.qml</file>
<file>commoncomponents/SBSMessageBase.qml</file>
<file>commoncomponents/ReplyToRow.qml</file>
<file>commoncomponents/ReadStatus.qml</file>
<file>commoncomponents/GeneratedMessageDelegate.qml</file>
<file>commoncomponents/DataTransferMessageDelegate.qml</file>
<file>commoncomponents/ContactMessageDelegate.qml</file>
<file>mainview/components/ScrollToBottomButton.qml</file>
<file>commoncomponents/TypingDots.qml</file>
<file>commoncomponents/JamiScrollBar.qml</file>
<file>qtquickcontrols2.conf</file>
<file>commoncomponents/JamiFlickable.qml</file>
<file>AccountMigrationView.qml</file>
<file>settingsview/js/logviewwindowcreation.js</file>
<file>mainview/js/keyboardshortcuttablecreation.js</file>
<file>mainview/components/KeyboardShortcutTable.qml</file>
<file>mainview/components/KeyboardShortcutKeyDelegate.qml</file>
<file>mainview/components/KeyboardShortcutTabButton.qml</file>
<file>LayoutManager.qml</file>
<file>mainview/components/JamiIdentifier.qml</file>
<file>wizardview/components/NoUsernamePopup.qml</file>
<file>wizardview/components/AdvancedAccountSettings.qml</file>
<file>commoncomponents/InfoBox.qml</file>
<file>mainview/components/TipBox.qml</file>
<file>mainview/components/CustomizeTipBox.qml</file>
<file>mainview/components/BackupTipBox.qml</file>
<file>mainview/components/InformativeTipBox.qml</file>
<file>commoncomponents/TimestampInfo.qml</file>
<file>commoncomponents/MaterialTextField.qml</file>
<file>commoncomponents/ModalTextEdit.qml</file>
<file>commoncomponents/UsernameTextEdit.qml</file>
<file>mainview/components/DocumentsScrollview.qml</file>
<file>mainview/components/FilePreview.qml</file>
<file>mainview/components/MediaPreview.qml</file>
<file>mainview/components/CallInformationWindow.qml</file>
</qresource>
</RCC>
......@@ -26,6 +26,8 @@ import net.jami.Models 1.1
import net.jami.Constants 1.1
import net.jami.Adapters 1.1
import "../commoncomponents"
WebEngineView {
id: wev
property bool isVideo
......
......@@ -74,7 +74,7 @@ Rectangle {
webChannel.registeredObjects: [jsBridgeObject]
onCompletedLoadHtml: ":/commoncomponents/emojipicker/emojiPickerLoader.html"
onCompletedLoadHtml: ":/webengine/emojipicker/emojiPickerLoader.html"
onActiveFocusChanged: {
if (visible) {
......@@ -88,10 +88,10 @@ Rectangle {
":qwebchannel.js"))
emojiPickerWebView.runJavaScript(
UtilsAdapter.qStringFromFile(
":/commoncomponents/emojipicker/emoji.js"))
":/webengine/emojipicker/emoji.js"))
emojiPickerWebView.runJavaScript(
UtilsAdapter.qStringFromFile(
":/commoncomponents/emojipicker/emojiPickerLoader.js"))
":/webengine/emojipicker/emojiPickerLoader.js"))
emojiPickerWebView.runJavaScript(
"init_emoji_picker(" + JamiTheme.darkTheme + ");")
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment