diff options
Diffstat (limited to 'Swift/QtUI')
| -rw-r--r-- | Swift/QtUI/QtHighlightEditor.cpp | 524 | ||||
| -rw-r--r-- | Swift/QtUI/QtHighlightEditor.h | 69 | ||||
| -rw-r--r-- | Swift/QtUI/QtHighlightEditor.ui | 446 | ||||
| -rw-r--r-- | Swift/QtUI/QtHighlightRuleWidget.cpp | 134 | ||||
| -rw-r--r-- | Swift/QtUI/QtHighlightRuleWidget.h | 49 | ||||
| -rw-r--r-- | Swift/QtUI/QtHighlightRuleWidget.ui | 260 | ||||
| -rw-r--r-- | Swift/QtUI/QtUIFactory.cpp | 6 | ||||
| -rw-r--r-- | Swift/QtUI/QtUIFactory.h | 2 | ||||
| -rw-r--r-- | Swift/QtUI/QtWebKitChatView.cpp | 33 | ||||
| -rw-r--r-- | Swift/QtUI/QtWebKitChatView.h | 5 | ||||
| -rw-r--r-- | Swift/QtUI/SConscript | 7 |
11 files changed, 1066 insertions, 469 deletions
diff --git a/Swift/QtUI/QtHighlightEditor.cpp b/Swift/QtUI/QtHighlightEditor.cpp new file mode 100644 index 0000000..3900cf9 --- /dev/null +++ b/Swift/QtUI/QtHighlightEditor.cpp @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cassert> + +#include <boost/lexical_cast.hpp> + +#include <Swift/QtUI/UserSearch/QtSuggestingJIDInput.h> +#include <Swift/Controllers/HighlightManager.cpp> +#include <Swift/QtUI/QtHighlightEditor.h> +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtSettingsProvider.h> + +#include <QTreeWidgetItem> +#include <QFileDialog> + +namespace Swift { + +QtHighlightEditor::QtHighlightEditor(QtSettingsProvider* settings, QWidget* parent) + : QWidget(parent), settings_(settings), previousRow_(-1) +{ + ui_.setupUi(this); + + connect(ui_.listWidget, SIGNAL(currentRowChanged(int)), SLOT(onCurrentRowChanged(int))); + + connect(ui_.newButton, SIGNAL(clicked()), SLOT(onNewButtonClicked())); + connect(ui_.deleteButton, SIGNAL(clicked()), SLOT(onDeleteButtonClicked())); + + connect(ui_.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(onApplyButtonClick())); + connect(ui_.buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), SLOT(onCancelButtonClick())); + connect(ui_.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(onOkButtonClick())); + + connect(ui_.noColorRadio, SIGNAL(clicked()), SLOT(colorOtherSelect())); + connect(ui_.defaultColorRadio, SIGNAL(clicked()), SLOT(colorOtherSelect())); + connect(ui_.customColorRadio, SIGNAL(clicked()), SLOT(colorCustomSelect())); + + connect(ui_.noSoundRadio, SIGNAL(clicked()), SLOT(soundOtherSelect())); + connect(ui_.defaultSoundRadio, SIGNAL(clicked()), SLOT(soundOtherSelect())); + connect(ui_.customSoundRadio, SIGNAL(clicked()), SLOT(soundCustomSelect())); + + /* replace the static line-edit control with the roster autocompleter */ + ui_.dummySenderName->setVisible(false); + jid_ = new QtSuggestingJIDInput(this, settings); + ui_.senderName->addWidget(jid_); + jid_->onUserSelected.connect(boost::bind(&QtHighlightEditor::handleOnUserSelected, this, _1)); + + /* handle autocomplete */ + connect(jid_, SIGNAL(textEdited(QString)), SLOT(handleContactSuggestionRequested(QString))); + + /* we need to be notified if any of the state changes so that we can update our textual rule description */ + connect(ui_.chatRadio, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.roomRadio, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.nickIsKeyword, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.allMsgRadio, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.senderRadio, SIGNAL(clicked()), SLOT(widgetClick())); + connect(jid_, SIGNAL(textChanged(const QString&)), SLOT(widgetClick())); + connect(ui_.keywordRadio, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.keyword, SIGNAL(textChanged(const QString&)), SLOT(widgetClick())); + connect(ui_.matchPartialWords, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.matchCase, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.noColorRadio, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.defaultColorRadio, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.customColorRadio, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.noSoundRadio, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.defaultSoundRadio, SIGNAL(clicked()), SLOT(widgetClick())); + connect(ui_.customSoundRadio, SIGNAL(clicked()), SLOT(widgetClick())); + + /* allow selection of a custom sound file */ + connect(ui_.soundFileButton, SIGNAL(clicked()), SLOT(selectSoundFile())); + + /* if these are not needed, then they should be removed */ + ui_.moveUpButton->setVisible(false); + ui_.moveDownButton->setVisible(false); + + setWindowTitle(tr("Highlight Rules")); +} + +QtHighlightEditor::~QtHighlightEditor() +{ +} + +std::string formatShortDescription(const HighlightRule &rule) +{ + const std::string chatOrRoom = (rule.getMatchChat() ? "chat" : "room"); + + std::vector<std::string> senders = rule.getSenders(); + std::vector<std::string> keywords = rule.getKeywords(); + + if (senders.empty() && keywords.empty() && !rule.getNickIsKeyword()) { + return std::string("All ") + chatOrRoom + " messages."; + } + + if (rule.getNickIsKeyword()) { + return std::string("All ") + chatOrRoom + " messages that mention my name."; + } + + if (!senders.empty()) { + return std::string("All ") + chatOrRoom + " messages from " + senders[0] + "."; + } + + if (!keywords.empty()) { + return std::string("All ") + chatOrRoom + " messages mentioning the keyword '" + keywords[0] + "'."; + } + + return "Unknown Rule"; +} + +void QtHighlightEditor::show() +{ + highlightManager_->loadSettings(); + + populateList(); + + if (ui_.listWidget->count()) { + selectRow(0); + } + + /* prepare default states */ + widgetClick(); + + QWidget::show(); + QWidget::activateWindow(); +} + +void QtHighlightEditor::setHighlightManager(HighlightManager* highlightManager) +{ + highlightManager_ = highlightManager; +} + +void QtHighlightEditor::setContactSuggestions(const std::vector<Contact::ref>& suggestions) +{ + jid_->setSuggestions(suggestions); +} + +void QtHighlightEditor::colorOtherSelect() +{ + ui_.foregroundColor->setEnabled(false); + ui_.backgroundColor->setEnabled(false); +} + +void QtHighlightEditor::colorCustomSelect() +{ + ui_.foregroundColor->setEnabled(true); + ui_.backgroundColor->setEnabled(true); +} + +void QtHighlightEditor::soundOtherSelect() +{ + ui_.soundFile->setEnabled(false); + ui_.soundFileButton->setEnabled(false); +} + +void QtHighlightEditor::soundCustomSelect() +{ + ui_.soundFile->setEnabled(true); + ui_.soundFileButton->setEnabled(true); +} + +void QtHighlightEditor::onNewButtonClicked() +{ + int row = getSelectedRow() + 1; + populateList(); + HighlightRule newRule; + newRule.setMatchChat(true); + highlightManager_->insertRule(row, newRule); + QListWidgetItem *item = new QListWidgetItem(); + item->setText(P2QSTRING(formatShortDescription(newRule))); + ui_.listWidget->insertItem(row, item); + selectRow(row); +} + +void QtHighlightEditor::onDeleteButtonClicked() +{ + int selectedRow = getSelectedRow(); + assert(selectedRow>=0 && selectedRow<ui_.listWidget->count()); + delete ui_.listWidget->takeItem(selectedRow); + highlightManager_->removeRule(selectedRow); + + if (!ui_.listWidget->count()) { + disableDialog(); + ui_.deleteButton->setEnabled(false); + } else { + if (selectedRow == ui_.listWidget->count()) { + selectRow(ui_.listWidget->count() - 1); + } else { + selectRow(selectedRow); + } + } +} + +void QtHighlightEditor::onCurrentRowChanged(int currentRow) +{ + ui_.deleteButton->setEnabled(currentRow != -1); + ui_.moveUpButton->setEnabled(currentRow != -1 && currentRow != 0); + ui_.moveDownButton->setEnabled(currentRow != -1 && currentRow != (ui_.listWidget->count()-1)); + + if (previousRow_ != -1) { + if (ui_.listWidget->count() > previousRow_) { + highlightManager_->setRule(previousRow_, ruleFromDialog()); + } + } + + if (currentRow != -1) { + HighlightRule rule = highlightManager_->getRule(currentRow); + ruleToDialog(rule); + if (ui_.listWidget->currentItem()) { + ui_.listWidget->currentItem()->setText(P2QSTRING(formatShortDescription(rule))); + } + } + + /* grey the dialog if we have nothing selected */ + if (currentRow == -1) { + disableDialog(); + } + + previousRow_ = currentRow; +} + +void QtHighlightEditor::onApplyButtonClick() +{ + selectRow(getSelectedRow()); /* force save */ + highlightManager_->storeSettings(); +} + +void QtHighlightEditor::onCancelButtonClick() +{ + close(); +} + +void QtHighlightEditor::onOkButtonClick() +{ + onApplyButtonClick(); + close(); +} + +void QtHighlightEditor::setChildWidgetStates() +{ + /* disable appropriate radio button child widgets */ + + if (ui_.chatRadio->isChecked()) { + if (ui_.nickIsKeyword->isChecked()) { + /* switch to another choice before we disable this button */ + ui_.allMsgRadio->setChecked(true); + } + ui_.nickIsKeyword->setEnabled(false); + } else if (ui_.roomRadio->isChecked()) { + ui_.nickIsKeyword->setEnabled(true); + } else { /* chats and rooms */ + ui_.nickIsKeyword->setEnabled(true); + } + + if (ui_.senderRadio->isChecked()) { + jid_->setEnabled(true); + } else { + jid_->setEnabled(false); + } + + if (ui_.keywordRadio->isChecked()) { + ui_.keyword->setEnabled(true); + ui_.matchPartialWords->setEnabled(true); + ui_.matchCase->setEnabled(true); + } else { + ui_.keyword->setEnabled(false); + ui_.matchPartialWords->setEnabled(false); + ui_.matchCase->setEnabled(false); + } + + if (ui_.chatRadio->isChecked()) { + ui_.allMsgRadio->setText(tr("Apply to all chat messages")); + } else { + ui_.allMsgRadio->setText(tr("Apply to all room messages")); + } +} + +void QtHighlightEditor::widgetClick() +{ + setChildWidgetStates(); + + HighlightRule rule = ruleFromDialog(); + + if (ui_.listWidget->currentItem()) { + ui_.listWidget->currentItem()->setText(P2QSTRING(formatShortDescription(rule))); + } +} + +void QtHighlightEditor::disableDialog() +{ + ui_.chatRadio->setEnabled(false); + ui_.roomRadio->setEnabled(false); + ui_.allMsgRadio->setEnabled(false); + ui_.nickIsKeyword->setEnabled(false); + ui_.senderRadio->setEnabled(false); + ui_.dummySenderName->setEnabled(false); + ui_.keywordRadio->setEnabled(false); + ui_.keyword->setEnabled(false); + ui_.matchPartialWords->setEnabled(false); + ui_.matchCase->setEnabled(false); + ui_.noColorRadio->setEnabled(false); + ui_.defaultColorRadio->setEnabled(false); + ui_.customColorRadio->setEnabled(false); + ui_.foregroundColor->setEnabled(false); + ui_.backgroundColor->setEnabled(false); + ui_.noSoundRadio->setEnabled(false); + ui_.defaultSoundRadio->setEnabled(false); + ui_.customSoundRadio->setEnabled(false); + ui_.soundFile->setEnabled(false); + ui_.soundFileButton->setEnabled(false); +} + +void QtHighlightEditor::handleContactSuggestionRequested(const QString& text) +{ + std::string stdText = Q2PSTRING(text); + onContactSuggestionsRequested(stdText); +} + +void QtHighlightEditor::selectSoundFile() +{ + QString path = QFileDialog::getOpenFileName(this, tr("Select sound file..."), QString(), "Sounds (*.wav)"); + ui_.soundFile->setText(path); +} + +void QtHighlightEditor::handleOnUserSelected(const JID& jid) { + /* this might seem like it should be standard behaviour for the suggesting input box, but is not desirable in all cases */ + jid_->setText(P2QSTRING(jid.toString())); +} + +void QtHighlightEditor::populateList() +{ + previousRow_ = -1; + ui_.listWidget->clear(); + HighlightRulesListPtr rules = highlightManager_->getRules(); + for (size_t i = 0; i < rules->getSize(); ++i) { + const HighlightRule& rule = rules->getRule(i); + QListWidgetItem *item = new QListWidgetItem(); + item->setText(P2QSTRING(formatShortDescription(rule))); + ui_.listWidget->addItem(item); + } +} + +void QtHighlightEditor::selectRow(int row) +{ + for (int i = 0; i < ui_.listWidget->count(); ++i) { + if (i == row) { + ui_.listWidget->item(i)->setSelected(true); + onCurrentRowChanged(i); + } else { + ui_.listWidget->item(i)->setSelected(false); + } + } + ui_.listWidget->setCurrentRow(row); +} + +int QtHighlightEditor::getSelectedRow() const +{ + for (int i = 0; i < ui_.listWidget->count(); ++i) { + if (ui_.listWidget->item(i)->isSelected()) { + return i; + } + } + return -1; +} + +HighlightRule QtHighlightEditor::ruleFromDialog() +{ + HighlightRule rule; + + if (ui_.chatRadio->isChecked()) { + rule.setMatchChat(true); + rule.setMatchMUC(false); + } else { + rule.setMatchChat(false); + rule.setMatchMUC(true); + } + + if (ui_.senderRadio->isChecked()) { + QString senderName = jid_->text(); + if (!senderName.isEmpty()) { + std::vector<std::string> senders; + senders.push_back(Q2PSTRING(senderName)); + rule.setSenders(senders); + } + } + + if (ui_.keywordRadio->isChecked()) { + QString keywordString = ui_.keyword->text(); + if (!keywordString.isEmpty()) { + std::vector<std::string> keywords; + keywords.push_back(Q2PSTRING(keywordString)); + rule.setKeywords(keywords); + } + } + + rule.setNickIsKeyword(ui_.nickIsKeyword->isChecked()); + rule.setMatchWholeWords(!ui_.matchPartialWords->isChecked()); + rule.setMatchCase(ui_.matchCase->isChecked()); + + HighlightAction& action = rule.getAction(); + + if (ui_.noColorRadio->isChecked()) { + action.setHighlightText(false); + action.setTextColor(""); + action.setTextBackground(""); + } else if (ui_.defaultColorRadio->isChecked()) { + action.setHighlightText(true); + action.setTextColor(""); + action.setTextBackground(""); + } else { + action.setHighlightText(true); + action.setTextColor(Q2PSTRING(ui_.foregroundColor->getColor().name())); + action.setTextBackground(Q2PSTRING(ui_.backgroundColor->getColor().name())); + } + + if (ui_.noSoundRadio->isChecked()) { + action.setPlaySound(false); + } else if (ui_.defaultSoundRadio->isChecked()) { + action.setPlaySound(true); + action.setSoundFile(""); + } else { + action.setPlaySound(true); + action.setSoundFile(Q2PSTRING(ui_.soundFile->text())); + } + + return rule; +} + +void QtHighlightEditor::ruleToDialog(const HighlightRule& rule) +{ + ui_.chatRadio->setEnabled(true); + ui_.roomRadio->setEnabled(true); + + if (rule.getMatchMUC()) { + ui_.chatRadio->setChecked(false); + ui_.roomRadio->setChecked(true); + } else { + ui_.chatRadio->setChecked(true); + ui_.roomRadio->setChecked(false); + } + + ui_.allMsgRadio->setEnabled(true); + ui_.allMsgRadio->setChecked(true); /* this is the default radio button */ + jid_->setText(""); + ui_.keyword->setText(""); + ui_.matchPartialWords->setChecked(false); + ui_.matchCase->setChecked(false); + + ui_.nickIsKeyword->setEnabled(true); + if (rule.getNickIsKeyword()) { + ui_.nickIsKeyword->setChecked(true); + } + + ui_.senderRadio->setEnabled(true); + std::vector<std::string> senders = rule.getSenders(); + if (!senders.empty()) { + ui_.senderRadio->setChecked(true); + jid_->setText(P2QSTRING(senders[0])); + } + + ui_.keywordRadio->setEnabled(true); + std::vector<std::string> keywords = rule.getKeywords(); + if (!keywords.empty()) { + ui_.keywordRadio->setChecked(true); + ui_.keyword->setText(P2QSTRING(keywords[0])); + ui_.matchPartialWords->setChecked(!rule.getMatchWholeWords()); + ui_.matchCase->setChecked(rule.getMatchCase()); + } + + const HighlightAction& action = rule.getAction(); + + ui_.noColorRadio->setEnabled(true); + ui_.defaultColorRadio->setEnabled(true); + ui_.customColorRadio->setEnabled(true); + if (action.highlightText()) { + if (action.getTextColor().empty() && action.getTextBackground().empty()) { + ui_.defaultColorRadio->setChecked(true); + ui_.foregroundColor->setEnabled(false); + ui_.backgroundColor->setEnabled(false); + } else { + ui_.foregroundColor->setEnabled(true); + ui_.backgroundColor->setEnabled(true); + QColor foregroundColor(P2QSTRING(action.getTextColor())); + ui_.foregroundColor->setColor(foregroundColor); + QColor backgroundColor(P2QSTRING(action.getTextBackground())); + ui_.backgroundColor->setColor(backgroundColor); + ui_.customColorRadio->setChecked(true); + } + } else { + ui_.noColorRadio->setChecked(true); + ui_.foregroundColor->setEnabled(false); + ui_.backgroundColor->setEnabled(false); + } + + ui_.noSoundRadio->setEnabled(true); + ui_.defaultSoundRadio->setEnabled(true); + ui_.customSoundRadio->setEnabled(true); + ui_.soundFile->setText(""); + ui_.soundFile->setEnabled(false); + ui_.soundFileButton->setEnabled(false); + if (action.playSound()) { + if (action.getSoundFile().empty()) { + ui_.defaultSoundRadio->setChecked(true); + } else { + ui_.customSoundRadio->setChecked(true); + ui_.soundFile->setText(P2QSTRING(action.getSoundFile())); + ui_.soundFile->setEnabled(true); + ui_.soundFileButton->setEnabled(true); + } + } else { + ui_.noSoundRadio->setChecked(true); + } + + /* set radio button child option states */ + setChildWidgetStates(); +} + +} diff --git a/Swift/QtUI/QtHighlightEditor.h b/Swift/QtUI/QtHighlightEditor.h new file mode 100644 index 0000000..c7db464 --- /dev/null +++ b/Swift/QtUI/QtHighlightEditor.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +/* + * Copyright (c) 2014 Kevin Smith and Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/HighlightRule.h> +#include <Swift/Controllers/UIInterfaces/HighlightEditorWindow.h> +#include <Swift/QtUI/ui_QtHighlightEditor.h> + +namespace Swift { + + class QtSettingsProvider; + class QtSuggestingJIDInput; + class QtWebKitChatView; + + class QtHighlightEditor : public QWidget, public HighlightEditorWindow { + Q_OBJECT + + public: + QtHighlightEditor(QtSettingsProvider* settings, QWidget* parent = NULL); + virtual ~QtHighlightEditor(); + + virtual void show(); + virtual void setHighlightManager(HighlightManager* highlightManager); + virtual void setContactSuggestions(const std::vector<Contact::ref>& suggestions); + + private slots: + void colorOtherSelect(); + void colorCustomSelect(); + void soundOtherSelect(); + void soundCustomSelect(); + void onNewButtonClicked(); + void onDeleteButtonClicked(); + void onCurrentRowChanged(int currentRow); + void onApplyButtonClick(); + void onCancelButtonClick(); + void onOkButtonClick(); + void setChildWidgetStates(); + void widgetClick(); + void disableDialog(); + void handleContactSuggestionRequested(const QString& text); + void selectSoundFile(); + + private: + void handleOnUserSelected(const JID& jid); + void populateList(); + void updateChatPreview(); + void selectRow(int row); + int getSelectedRow() const; + HighlightRule ruleFromDialog(); + void ruleToDialog(const HighlightRule& rule); + + Ui::QtHighlightEditor ui_; + QtSettingsProvider* settings_; + HighlightManager* highlightManager_; + QtSuggestingJIDInput* jid_; + int previousRow_; + }; + +} diff --git a/Swift/QtUI/QtHighlightEditor.ui b/Swift/QtUI/QtHighlightEditor.ui new file mode 100644 index 0000000..09a7297 --- /dev/null +++ b/Swift/QtUI/QtHighlightEditor.ui @@ -0,0 +1,446 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtHighlightEditor</class> + <widget class="QWidget" name="QtHighlightEditor"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>439</width> + <height>836</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>439</width> + <height>836</height> + </size> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Incoming messages are checked against the following rules. First rule that matches will be executed.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QListWidget" name="listWidget"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <spacer name="horizontalSpacer_8"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="newButton"> + <property name="text"> + <string>New Rule</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="deleteButton"> + <property name="text"> + <string>Remove Rule</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="moveUpButton"> + <property name="text"> + <string>Move Up</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="moveDownButton"> + <property name="text"> + <string>Move Down</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="Line" name="line_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Apply Rule To</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QRadioButton" name="chatRadio"> + <property name="text"> + <string>Chats</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="roomRadio"> + <property name="text"> + <string>Rooms</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>246</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Rule Conditions</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QRadioButton" name="allMsgRadio"> + <property name="text"> + <string>Apply to all messages</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="nickIsKeyword"> + <property name="text"> + <string>Only messages mentioning me</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="senderRadio"> + <property name="text"> + <string>Messages from this sender:</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="senderName"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</enum> + </property> + <item> + <widget class="QLineEdit" name="dummySenderName"/> + </item> + </layout> + </item> + <item> + <widget class="QRadioButton" name="keywordRadio"> + <property name="text"> + <string>Messages containing this keyword:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="keyword"/> + </item> + <item> + <widget class="QCheckBox" name="matchPartialWords"> + <property name="text"> + <string>Match keyword within longer words</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="matchCase"> + <property name="text"> + <string>Keyword is case sensitive</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Highlight Action</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QRadioButton" name="noColorRadio"> + <property name="text"> + <string>No Highlight</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="defaultColorRadio"> + <property name="text"> + <string>Default Color</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="customColorRadio"> + <property name="text"> + <string>Custom Color</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_6"> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="Swift::QtColorToolButton" name="foregroundColor"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Text</string> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + <item> + <widget class="Swift::QtColorToolButton" name="backgroundColor"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Background</string> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Sound Action</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <item> + <widget class="QRadioButton" name="noSoundRadio"> + <property name="text"> + <string>No Sound</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="defaultSoundRadio"> + <property name="text"> + <string>Default Sound</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="customSoundRadio"> + <property name="text"> + <string>Custom Sound</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_6"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLineEdit" name="soundFile"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="readOnly"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="soundFileButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="Line" name="line_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_9"> + <item> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Swift::QtColorToolButton</class> + <extends>QToolButton</extends> + <header>QtColorToolButton.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtHighlightRuleWidget.cpp b/Swift/QtUI/QtHighlightRuleWidget.cpp deleted file mode 100644 index 9c0df5e..0000000 --- a/Swift/QtUI/QtHighlightRuleWidget.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2012 Maciej Niedzielski - * Licensed under the simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#include <QDataWidgetMapper> -#include <QStringListModel> -#include <QFileDialog> - -#include <Swift/QtUI/QtHighlightRuleWidget.h> -#include <Swift/QtUI/QtHighlightRulesItemModel.h> - -namespace Swift { - -QtHighlightRuleWidget::QtHighlightRuleWidget(QWidget* parent) - : QWidget(parent) -{ - ui_.setupUi(this); - - QStringList applyToItems; - for (int i = 0; i < QtHighlightRulesItemModel::ApplyToEOL; ++i) { - applyToItems << QtHighlightRulesItemModel::getApplyToString(i); - } - QStringListModel * applyToModel = new QStringListModel(applyToItems, this); - ui_.applyTo->setModel(applyToModel); - - connect(ui_.highlightText, SIGNAL(toggled(bool)), SLOT(onHighlightTextToggled(bool))); - connect(ui_.customColors, SIGNAL(toggled(bool)), SLOT(onCustomColorsToggled(bool))); - connect(ui_.playSound, SIGNAL(toggled(bool)), SLOT(onPlaySoundToggled(bool))); - connect(ui_.customSound, SIGNAL(toggled(bool)), SLOT(onCustomSoundToggled(bool))); - connect(ui_.soundFileButton, SIGNAL(clicked()), SLOT(onSoundFileButtonClicked())); - - mapper_ = new QDataWidgetMapper(this); - hasValidIndex_ = false; - model_ = NULL; -} - -QtHighlightRuleWidget::~QtHighlightRuleWidget() -{ -} - -/** Widget does not gain ownership over the model */ -void QtHighlightRuleWidget::setModel(QtHighlightRulesItemModel* model) -{ - model_ = model; - mapper_->setModel(model_); -} - -void QtHighlightRuleWidget::setActiveIndex(const QModelIndex& index) -{ - if (index.isValid()) { - if (!hasValidIndex_) { - mapper_->addMapping(ui_.applyTo, QtHighlightRulesItemModel::ApplyTo, "currentIndex"); - mapper_->addMapping(ui_.senders, QtHighlightRulesItemModel::Sender, "plainText"); - mapper_->addMapping(ui_.keywords, QtHighlightRulesItemModel::Keyword, "plainText"); - mapper_->addMapping(ui_.nickIsKeyword, QtHighlightRulesItemModel::NickIsKeyword); - mapper_->addMapping(ui_.matchCase, QtHighlightRulesItemModel::MatchCase); - mapper_->addMapping(ui_.matchWholeWords, QtHighlightRulesItemModel::MatchWholeWords); - mapper_->addMapping(ui_.highlightText, QtHighlightRulesItemModel::HighlightText); - mapper_->addMapping(ui_.foreground, QtHighlightRulesItemModel::TextColor, "color"); - mapper_->addMapping(ui_.background, QtHighlightRulesItemModel::TextBackground, "color"); - mapper_->addMapping(ui_.playSound, QtHighlightRulesItemModel::PlaySound); - mapper_->addMapping(ui_.soundFile, QtHighlightRulesItemModel::SoundFile); - } - mapper_->setCurrentModelIndex(index); - ui_.customColors->setChecked(ui_.foreground->getColor().isValid() || ui_.background->getColor().isValid()); - ui_.customSound->setChecked(!ui_.soundFile->text().isEmpty()); - ui_.applyTo->focusWidget(); - } else { - if (hasValidIndex_) { - mapper_->clearMapping(); - } - } - - hasValidIndex_ = index.isValid(); -} - -void QtHighlightRuleWidget::onCustomColorsToggled(bool enabled) -{ - if (!enabled) { - ui_.foreground->setColor(QColor()); - ui_.background->setColor(QColor()); - } - ui_.foreground->setEnabled(enabled); - ui_.background->setEnabled(enabled); -} - -void QtHighlightRuleWidget::onCustomSoundToggled(bool enabled) -{ - if (enabled) { - if (ui_.soundFile->text().isEmpty()) { - onSoundFileButtonClicked(); - } - } else { - ui_.soundFile->clear(); - } - ui_.soundFile->setEnabled(enabled); - ui_.soundFileButton->setEnabled(enabled); -} - -void QtHighlightRuleWidget::onSoundFileButtonClicked() -{ - QString s = QFileDialog::getOpenFileName(this, tr("Choose sound file"), QString(), tr("Sound files (*.wav)")); - if (!s.isEmpty()) { - ui_.soundFile->setText(s); - } -} - -void QtHighlightRuleWidget::onHighlightTextToggled(bool enabled) -{ - ui_.customColors->setEnabled(enabled); -} - -void QtHighlightRuleWidget::onPlaySoundToggled(bool enabled) -{ - ui_.customSound->setEnabled(enabled); -} - -void QtHighlightRuleWidget::save() -{ - if (hasValidIndex_) { - mapper_->submit(); - } -} - -void QtHighlightRuleWidget::revert() -{ - if (hasValidIndex_) { - mapper_->revert(); - } -} - -} diff --git a/Swift/QtUI/QtHighlightRuleWidget.h b/Swift/QtUI/QtHighlightRuleWidget.h deleted file mode 100644 index 8a59a14..0000000 --- a/Swift/QtUI/QtHighlightRuleWidget.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2012 Maciej Niedzielski - * Licensed under the simplified BSD license. - * See Documentation/Licenses/BSD-simplified.txt for more information. - */ - -#pragma once - -#include <QWidget> -#include <QModelIndex> - -#include <Swift/QtUI/ui_QtHighlightRuleWidget.h> - -class QDataWidgetMapper; - -namespace Swift { - - class QtHighlightRulesItemModel; - - class QtHighlightRuleWidget : public QWidget - { - Q_OBJECT - - public: - explicit QtHighlightRuleWidget(QWidget* parent = NULL); - ~QtHighlightRuleWidget(); - - void setModel(QtHighlightRulesItemModel* model); - - public slots: - void setActiveIndex(const QModelIndex&); - void save(); - void revert(); - - private slots: - void onHighlightTextToggled(bool); - void onCustomColorsToggled(bool); - void onPlaySoundToggled(bool); - void onCustomSoundToggled(bool); - void onSoundFileButtonClicked(); - - private: - QDataWidgetMapper * mapper_; - QtHighlightRulesItemModel * model_; - bool hasValidIndex_; - Ui::QtHighlightRuleWidget ui_; - }; - -} diff --git a/Swift/QtUI/QtHighlightRuleWidget.ui b/Swift/QtUI/QtHighlightRuleWidget.ui deleted file mode 100644 index 9c465f9..0000000 --- a/Swift/QtUI/QtHighlightRuleWidget.ui +++ /dev/null @@ -1,260 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>QtHighlightRuleWidget</class> - <widget class="QWidget" name="QtHighlightRuleWidget"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>361</width> - <height>524</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <widget class="QGroupBox" name="groupBox"> - <property name="title"> - <string>Rule conditions</string> - </property> - <layout class="QFormLayout" name="formLayout"> - <property name="fieldGrowthPolicy"> - <enum>QFormLayout::ExpandingFieldsGrow</enum> - </property> - <item row="0" column="0" colspan="2"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Choose when this rule should be applied. -If you want to provide more than one sender or keyword, input them in separate lines.</string> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - <item row="1" column="0" colspan="2"> - <widget class="Line" name="line"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>&Apply to:</string> - </property> - <property name="buddy"> - <cstring>applyTo</cstring> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QComboBox" name="applyTo"/> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>&Senders:</string> - </property> - <property name="buddy"> - <cstring>senders</cstring> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QPlainTextEdit" name="senders"/> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string>&Keywords:</string> - </property> - <property name="buddy"> - <cstring>keywords</cstring> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="QPlainTextEdit" name="keywords"/> - </item> - <item row="5" column="1"> - <widget class="QCheckBox" name="nickIsKeyword"> - <property name="text"> - <string>Treat &nick as a keyword (in MUC)</string> - </property> - </widget> - </item> - <item row="6" column="1"> - <widget class="QCheckBox" name="matchWholeWords"> - <property name="text"> - <string>Match whole &words</string> - </property> - </widget> - </item> - <item row="7" column="1"> - <widget class="QCheckBox" name="matchCase"> - <property name="text"> - <string>Match &case</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="groupBox_2"> - <property name="title"> - <string>Actions</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QCheckBox" name="highlightText"> - <property name="text"> - <string>&Highlight text</string> - </property> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>28</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QCheckBox" name="customColors"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Custom c&olors:</string> - </property> - </widget> - </item> - <item> - <widget class="Swift::QtColorToolButton" name="foreground"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>&Foreground</string> - </property> - <property name="toolButtonStyle"> - <enum>Qt::ToolButtonTextBesideIcon</enum> - </property> - </widget> - </item> - <item> - <widget class="Swift::QtColorToolButton" name="background"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>&Background</string> - </property> - <property name="toolButtonStyle"> - <enum>Qt::ToolButtonTextBesideIcon</enum> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QCheckBox" name="playSound"> - <property name="text"> - <string>&Play sound</string> - </property> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>28</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QCheckBox" name="customSound"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Custom soun&d:</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="soundFile"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="readOnly"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="soundFileButton"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>...</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>101</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>Swift::QtColorToolButton</class> - <extends>QToolButton</extends> - <header>QtColorToolButton.h</header> - </customwidget> - </customwidgets> - <resources/> - <connections/> -</ui> diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp index 701170c..b0c1492 100644 --- a/Swift/QtUI/QtUIFactory.cpp +++ b/Swift/QtUI/QtUIFactory.cpp @@ -1,179 +1,179 @@ /* * Copyright (c) 2010-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include <Swift/QtUI/QtUIFactory.h> #include <QSplitter> #include <Swift/QtUI/QtXMLConsoleWidget.h> #include <Swift/QtUI/QtChatTabs.h> #include <Swift/QtUI/QtMainWindow.h> #include <Swift/QtUI/QtLoginWindow.h> #include <Swift/QtUI/QtSystemTray.h> #include <Swift/QtUI/QtSettingsProvider.h> #include <Swift/QtUI/QtMainWindow.h> #include <Swift/QtUI/QtChatWindow.h> #include <Swift/QtUI/QtJoinMUCWindow.h> #include <Swift/QtUI/QtChatWindowFactory.h> #include <Swift/QtUI/QtSwiftUtil.h> #include <Swift/QtUI/MUCSearch/QtMUCSearchWindow.h> #include <Swift/QtUI/UserSearch/QtUserSearchWindow.h> #include <Swift/QtUI/QtProfileWindow.h> #include <Swift/QtUI/QtContactEditWindow.h> #include <Swift/QtUI/QtAdHocCommandWindow.h> #include <Swift/QtUI/QtFileTransferListWidget.h> -#include <Swift/QtUI/QtHighlightEditorWidget.h> +#include <Swift/QtUI/QtHighlightEditor.h> #include <Swift/QtUI/Whiteboard/QtWhiteboardWindow.h> #include <Swift/Controllers/Settings/SettingsProviderHierachy.h> #include <Swift/QtUI/QtUISettingConstants.h> #include <Swift/QtUI/QtHistoryWindow.h> #include <Swiften/Whiteboard/WhiteboardSession.h> #include <Swift/QtUI/QtSingleWindow.h> #include <Swift/QtUI/QtBlockListEditorWindow.h> namespace Swift { QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, StatusCache* statusCache, bool startMinimized, bool emoticonsExist, bool enableAdHocCommandOnJID) : settings(settings), qtOnlySettings(qtOnlySettings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), timerFactory_(timerFactory), lastMainWindow(NULL), loginWindow(NULL), statusCache(statusCache), startMinimized(startMinimized), emoticonsExist_(emoticonsExist), enableAdHocCommandOnJID_(enableAdHocCommandOnJID) { chatFontSize = settings->getSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE); historyFontSize_ = settings->getSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE); } XMLConsoleWidget* QtUIFactory::createXMLConsoleWidget() { QtXMLConsoleWidget* widget = new QtXMLConsoleWidget(); tabs->addTab(widget); if (!tabs->isVisible()) { tabs->show(); } widget->show(); return widget; } HistoryWindow* QtUIFactory::createHistoryWindow(UIEventStream* uiEventStream) { QtHistoryWindow* window = new QtHistoryWindow(settings, uiEventStream); tabs->addTab(window); if (!tabs->isVisible()) { tabs->show(); } connect(window, SIGNAL(fontResized(int)), this, SLOT(handleHistoryWindowFontResized(int))); window->handleFontResized(historyFontSize_); window->show(); return window; } void QtUIFactory::handleHistoryWindowFontResized(int size) { historyFontSize_ = size; settings->storeSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE, size); } FileTransferListWidget* QtUIFactory::createFileTransferListWidget() { QtFileTransferListWidget* widget = new QtFileTransferListWidget(); tabs->addTab(widget); if (!tabs->isVisible()) { tabs->show(); } widget->show(); return widget; } MainWindow* QtUIFactory::createMainWindow(UIEventStream* eventStream) { lastMainWindow = new QtMainWindow(settings, eventStream, loginWindow->getMenus(), statusCache, emoticonsExist_, enableAdHocCommandOnJID_); return lastMainWindow; } LoginWindow* QtUIFactory::createLoginWindow(UIEventStream* eventStream) { loginWindow = new QtLoginWindow(eventStream, settings, timerFactory_); if (netbookSplitter) { netbookSplitter->insertAtFront(loginWindow); } connect(systemTray, SIGNAL(clicked()), loginWindow, SLOT(toggleBringToFront())); #ifndef SWIFT_MOBILE QVariant loginWindowGeometryVariant = qtOnlySettings->getQSettings()->value("loginWindowGeometry"); if (loginWindowGeometryVariant.isValid()) { loginWindow->restoreGeometry(loginWindowGeometryVariant.toByteArray()); } connect(loginWindow, SIGNAL(geometryChanged()), this, SLOT(handleLoginWindowGeometryChanged())); if (startMinimized) loginWindow->hide(); #endif return loginWindow; } void QtUIFactory::handleLoginWindowGeometryChanged() { qtOnlySettings->getQSettings()->setValue("loginWindowGeometry", loginWindow->saveGeometry()); } EventWindow* QtUIFactory::createEventWindow() { return lastMainWindow->getEventWindow(); } ChatListWindow* QtUIFactory::createChatListWindow(UIEventStream*) { return lastMainWindow->getChatListWindow(); } MUCSearchWindow* QtUIFactory::createMUCSearchWindow() { return new QtMUCSearchWindow(); } ChatWindow* QtUIFactory::createChatWindow(const JID& contact, UIEventStream* eventStream) { QtChatWindow* window = dynamic_cast<QtChatWindow*>(chatWindowFactory->createChatWindow(contact, eventStream)); chatWindows.push_back(window); std::vector<QPointer<QtChatWindow> > deletions; foreach (QPointer<QtChatWindow> existingWindow, chatWindows) { if (existingWindow.isNull()) { deletions.push_back(existingWindow); } else { connect(window, SIGNAL(fontResized(int)), existingWindow, SLOT(handleFontResized(int))); connect(existingWindow, SIGNAL(fontResized(int)), window, SLOT(handleFontResized(int))); } } foreach (QPointer<QtChatWindow> deletedWindow, deletions) { chatWindows.erase(std::remove(chatWindows.begin(), chatWindows.end(), deletedWindow), chatWindows.end()); } connect(window, SIGNAL(fontResized(int)), this, SLOT(handleChatWindowFontResized(int))); window->handleFontResized(chatFontSize); return window; } void QtUIFactory::handleChatWindowFontResized(int size) { chatFontSize = size; settings->storeSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE, size); } UserSearchWindow* QtUIFactory::createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream, const std::set<std::string>& groups) { return new QtUserSearchWindow(eventStream, type, groups, qtOnlySettings); } JoinMUCWindow* QtUIFactory::createJoinMUCWindow(UIEventStream* uiEventStream) { return new QtJoinMUCWindow(uiEventStream); } ProfileWindow* QtUIFactory::createProfileWindow() { return new QtProfileWindow(); } ContactEditWindow* QtUIFactory::createContactEditWindow() { return new QtContactEditWindow(); } WhiteboardWindow* QtUIFactory::createWhiteboardWindow(boost::shared_ptr<WhiteboardSession> whiteboardSession) { return new QtWhiteboardWindow(whiteboardSession); } -HighlightEditorWidget* QtUIFactory::createHighlightEditorWidget() { - return new QtHighlightEditorWidget(); +HighlightEditorWindow* QtUIFactory::createHighlightEditorWindow() { + return new QtHighlightEditor(qtOnlySettings); } BlockListEditorWidget *QtUIFactory::createBlockListEditorWidget() { return new QtBlockListEditorWindow(); } AdHocCommandWindow* QtUIFactory::createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) { return new QtAdHocCommandWindow(command); } } diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h index 4c50572..9c07e76 100644 --- a/Swift/QtUI/QtUIFactory.h +++ b/Swift/QtUI/QtUIFactory.h @@ -1,78 +1,78 @@ /* * Copyright (c) 2010-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include <QObject> #include <QPointer> #include <Swift/Controllers/UIInterfaces/UIFactory.h> class QSplitter; namespace Swift { class QtSettingsProvider; class SettingsProviderHierachy; class QtChatTabs; class QtSystemTray; class QtLoginWindow; class QtMainWindow; class QtChatTheme; class QtChatWindowFactory; class QtChatWindow; class TimerFactory; class historyWindow_; class WhiteboardSession; class StatusCache; class QtSingleWindow; class QtUIFactory : public QObject, public UIFactory { Q_OBJECT public: QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, StatusCache* statusCache, bool startMinimized, bool emoticonsExist, bool enableAdHocCommandOnJID); virtual XMLConsoleWidget* createXMLConsoleWidget(); virtual HistoryWindow* createHistoryWindow(UIEventStream*); virtual MainWindow* createMainWindow(UIEventStream* eventStream); virtual LoginWindow* createLoginWindow(UIEventStream* eventStream); virtual EventWindow* createEventWindow(); virtual ChatListWindow* createChatListWindow(UIEventStream*); virtual MUCSearchWindow* createMUCSearchWindow(); virtual ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream); virtual UserSearchWindow* createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream, const std::set<std::string>& groups); virtual JoinMUCWindow* createJoinMUCWindow(UIEventStream* uiEventStream); virtual ProfileWindow* createProfileWindow(); virtual ContactEditWindow* createContactEditWindow(); virtual FileTransferListWidget* createFileTransferListWidget(); virtual WhiteboardWindow* createWhiteboardWindow(boost::shared_ptr<WhiteboardSession> whiteboardSession); - virtual HighlightEditorWidget* createHighlightEditorWidget(); + virtual HighlightEditorWindow* createHighlightEditorWindow(); virtual BlockListEditorWidget* createBlockListEditorWidget(); virtual AdHocCommandWindow* createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command); private slots: void handleLoginWindowGeometryChanged(); void handleChatWindowFontResized(int); void handleHistoryWindowFontResized(int); private: SettingsProviderHierachy* settings; QtSettingsProvider* qtOnlySettings; QtChatTabs* tabs; QtSingleWindow* netbookSplitter; QtSystemTray* systemTray; QtChatWindowFactory* chatWindowFactory; TimerFactory* timerFactory_; QtMainWindow* lastMainWindow; QtLoginWindow* loginWindow; StatusCache* statusCache; std::vector<QPointer<QtChatWindow> > chatWindows; bool startMinimized; int chatFontSize; int historyFontSize_; bool emoticonsExist_; bool enableAdHocCommandOnJID_; }; } diff --git a/Swift/QtUI/QtWebKitChatView.cpp b/Swift/QtUI/QtWebKitChatView.cpp index 23bc099..1486293 100644 --- a/Swift/QtUI/QtWebKitChatView.cpp +++ b/Swift/QtUI/QtWebKitChatView.cpp @@ -1,942 +1,945 @@ /* * Copyright (c) 2010-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtWebKitChatView.h" #include <QtDebug> #include <QEventLoop> #include <QFile> #include <QDesktopServices> #include <QVBoxLayout> #include <QWebFrame> #include <QKeyEvent> #include <QStackedWidget> #include <QTimer> #include <QMessageBox> #include <QApplication> #include <QInputDialog> #include <QFileDialog> #include <Swiften/Base/Log.h> #include <Swiften/Base/FileSize.h> #include <Swiften/StringCodecs/Base64.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> #include <Swift/QtUI/QtWebView.h> #include <Swift/QtUI/QtChatWindow.h> #include <Swift/QtUI/QtChatWindowJSBridge.h> #include <Swift/QtUI/QtScaledAvatarCache.h> #include <Swift/QtUI/QtSwiftUtil.h> #include <Swift/QtUI/QtUtilities.h> #include <Swift/QtUI/MessageSnippet.h> #include <Swift/QtUI/SystemMessageSnippet.h> namespace Swift { const QString QtWebKitChatView::ButtonWhiteboardSessionCancel = QString("whiteboard-cancel"); const QString QtWebKitChatView::ButtonWhiteboardSessionAcceptRequest = QString("whiteboard-acceptrequest"); const QString QtWebKitChatView::ButtonWhiteboardShowWindow = QString("whiteboard-showwindow"); const QString QtWebKitChatView::ButtonFileTransferCancel = QString("filetransfer-cancel"); const QString QtWebKitChatView::ButtonFileTransferSetDescription = QString("filetransfer-setdescription"); const QString QtWebKitChatView::ButtonFileTransferSendRequest = QString("filetransfer-sendrequest"); const QString QtWebKitChatView::ButtonFileTransferAcceptRequest = QString("filetransfer-acceptrequest"); const QString QtWebKitChatView::ButtonMUCInvite = QString("mucinvite"); QtWebKitChatView::QtWebKitChatView(QtChatWindow* window, UIEventStream* eventStream, QtChatTheme* theme, QWidget* parent, bool disableAutoScroll) : QtChatView(parent), window_(window), eventStream_(eventStream), fontSizeSteps_(0), disableAutoScroll_(disableAutoScroll), previousMessageKind_(PreviosuMessageWasNone), previousMessageWasSelf_(false), showEmoticons_(false), insertingLastLine_(false), idCounter_(0) { theme_ = theme; QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0,0,0,0); webView_ = new QtWebView(this); connect(webView_, SIGNAL(linkClicked(const QUrl&)), SLOT(handleLinkClicked(const QUrl&))); connect(webView_, SIGNAL(loadFinished(bool)), SLOT(handleViewLoadFinished(bool))); connect(webView_, SIGNAL(gotFocus()), SIGNAL(gotFocus())); connect(webView_, SIGNAL(clearRequested()), SLOT(handleClearRequested())); connect(webView_, SIGNAL(fontGrowRequested()), SLOT(increaseFontSize())); connect(webView_, SIGNAL(fontShrinkRequested()), SLOT(decreaseFontSize())); #if defined (Q_OS_UNIX) && !defined(Q_OS_MAC) /* To give a border on Linux, where it looks bad without */ QStackedWidget* stack = new QStackedWidget(this); stack->addWidget(webView_); stack->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); stack->setLineWidth(2); mainLayout->addWidget(stack); #else mainLayout->addWidget(webView_); #endif #ifdef SWIFT_EXPERIMENTAL_FT setAcceptDrops(true); #endif webPage_ = new QWebPage(this); webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); if (Log::getLogLevel() == Log::debug) { webPage_->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); } webView_->setPage(webPage_); connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard())); connect(webPage_, SIGNAL(scrollRequested(int, int, const QRect&)), SLOT(handleScrollRequested(int, int, const QRect&))); viewReady_ = false; isAtBottom_ = true; resetView(); jsBridge = new QtChatWindowJSBridge(); addToJSEnvironment("chatwindow", jsBridge); connect(jsBridge, SIGNAL(buttonClicked(QString,QString,QString,QString,QString,QString)), this, SLOT(handleHTMLButtonClicked(QString,QString,QString,QString,QString,QString))); } QtWebKitChatView::~QtWebKitChatView() { delete jsBridge; } void QtWebKitChatView::handleClearRequested() { QMessageBox messageBox(this); messageBox.setWindowTitle(tr("Clear log")); messageBox.setText(tr("You are about to clear the contents of your chat log.")); messageBox.setInformativeText(tr("Are you sure?")); messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); messageBox.setDefaultButton(QMessageBox::Yes); int button = messageBox.exec(); if (button == QMessageBox::Yes) { logCleared(); resetView(); } } void QtWebKitChatView::handleKeyPressEvent(QKeyEvent* event) { webView_->keyPressEvent(event); } void QtWebKitChatView::addMessageBottom(boost::shared_ptr<ChatSnippet> snippet) { if (viewReady_) { addToDOM(snippet); } else { /* If this asserts, the previous queuing code was necessary and should be reinstated */ assert(false); } } void QtWebKitChatView::addMessageTop(boost::shared_ptr<ChatSnippet> snippet) { // save scrollbar maximum value if (!topMessageAdded_) { scrollBarMaximum_ = webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical); } topMessageAdded_ = true; QWebElement continuationElement = firstElement_.findFirst("#insert"); bool insert = snippet->getAppendToPrevious(); bool fallback = continuationElement.isNull(); boost::shared_ptr<ChatSnippet> newSnippet = (insert && fallback) ? snippet->getContinuationFallbackSnippet() : snippet; QWebElement newElement = snippetToDOM(newSnippet); if (insert && !fallback) { Q_ASSERT(!continuationElement.isNull()); continuationElement.replace(newElement); } else { continuationElement.removeFromDocument(); topInsertPoint_.prependOutside(newElement); } firstElement_ = newElement; if (lastElement_.isNull()) { lastElement_ = firstElement_; } if (fontSizeSteps_ != 0) { double size = 1.0 + 0.2 * fontSizeSteps_; QString sizeString(QString().setNum(size, 'g', 3) + "em"); const QWebElementCollection spans = firstElement_.findAll("span.swift_resizable"); Q_FOREACH (QWebElement span, spans) { span.setStyleProperty("font-size", sizeString); } } } QWebElement QtWebKitChatView::snippetToDOM(boost::shared_ptr<ChatSnippet> snippet) { QWebElement newElement = newInsertPoint_.clone(); newElement.setInnerXml(snippet->getContent()); Q_ASSERT(!newElement.isNull()); return newElement; } void QtWebKitChatView::addToDOM(boost::shared_ptr<ChatSnippet> snippet) { //qDebug() << snippet->getContent(); rememberScrolledToBottom(); bool insert = snippet->getAppendToPrevious(); QWebElement continuationElement = lastElement_.findFirst("#insert"); bool fallback = insert && continuationElement.isNull(); boost::shared_ptr<ChatSnippet> newSnippet = (insert && fallback) ? snippet->getContinuationFallbackSnippet() : snippet; QWebElement newElement = snippetToDOM(newSnippet); if (insert && !fallback) { Q_ASSERT(!continuationElement.isNull()); continuationElement.replace(newElement); } else { continuationElement.removeFromDocument(); newInsertPoint_.prependOutside(newElement); } lastElement_ = newElement; if (fontSizeSteps_ != 0) { double size = 1.0 + 0.2 * fontSizeSteps_; QString sizeString(QString().setNum(size, 'g', 3) + "em"); const QWebElementCollection spans = lastElement_.findAll("span.swift_resizable"); Q_FOREACH (QWebElement span, spans) { span.setStyleProperty("font-size", sizeString); } } //qDebug() << "-----------------"; //qDebug() << webPage_->mainFrame()->toHtml(); } void QtWebKitChatView::addLastSeenLine() { /* if the line is added we should break the snippet */ insertingLastLine_ = true; if (lineSeparator_.isNull()) { lineSeparator_ = newInsertPoint_.clone(); lineSeparator_.setInnerXml(QString("<hr/>")); newInsertPoint_.prependOutside(lineSeparator_); } else { QWebElement lineSeparatorC = lineSeparator_.clone(); lineSeparatorC.removeFromDocument(); } newInsertPoint_.prependOutside(lineSeparator_); } void QtWebKitChatView::replaceLastMessage(const QString& newMessage, const ChatWindow::TimestampBehaviour timestampBehaviour) { assert(viewReady_); rememberScrolledToBottom(); assert(!lastElement_.isNull()); QWebElement replace = lastElement_.findFirst("span.swift_message"); assert(!replace.isNull()); QString old = lastElement_.toOuterXml(); replace.setInnerXml(ChatSnippet::escape(newMessage)); if (timestampBehaviour == ChatWindow::UpdateTimestamp) { replace = lastElement_.findFirst("span.swift_time"); assert(!replace.isNull()); replace.setInnerXml(ChatSnippet::timeToEscapedString(QDateTime::currentDateTime())); } } void QtWebKitChatView::replaceLastMessage(const QString& newMessage, const QString& note) { rememberScrolledToBottom(); replaceLastMessage(newMessage, ChatWindow::KeepTimestamp); QWebElement replace = lastElement_.findFirst("span.swift_time"); assert(!replace.isNull()); replace.setInnerXml(ChatSnippet::escape(note)); } QString QtWebKitChatView::getLastSentMessage() { return lastElement_.toPlainText(); } void QtWebKitChatView::addToJSEnvironment(const QString& name, QObject* obj) { webView_->page()->currentFrame()->addToJavaScriptWindowObject(name, obj); } void QtWebKitChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime) { rememberScrolledToBottom(); QWebElement message = document_.findFirst("#" + id); if (!message.isNull()) { QWebElement replaceContent = message.findFirst("span.swift_inner_message"); assert(!replaceContent.isNull()); QString old = replaceContent.toOuterXml(); replaceContent.setInnerXml(ChatSnippet::escape(newMessage)); QWebElement replaceTime = message.findFirst("span.swift_time"); assert(!replaceTime.isNull()); old = replaceTime.toOuterXml(); replaceTime.setInnerXml(ChatSnippet::escape(tr("%1 edited").arg(ChatSnippet::timeToEscapedString(editTime)))); } else { qWarning() << "Trying to replace element with id " << id << " but it's not there."; } } void QtWebKitChatView::showEmoticons(bool show) { showEmoticons_ = show; { const QWebElementCollection spans = document_.findAll("span.swift_emoticon_image"); Q_FOREACH (QWebElement span, spans) { span.setStyleProperty("display", show ? "inline" : "none"); } } { const QWebElementCollection spans = document_.findAll("span.swift_emoticon_text"); Q_FOREACH (QWebElement span, spans) { span.setStyleProperty("display", show ? "none" : "inline"); } } } void QtWebKitChatView::copySelectionToClipboard() { if (!webPage_->selectedText().isEmpty()) { webPage_->triggerAction(QWebPage::Copy); } } void QtWebKitChatView::setAckXML(const QString& id, const QString& xml) { QWebElement message = document_.findFirst("#" + id); /* Deliberately not asserting here, so that when we start expiring old messages it won't hit us */ if (message.isNull()) return; QWebElement ackElement = message.findFirst("span.swift_ack"); assert(!ackElement.isNull()); ackElement.setInnerXml(xml); } void QtWebKitChatView::setReceiptXML(const QString& id, const QString& xml) { QWebElement message = document_.findFirst("#" + id); if (message.isNull()) return; QWebElement receiptElement = message.findFirst("span.swift_receipt"); assert(!receiptElement.isNull()); receiptElement.setInnerXml(xml); } void QtWebKitChatView::displayReceiptInfo(const QString& id, bool showIt) { QWebElement message = document_.findFirst("#" + id); if (message.isNull()) return; QWebElement receiptElement = message.findFirst("span.swift_receipt"); assert(!receiptElement.isNull()); receiptElement.setStyleProperty("display", showIt ? "inline" : "none"); } void QtWebKitChatView::rememberScrolledToBottom() { isAtBottom_ = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) >= (webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical) - 1); } void QtWebKitChatView::scrollToBottom() { isAtBottom_ = true; webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical)); webView_->update(); /* Work around redraw bug in some versions of Qt. */ } void QtWebKitChatView::handleFrameSizeChanged() { if (topMessageAdded_) { // adjust new scrollbar position int newMaximum = webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical); webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, newMaximum - scrollBarMaximum_); topMessageAdded_ = false; } if (isAtBottom_ && !disableAutoScroll_) { scrollToBottom(); } } void QtWebKitChatView::handleLinkClicked(const QUrl& url) { QDesktopServices::openUrl(url); } void QtWebKitChatView::handleViewLoadFinished(bool ok) { Q_ASSERT(ok); viewReady_ = true; } void QtWebKitChatView::increaseFontSize(int numSteps) { //qDebug() << "Increasing"; fontSizeSteps_ += numSteps; emit fontResized(fontSizeSteps_); } void QtWebKitChatView::decreaseFontSize() { fontSizeSteps_--; if (fontSizeSteps_ < 0) { fontSizeSteps_ = 0; } emit fontResized(fontSizeSteps_); } void QtWebKitChatView::resizeFont(int fontSizeSteps) { fontSizeSteps_ = fontSizeSteps; double size = 1.0 + 0.2 * fontSizeSteps_; QString sizeString(QString().setNum(size, 'g', 3) + "em"); //qDebug() << "Setting to " << sizeString; const QWebElementCollection spans = document_.findAll("span.swift_resizable"); Q_FOREACH (QWebElement span, spans) { span.setStyleProperty("font-size", sizeString); } webView_->setFontSizeIsMinimal(size == 1.0); } void QtWebKitChatView::resetView() { lastElement_ = QWebElement(); firstElement_ = lastElement_; topMessageAdded_ = false; scrollBarMaximum_ = 0; QString pageHTML = theme_->getTemplate(); pageHTML.replace("==bodyBackground==", "background-color:#e3e3e3"); pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getBase()); if (pageHTML.count("%@") > 3) { pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getMainCSS()); } pageHTML.replace(pageHTML.indexOf("%@"), 2, "Variants/Blue on Green.css"); pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*headerSnippet.getContent()*/); pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*footerSnippet.getContent()*/); QEventLoop syncLoop; connect(webView_, SIGNAL(loadFinished(bool)), &syncLoop, SLOT(quit())); webPage_->mainFrame()->setHtml(pageHTML); while (!viewReady_) { QTimer t; t.setSingleShot(true); connect(&t, SIGNAL(timeout()), &syncLoop, SLOT(quit())); t.start(50); syncLoop.exec(); } document_ = webPage_->mainFrame()->documentElement(); resetTopInsertPoint(); QWebElement chatElement = document_.findFirst("#Chat"); newInsertPoint_ = chatElement.clone(); newInsertPoint_.setOuterXml("<div id='swift_insert'/>"); chatElement.appendInside(newInsertPoint_); Q_ASSERT(!newInsertPoint_.isNull()); scrollToBottom(); connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection); } static QWebElement findElementWithID(QWebElement document, QString elementName, QString id) { QWebElementCollection elements = document.findAll(elementName); Q_FOREACH(QWebElement element, elements) { if (element.attribute("id") == id) { return element; } } return QWebElement(); } void QtWebKitChatView::setFileTransferProgress(QString id, const int percentageDone) { QWebElement ftElement = findElementWithID(document_, "div", id); if (ftElement.isNull()) { SWIFT_LOG(debug) << "Tried to access FT UI via invalid id!" << std::endl; return; } QWebElement progressBar = ftElement.findFirst("div.progressbar"); progressBar.setStyleProperty("width", QString::number(percentageDone) + "%"); QWebElement progressBarValue = ftElement.findFirst("div.progressbar-value"); progressBarValue.setInnerXml(QString::number(percentageDone) + " %"); } void QtWebKitChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& /* msg */) { QWebElement ftElement = findElementWithID(document_, "div", id); if (ftElement.isNull()) { SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id) << std::endl; return; } QString newInnerHTML = ""; if (state == ChatWindow::WaitingForAccept) { newInnerHTML = tr("Waiting for other side to accept the transfer.") + "<br/>" + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, id); } if (state == ChatWindow::Negotiating) { // replace with text "Negotiaging" + Cancel button newInnerHTML = tr("Negotiating...") + "<br/>" + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, id); } else if (state == ChatWindow::Transferring) { // progress bar + Cancel Button newInnerHTML = "<div style=\"position: relative; width: 90%; height: 20px; border: 2px solid grey; -webkit-border-radius: 10px;\">" "<div class=\"progressbar\" style=\"width: 0%; height: 100%; background: #AAA; -webkit-border-radius: 6px;\">" "<div class=\"progressbar-value\" style=\"position: absolute; top: 0px; left: 0px; width: 100%; text-align: center; padding-top: 2px;\">" "0%" "</div>" "</div>" "</div>" + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, id); } else if (state == ChatWindow::Canceled) { newInnerHTML = tr("Transfer has been canceled!"); } else if (state == ChatWindow::Finished) { // text "Successful transfer" newInnerHTML = tr("Transfer completed successfully."); } else if (state == ChatWindow::FTFailed) { newInnerHTML = tr("Transfer failed."); } ftElement.setInnerXml(newInnerHTML); } void QtWebKitChatView::setWhiteboardSessionStatus(QString id, const ChatWindow::WhiteboardSessionState state) { QWebElement divElement = findElementWithID(document_, "div", id); QString newInnerHTML; if (state == ChatWindow::WhiteboardAccepted) { newInnerHTML = tr("Started whiteboard chat") + "<br/>" + buildChatWindowButton(tr("Show whiteboard"), ButtonWhiteboardShowWindow, id); } else if (state == ChatWindow::WhiteboardTerminated) { newInnerHTML = tr("Whiteboard chat has been canceled"); } else if (state == ChatWindow::WhiteboardRejected) { newInnerHTML = tr("Whiteboard chat request has been rejected"); } divElement.setInnerXml(newInnerHTML); } void QtWebKitChatView::setMUCInvitationJoined(QString id) { QWebElement divElement = findElementWithID(document_, "div", id); QWebElement buttonElement = findElementWithID(divElement, "input", "mucinvite"); if (!buttonElement.isNull()) { buttonElement.setAttribute("value", tr("Return to room")); } } void QtWebKitChatView::handleScrollRequested(int, int dy, const QRect&) { rememberScrolledToBottom(); int pos = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) - dy; emit scrollRequested(pos); if (pos == 0) { emit scrollReachedTop(); } else if (pos == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical)) { emit scrollReachedBottom(); } } int QtWebKitChatView::getSnippetPositionByDate(const QDate& date) { QWebElement message = webPage_->mainFrame()->documentElement().findFirst(".date" + date.toString(Qt::ISODate)); return message.geometry().top(); } void QtWebKitChatView::resetTopInsertPoint() { QWebElement continuationElement = firstElement_.findFirst("#insert"); continuationElement.removeFromDocument(); firstElement_ = QWebElement(); topInsertPoint_.removeFromDocument(); QWebElement chatElement = document_.findFirst("#Chat"); topInsertPoint_ = chatElement.clone(); topInsertPoint_.setOuterXml("<div id='swift_insert'/>"); chatElement.prependInside(topInsertPoint_); } std::string QtWebKitChatView::addMessage( const ChatWindow::ChatMessage& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) { return addMessage(chatMessageToHTML(message), senderName, senderIsSelf, label, avatarPath, "", time, highlight, ChatSnippet::getDirection(message)); } +QString QtWebKitChatView::getHighlightSpanStart(const std::string& text, const std::string& background) { + QString ecsapeColor = QtUtilities::htmlEscape(P2QSTRING(text)); + QString escapeBackground = QtUtilities::htmlEscape(P2QSTRING(background)); + if (ecsapeColor.isEmpty()) { + ecsapeColor = "black"; + } + if (escapeBackground.isEmpty()) { + escapeBackground = "yellow"; + } + return QString("<span style=\"color: %1; background: %2\">").arg(ecsapeColor).arg(escapeBackground); +} + +QString QtWebKitChatView::getHighlightSpanStart(const HighlightAction& highlight) { + return getHighlightSpanStart(highlight.getTextColor(), highlight.getTextBackground()); +} + QString QtWebKitChatView::chatMessageToHTML(const ChatWindow::ChatMessage& message) { QString result; foreach (boost::shared_ptr<ChatWindow::ChatMessagePart> part, message.getParts()) { boost::shared_ptr<ChatWindow::ChatTextMessagePart> textPart; boost::shared_ptr<ChatWindow::ChatURIMessagePart> uriPart; boost::shared_ptr<ChatWindow::ChatEmoticonMessagePart> emoticonPart; boost::shared_ptr<ChatWindow::ChatHighlightingMessagePart> highlightPart; if ((textPart = boost::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(part))) { QString text = QtUtilities::htmlEscape(P2QSTRING(textPart->text)); text.replace("\n","<br/>"); result += text; continue; } if ((uriPart = boost::dynamic_pointer_cast<ChatWindow::ChatURIMessagePart>(part))) { QString uri = QtUtilities::htmlEscape(P2QSTRING(uriPart->target)); result += "<a href='" + uri + "' >" + uri + "</a>"; continue; } if ((emoticonPart = boost::dynamic_pointer_cast<ChatWindow::ChatEmoticonMessagePart>(part))) { QString textStyle = showEmoticons_ ? "style='display:none'" : ""; QString imageStyle = showEmoticons_ ? "" : "style='display:none'"; result += "<span class='swift_emoticon_image' " + imageStyle + "><img src='" + P2QSTRING(emoticonPart->imagePath) + "'/></span><span class='swift_emoticon_text' " + textStyle + ">" + QtUtilities::htmlEscape(P2QSTRING(emoticonPart->alternativeText)) + "</span>"; continue; } if ((highlightPart = boost::dynamic_pointer_cast<ChatWindow::ChatHighlightingMessagePart>(part))) { - //FIXME: Maybe do something here. Anything, really. + QString spanStart = getHighlightSpanStart(highlightPart->foregroundColor, highlightPart->backgroundColor); + result += spanStart + QtUtilities::htmlEscape(P2QSTRING(highlightPart->text)) + "</span>"; continue; } } return result; } - -QString QtWebKitChatView::getHighlightSpanStart(const HighlightAction& highlight) { - QString color = QtUtilities::htmlEscape(P2QSTRING(highlight.getTextColor())); - QString background = QtUtilities::htmlEscape(P2QSTRING(highlight.getTextBackground())); - if (color.isEmpty()) { - color = "black"; - } - if (background.isEmpty()) { - background = "yellow"; - } - - return QString("<span style=\"color: %1; background: %2\">").arg(color).arg(background); -} - std::string QtWebKitChatView::addMessage( const QString& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time, const HighlightAction& highlight, ChatSnippet::Direction direction) { QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str()); QString htmlString; if (label) { htmlString = QString("<span style=\"border: thin dashed grey; padding-left: .5em; padding-right: .5em; color: %1; background-color: %2; font-size: 90%; margin-right: .5em; \" class='swift_label'>").arg(QtUtilities::htmlEscape(P2QSTRING(label->getForegroundColor()))).arg(QtUtilities::htmlEscape(P2QSTRING(label->getBackgroundColor()))); htmlString += QString("%1</span> ").arg(QtUtilities::htmlEscape(P2QSTRING(label->getDisplayMarking()))); } QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">"; QString styleSpanEnd = style == "" ? "" : "</span>"; QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : ""; QString highlightSpanEnd = highlight.highlightText() ? "</span>" : ""; htmlString += "<span class='swift_inner_message'>" + styleSpanStart + highlightSpanStart + message + highlightSpanEnd + styleSpanEnd + "</span>" ; bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf); QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded(); std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++); addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), direction)); previousMessageWasSelf_ = senderIsSelf; previousSenderName_ = P2QSTRING(senderName); previousMessageKind_ = PreviousMessageWasMessage; return id; } std::string QtWebKitChatView::addAction(const ChatWindow::ChatMessage& message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) { return addMessage(" *" + chatMessageToHTML(message) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time, highlight, ChatSnippet::getDirection(message)); } static QString encodeButtonArgument(const QString& str) { return QtUtilities::htmlEscape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str))))); } static QString decodeButtonArgument(const QString& str) { return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str)))); } QString QtWebKitChatView::buildChatWindowButton(const QString& name, const QString& id, const QString& arg1, const QString& arg2, const QString& arg3, const QString& arg4, const QString& arg5) { QRegExp regex("[A-Za-z][A-Za-z0-9\\-\\_]+"); Q_ASSERT(regex.exactMatch(id)); QString html = QString("<input id='%2' type='submit' value='%1' onclick='chatwindow.buttonClicked(\"%2\", \"%3\", \"%4\", \"%5\", \"%6\", \"%7\");' />").arg(name).arg(id).arg(encodeButtonArgument(arg1)).arg(encodeButtonArgument(arg2)).arg(encodeButtonArgument(arg3)).arg(encodeButtonArgument(arg4)).arg(encodeButtonArgument(arg5)); return html; } std::string QtWebKitChatView::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) { SWIFT_LOG(debug) << "addFileTransfer" << std::endl; QString ft_id = QString("ft%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); QString actionText; QString htmlString; QString formattedFileSize = P2QSTRING(formatSize(sizeInBytes)); if (senderIsSelf) { // outgoing actionText = tr("Send file"); htmlString = actionText + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" + "<div id='" + ft_id + "'>" + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + buildChatWindowButton(tr("Set Description"), ButtonFileTransferSetDescription, ft_id) + buildChatWindowButton(tr("Send"), ButtonFileTransferSendRequest, ft_id) + "</div>"; } else { // incoming actionText = tr("Receiving file"); htmlString = actionText + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" + "<div id='" + ft_id + "'>" + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + buildChatWindowButton(tr("Accept"), ButtonFileTransferAcceptRequest, ft_id, P2QSTRING(filename)) + "</div>"; } //addMessage(message, senderName, senderIsSelf, boost::shared_ptr<SecurityLabel>(), "", boost::posix_time::second_clock::local_time()); bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasFileTransfer, senderName, senderIsSelf); QString qAvatarPath = "qrc:/icons/avatar.png"; std::string id = "ftmessage" + boost::lexical_cast<std::string>(idCounter_++); addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), ChatSnippet::getDirection(actionText))); previousMessageWasSelf_ = senderIsSelf; previousSenderName_ = P2QSTRING(senderName); previousMessageKind_ = PreviousMessageWasFileTransfer; return Q2PSTRING(ft_id); } void QtWebKitChatView::setFileTransferProgress(std::string id, const int percentageDone) { setFileTransferProgress(P2QSTRING(id), percentageDone); } void QtWebKitChatView::setFileTransferStatus(std::string id, const ChatWindow::FileTransferState state, const std::string& msg) { setFileTransferStatus(P2QSTRING(id), state, P2QSTRING(msg)); } std::string QtWebKitChatView::addWhiteboardRequest(const QString& contact, bool senderIsSelf) { QString wb_id = QString("wb%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); QString htmlString; QString actionText; if (senderIsSelf) { actionText = tr("Starting whiteboard chat"); htmlString = "<div id='" + wb_id + "'>" + actionText + "<br />"+ buildChatWindowButton(tr("Cancel"), ButtonWhiteboardSessionCancel, wb_id) + "</div>"; } else { actionText = tr("%1 would like to start a whiteboard chat"); htmlString = "<div id='" + wb_id + "'>" + actionText.arg(QtUtilities::htmlEscape(contact)) + ": <br/>" + buildChatWindowButton(tr("Cancel"), ButtonWhiteboardSessionCancel, wb_id) + buildChatWindowButton(tr("Accept"), ButtonWhiteboardSessionAcceptRequest, wb_id) + "</div>"; } QString qAvatarPath = "qrc:/icons/avatar.png"; std::string id = "wbmessage" + boost::lexical_cast<std::string>(idCounter_++); addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(contact), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, false, theme_, P2QSTRING(id), ChatSnippet::getDirection(actionText))); previousMessageWasSelf_ = false; previousSenderName_ = contact; return Q2PSTRING(wb_id); } void QtWebKitChatView::setWhiteboardSessionStatus(const std::string& id, const ChatWindow::WhiteboardSessionState state) { setWhiteboardSessionStatus(P2QSTRING(id), state); } void QtWebKitChatView::handleHTMLButtonClicked(QString id, QString encodedArgument1, QString encodedArgument2, QString encodedArgument3, QString encodedArgument4, QString encodedArgument5) { QString arg1 = decodeButtonArgument(encodedArgument1); QString arg2 = decodeButtonArgument(encodedArgument2); QString arg3 = decodeButtonArgument(encodedArgument3); QString arg4 = decodeButtonArgument(encodedArgument4); QString arg5 = decodeButtonArgument(encodedArgument5); if (id.startsWith(ButtonFileTransferCancel)) { QString ft_id = arg1; window_->onFileTransferCancel(Q2PSTRING(ft_id)); } else if (id.startsWith(ButtonFileTransferSetDescription)) { QString ft_id = arg1; bool ok = false; QString text = QInputDialog::getText(this, tr("File transfer description"), tr("Description:"), QLineEdit::Normal, "", &ok); if (ok) { descriptions_[ft_id] = text; } } else if (id.startsWith(ButtonFileTransferSendRequest)) { QString ft_id = arg1; QString text = descriptions_.find(ft_id) == descriptions_.end() ? QString() : descriptions_[ft_id]; window_->onFileTransferStart(Q2PSTRING(ft_id), Q2PSTRING(text)); } else if (id.startsWith(ButtonFileTransferAcceptRequest)) { QString ft_id = arg1; QString filename = arg2; QString path = QFileDialog::getSaveFileName(this, tr("Save File"), filename); if (!path.isEmpty()) { window_->onFileTransferAccept(Q2PSTRING(ft_id), Q2PSTRING(path)); } } else if (id.startsWith(ButtonWhiteboardSessionAcceptRequest)) { QString id = arg1; setWhiteboardSessionStatus(id, ChatWindow::WhiteboardAccepted); window_->onWhiteboardSessionAccept(); } else if (id.startsWith(ButtonWhiteboardSessionCancel)) { QString id = arg1; setWhiteboardSessionStatus(id, ChatWindow::WhiteboardTerminated); window_->onWhiteboardSessionCancel(); } else if (id.startsWith(ButtonWhiteboardShowWindow)) { QString id = arg1; window_->onWhiteboardWindowShow(); } else if (id.startsWith(ButtonMUCInvite)) { QString roomJID = arg1; QString password = arg2; QString elementID = arg3; QString isImpromptu = arg4; QString isContinuation = arg5; eventStream_->send(boost::make_shared<JoinMUCUIEvent>(Q2PSTRING(roomJID), Q2PSTRING(password), boost::optional<std::string>(), false, false, isImpromptu.contains("true"), isContinuation.contains("true"))); setMUCInvitationJoined(elementID); } else { SWIFT_LOG(debug) << "Unknown HTML button! ( " << Q2PSTRING(id) << " )" << std::endl; } } void QtWebKitChatView::addErrorMessage(const ChatWindow::ChatMessage& errorMessage) { if (window_->isWidgetSelected()) { window_->onAllMessagesRead(); } QString errorMessageHTML(chatMessageToHTML(errorMessage)); addMessageBottom(boost::make_shared<SystemMessageSnippet>("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_, ChatSnippet::getDirection(errorMessage))); previousMessageWasSelf_ = false; previousMessageKind_ = PreviousMessageWasSystem; } void QtWebKitChatView::addSystemMessage(const ChatWindow::ChatMessage& message, ChatWindow::Direction direction) { if (window_->isWidgetSelected()) { window_->onAllMessagesRead(); } QString messageHTML = chatMessageToHTML(message); addMessageBottom(boost::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, getActualDirection(message, direction))); previousMessageKind_ = PreviousMessageWasSystem; } void QtWebKitChatView::replaceWithAction(const ChatWindow::ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) { replaceMessage(" *" + chatMessageToHTML(message) + "*", id, time, "font-style:italic ", highlight); } void QtWebKitChatView::replaceMessage(const ChatWindow::ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) { replaceMessage(chatMessageToHTML(message), id, time, "", highlight); } void QtWebKitChatView::replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style, const HighlightAction& highlight) { if (!id.empty()) { if (window_->isWidgetSelected()) { window_->onAllMessagesRead(); } QString messageHTML(message); QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">"; QString styleSpanEnd = style == "" ? "" : "</span>"; QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : ""; QString highlightSpanEnd = highlight.highlightText() ? "</span>" : ""; messageHTML = styleSpanStart + highlightSpanStart + messageHTML + highlightSpanEnd + styleSpanEnd; replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time)); } else { std::cerr << "Trying to replace a message with no id"; } } void QtWebKitChatView::addPresenceMessage(const ChatWindow::ChatMessage& message, ChatWindow::Direction direction) { if (window_->isWidgetSelected()) { window_->onAllMessagesRead(); } QString messageHTML = chatMessageToHTML(message); addMessageBottom(boost::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, getActualDirection(message, direction))); previousMessageKind_ = PreviousMessageWasPresence; } void QtWebKitChatView::replaceLastMessage(const ChatWindow::ChatMessage& message, const ChatWindow::TimestampBehaviour timestampBehaviour) { replaceLastMessage(chatMessageToHTML(message), timestampBehaviour); } void QtWebKitChatView::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct, bool isImpromptu, bool isContinuation) { if (window_->isWidgetSelected()) { window_->onAllMessagesRead(); } QString message; if (isImpromptu) { message = QObject::tr("You've been invited to join a chat.") + "\n"; } else { message = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + "\n"; } QString htmlString = message; if (!reason.empty()) { htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "\n"; } if (!direct) { htmlString += QObject::tr("This person may not have really sent this invitation!") + "\n"; } htmlString = chatMessageToHTML(ChatWindow::ChatMessage(Q2PSTRING(htmlString))); QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); htmlString += "<div id='" + id + "'>" + buildChatWindowButton(chatMessageToHTML(ChatWindow::ChatMessage(Q2PSTRING((tr("Accept Invite"))))), ButtonMUCInvite, QtUtilities::htmlEscape(P2QSTRING(jid.toString())), QtUtilities::htmlEscape(P2QSTRING(password)), id, QtUtilities::htmlEscape(isImpromptu ? "true" : "false"), QtUtilities::htmlEscape(isContinuation ? "true" : "false")) + "</div>"; bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMUCInvite, senderName, false); QString qAvatarPath = "qrc:/icons/avatar.png"; addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id, ChatSnippet::getDirection(message))); previousMessageWasSelf_ = false; previousSenderName_ = P2QSTRING(senderName); previousMessageKind_ = PreviousMessageWasMUCInvite; } void QtWebKitChatView::setAckState(std::string const& id, ChatWindow::AckState state) { QString xml; switch (state) { case ChatWindow::Pending: xml = "<img src='qrc:/icons/throbber.gif' title='" + tr("This message has not been received by your server yet.") + "'/>"; displayReceiptInfo(P2QSTRING(id), false); break; case ChatWindow::Received: xml = ""; displayReceiptInfo(P2QSTRING(id), true); break; case ChatWindow::Failed: xml = "<img src='qrc:/icons/error.png' title='" + tr("This message may not have been transmitted.") + "'/>"; break; } setAckXML(P2QSTRING(id), xml); } void QtWebKitChatView::setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) { QString xml; switch (state) { case ChatWindow::ReceiptReceived: xml = "<img src='qrc:/icons/check.png' title='" + tr("The receipt for this message has been received.") + "'/>"; break; case ChatWindow::ReceiptRequested: xml = "<img src='qrc:/icons/warn.png' title='" + tr("The receipt for this message has not yet been received. The recipient(s) might not have received this message.") + "'/>"; break; case ChatWindow::ReceiptFailed: xml = "<img src='qrc:/icons/error.png' title='" + tr("Failed to transmit message to the receipient(s).") + "'/>"; } setReceiptXML(P2QSTRING(id), xml); } bool QtWebKitChatView::appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) { bool result = previousMessageKind_ == messageKind && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_&& previousSenderName_ == P2QSTRING(senderName))); if (insertingLastLine_) { insertingLastLine_ = false; return false; } return result; } ChatSnippet::Direction QtWebKitChatView::getActualDirection(const ChatWindow::ChatMessage& message, ChatWindow::Direction direction) { if (direction == ChatWindow::DefaultDirection) { return QCoreApplication::translate("QApplication", "QT_LAYOUT_DIRECTION") == "RTL" ? ChatSnippet::RTL : ChatSnippet::LTR; } else { return ChatSnippet::getDirection(message); } } // void QtWebKitChatView::setShowEmoticons(bool value) { // showEmoticons_ = value; // } } diff --git a/Swift/QtUI/QtWebKitChatView.h b/Swift/QtUI/QtWebKitChatView.h index fb6e4da..925ceeb 100644 --- a/Swift/QtUI/QtWebKitChatView.h +++ b/Swift/QtUI/QtWebKitChatView.h @@ -1,187 +1,188 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon + * Copyright (c) 2010-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include <QString> #include <QWidget> #include <QList> #include <QWebElement> #include <boost/shared_ptr.hpp> #include <Swiften/Base/Override.h> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> #include <Swift/QtUI/ChatSnippet.h> #include <Swift/QtUI/QtChatView.h> class QWebPage; class QUrl; class QDate; namespace Swift { class QtWebView; class QtChatTheme; class QtChatWindowJSBridge; class UIEventStream; class QtChatWindow; class QtWebKitChatView : public QtChatView { Q_OBJECT public: static const QString ButtonWhiteboardSessionCancel; static const QString ButtonWhiteboardSessionAcceptRequest; static const QString ButtonWhiteboardShowWindow; static const QString ButtonFileTransferCancel; static const QString ButtonFileTransferSetDescription; static const QString ButtonFileTransferSendRequest; static const QString ButtonFileTransferAcceptRequest; static const QString ButtonMUCInvite; public: QtWebKitChatView(QtChatWindow* window, UIEventStream* eventStream, QtChatTheme* theme, QWidget* parent, bool disableAutoScroll = false); ~QtWebKitChatView(); /** Add message to window. * @return id of added message (for acks). */ virtual std::string addMessage(const ChatWindow::ChatMessage& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) SWIFTEN_OVERRIDE; /** Adds action to window. * @return id of added message (for acks); */ virtual std::string addAction(const ChatWindow::ChatMessage& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) SWIFTEN_OVERRIDE; virtual void addSystemMessage(const ChatWindow::ChatMessage& message, ChatWindow::Direction direction) SWIFTEN_OVERRIDE; virtual void addPresenceMessage(const ChatWindow::ChatMessage& message, ChatWindow::Direction direction) SWIFTEN_OVERRIDE; virtual void addErrorMessage(const ChatWindow::ChatMessage& message) SWIFTEN_OVERRIDE; virtual void replaceMessage(const ChatWindow::ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) SWIFTEN_OVERRIDE; virtual void replaceWithAction(const ChatWindow::ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) SWIFTEN_OVERRIDE; void replaceLastMessage(const ChatWindow::ChatMessage& message, const ChatWindow::TimestampBehaviour timestampBehaviour); void setAckState(const std::string& id, ChatWindow::AckState state); virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) SWIFTEN_OVERRIDE; virtual void setFileTransferProgress(std::string, const int percentageDone) SWIFTEN_OVERRIDE; virtual void setFileTransferStatus(std::string, const ChatWindow::FileTransferState state, const std::string& msg = "") SWIFTEN_OVERRIDE; virtual void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct, bool isImpromptu, bool isContinuation) SWIFTEN_OVERRIDE; virtual std::string addWhiteboardRequest(const QString& contact, bool senderIsSelf) SWIFTEN_OVERRIDE; virtual void setWhiteboardSessionStatus(const std::string& id, const ChatWindow::WhiteboardSessionState state) SWIFTEN_OVERRIDE; virtual void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) SWIFTEN_OVERRIDE; virtual void showEmoticons(bool show) SWIFTEN_OVERRIDE; void addMessageTop(boost::shared_ptr<ChatSnippet> snippet); void addMessageBottom(boost::shared_ptr<ChatSnippet> snippet); int getSnippetPositionByDate(const QDate& date); // FIXME : This probably shouldn't have been public void addLastSeenLine(); private: // previously public, now private void replaceLastMessage(const QString& newMessage, const ChatWindow::TimestampBehaviour timestampBehaviour); void replaceLastMessage(const QString& newMessage, const QString& note); void replaceMessage(const QString& newMessage, const QString& id, const QDateTime& time); void rememberScrolledToBottom(); void setAckXML(const QString& id, const QString& xml); void setReceiptXML(const QString& id, const QString& xml); void displayReceiptInfo(const QString& id, bool showIt); QString getLastSentMessage(); void addToJSEnvironment(const QString&, QObject*); void setFileTransferProgress(QString id, const int percentageDone); void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg); void setWhiteboardSessionStatus(QString id, const ChatWindow::WhiteboardSessionState state); void setMUCInvitationJoined(QString id); signals: void gotFocus(); void fontResized(int); void logCleared(); void scrollRequested(int pos); void scrollReachedTop(); void scrollReachedBottom(); public slots: void copySelectionToClipboard(); void handleLinkClicked(const QUrl&); void resetView(); void resetTopInsertPoint(); void increaseFontSize(int numSteps = 1); void decreaseFontSize(); void resizeFont(int fontSizeSteps); void scrollToBottom(); void handleKeyPressEvent(QKeyEvent* event); private slots: void handleViewLoadFinished(bool); void handleFrameSizeChanged(); void handleClearRequested(); void handleScrollRequested(int dx, int dy, const QRect& rectToScroll); void handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3, QString arg4, QString arg5); private: enum PreviousMessageKind { PreviosuMessageWasNone, PreviousMessageWasMessage, PreviousMessageWasSystem, PreviousMessageWasPresence, PreviousMessageWasFileTransfer, PreviousMessageWasMUCInvite }; std::string addMessage( const QString& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time, const HighlightAction& highlight, ChatSnippet::Direction direction); void replaceMessage( const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style, const HighlightAction& highlight); bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf); static ChatSnippet::Direction getActualDirection(const ChatWindow::ChatMessage& message, ChatWindow::Direction direction); - QString chatMessageToHTML(const ChatWindow::ChatMessage& message); + QString getHighlightSpanStart(const std::string& text, const std::string& background); QString getHighlightSpanStart(const HighlightAction& highlight); + QString chatMessageToHTML(const ChatWindow::ChatMessage& message); static QString buildChatWindowButton(const QString& name, const QString& id, const QString& arg1 = QString(), const QString& arg2 = QString(), const QString& arg3 = QString(), const QString& arg4 = QString(), const QString& arg5 = QString()); private: void headerEncode(); void messageEncode(); void addToDOM(boost::shared_ptr<ChatSnippet> snippet); QWebElement snippetToDOM(boost::shared_ptr<ChatSnippet> snippet); QtChatWindow* window_; UIEventStream* eventStream_; bool viewReady_; bool isAtBottom_; bool topMessageAdded_; int scrollBarMaximum_; QtWebView* webView_; QWebPage* webPage_; int fontSizeSteps_; QtChatTheme* theme_; QWebElement newInsertPoint_; QWebElement topInsertPoint_; QWebElement lineSeparator_; QWebElement lastElement_; QWebElement firstElement_; QWebElement document_; bool disableAutoScroll_; QtChatWindowJSBridge* jsBridge; PreviousMessageKind previousMessageKind_; bool previousMessageWasSelf_; bool showEmoticons_; bool insertingLastLine_; int idCounter_; QString previousSenderName_; std::map<QString, QString> descriptions_; }; } diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index dd7d0c3..26e738a 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -1,444 +1,441 @@ import os, shutil, datetime, re, time import Version def generateDefaultTheme(dir) : sourceDir = dir.abspath result = "<!-- WARNING: This file is automatically generated. Any changes will be overwritten. -->\n" result += "<RCC version =\"1.0\">" result += "<qresource prefix=\"/themes/Default\">" for (path, dirs, files) in os.walk(sourceDir) : for file in files : filePath = os.path.join(path,file) result += "<file alias=\"%(alias)s\">%(path)s</file>" % { "alias": filePath[len(sourceDir)+1:], "path": filePath } result += "</qresource>" result += "</RCC>" return result Import("env") myenv = env.Clone() # Disable warnings that affect Qt myenv["CXXFLAGS"] = filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"]) if "clang" in env["CC"] : myenv.Append(CXXFLAGS = ["-Wno-float-equal", "-Wno-shorten-64-to-32", "-Wno-missing-prototypes", "-Wno-unreachable-code", "-Wno-disabled-macro-expansion", "-Wno-unused-private-field", "-Wno-extra-semi", "-Wno-duplicate-enum", "-Wno-missing-variable-declarations", "-Wno-conversion", "-Wno-undefined-reinterpret-cast"]) myenv.UseFlags(env["SWIFT_CONTROLLERS_FLAGS"]) myenv.UseFlags(env["SWIFTOOLS_FLAGS"]) if myenv["HAVE_XSS"] : myenv.UseFlags(env["XSS_FLAGS"]) if env["PLATFORM"] == "posix" : myenv.Append(LIBS = ["X11"]) if myenv["HAVE_SPARKLE"] : myenv.UseFlags(env["SPARKLE_FLAGS"]) myenv.UseFlags(env["SWIFTEN_FLAGS"]) myenv.UseFlags(env["SWIFTEN_DEP_FLAGS"]) if myenv.get("HAVE_BREAKPAD") : myenv.UseFlags(env["BREAKPAD_FLAGS"]) if myenv.get("HAVE_GROWL", False) : myenv.UseFlags(myenv["GROWL_FLAGS"]) myenv.Append(CPPDEFINES = ["HAVE_GROWL"]) if myenv["swift_mobile"] : myenv.Append(CPPDEFINES = ["SWIFT_MOBILE"]) if myenv.get("HAVE_SNARL", False) : myenv.UseFlags(myenv["SNARL_FLAGS"]) myenv.Append(CPPDEFINES = ["HAVE_SNARL"]) if myenv.get("HAVE_HUNSPELL", True): myenv.Append(CPPDEFINES = ["HAVE_HUNSPELL"]) myenv.UseFlags(myenv["HUNSPELL_FLAGS"]) if env["PLATFORM"] == "win32" : myenv.Append(LIBS = ["cryptui"]) myenv.UseFlags(myenv["PLATFORM_FLAGS"]) myenv.Tool("qt4", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.Tool("nsis", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.Tool("wix", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.Tool("textfile", toolpath = ["#/BuildTools/SCons/Tools"]) qt4modules = ['QtCore', 'QtWebKit', 'QtGui'] if myenv["qt5"] : qt_version = '5' qt4modules += ['QtWidgets', 'QtWebKitWidgets', 'QtMultimedia'] else : qt_version = '4' if env["PLATFORM"] == "posix" : qt4modules += ["QtDBus"] if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" : qt4modules += ["QtNetwork"] myenv.EnableQt4Modules(qt4modules, debug = False, version = qt_version) myenv.Append(CPPPATH = ["."]) if env["PLATFORM"] == "win32" : #myenv.Append(LINKFLAGS = ["/SUBSYSTEM:CONSOLE"]) myenv.Append(LINKFLAGS = ["/SUBSYSTEM:WINDOWS"]) myenv.Append(LIBS = "qtmain") if myenv.get("HAVE_SCHANNEL", 0) : myenv.Append(LIBS = "Cryptui") myenv.Append(CPPDEFINES = "HAVE_SCHANNEL") if env["debug"] and not env["optimize"]: myenv.Append(LINKFLAGS = ["/NODEFAULTLIB:msvcrt"]) myenv.WriteVal("DefaultTheme.qrc", myenv.Value(generateDefaultTheme(myenv.Dir("#/Swift/resources/themes/Default")))) sources = [ "main.cpp", "QtAboutWidget.cpp", "QtSpellCheckerWindow.cpp", "QtAvatarWidget.cpp", "QtUIFactory.cpp", "QtChatWindowFactory.cpp", "QtClickableLabel.cpp", "QtLoginWindow.cpp", "QtMainWindow.cpp", "QtProfileWindow.cpp", "QtBlockListEditorWindow.cpp", "QtNameWidget.cpp", "QtSettingsProvider.cpp", "QtStatusWidget.cpp", "QtScaledAvatarCache.cpp", "QtSwift.cpp", "QtURIHandler.cpp", "QtChatWindow.cpp", "QtChatView.cpp", "QtWebKitChatView.cpp", "QtPlainChatView.cpp", "QtChatTheme.cpp", "QtChatTabs.cpp", "QtSoundPlayer.cpp", "QtSystemTray.cpp", "QtCachedImageScaler.cpp", "QtTabbable.cpp", "QtTabWidget.cpp", "QtTextEdit.cpp", "QtXMLConsoleWidget.cpp", "QtHistoryWindow.cpp", "QtFileTransferListWidget.cpp", "QtFileTransferListItemModel.cpp", "QtAdHocCommandWindow.cpp", "QtAdHocCommandWithJIDWindow.cpp", "QtUtilities.cpp", "QtBookmarkDetailWindow.cpp", "QtAddBookmarkWindow.cpp", "QtEditBookmarkWindow.cpp", "QtContactEditWindow.cpp", "QtContactEditWidget.cpp", "QtSingleWindow.cpp", - "QtHighlightEditorWidget.cpp", - "QtHighlightRulesItemModel.cpp", - "QtHighlightRuleWidget.cpp", + "QtHighlightEditor.cpp", "QtColorToolButton.cpp", "QtClosableLineEdit.cpp", "ChatSnippet.cpp", "MessageSnippet.cpp", "SystemMessageSnippet.cpp", "QtElidingLabel.cpp", "QtFormWidget.cpp", "QtFormResultItemModel.cpp", "QtLineEdit.cpp", "QtJoinMUCWindow.cpp", "QtConnectionSettingsWindow.cpp", "Roster/RosterModel.cpp", "Roster/QtTreeWidget.cpp", # "Roster/QtTreeWidgetItem.cpp", "Roster/RosterDelegate.cpp", "Roster/GroupItemDelegate.cpp", "Roster/DelegateCommons.cpp", "Roster/QtFilterWidget.cpp", "Roster/QtRosterWidget.cpp", "Roster/QtOccupantListWidget.cpp", "Roster/RosterTooltip.cpp", "EventViewer/EventModel.cpp", "EventViewer/EventDelegate.cpp", "EventViewer/TwoLineDelegate.cpp", "EventViewer/QtEventWindow.cpp", "EventViewer/QtEvent.cpp", "ChatList/QtChatListWindow.cpp", "ChatList/ChatListModel.cpp", "ChatList/ChatListDelegate.cpp", "ChatList/ChatListMUCItem.cpp", "ChatList/ChatListRecentItem.cpp", "ChatList/ChatListWhiteboardItem.cpp", "MUCSearch/QtMUCSearchWindow.cpp", "MUCSearch/MUCSearchModel.cpp", "MUCSearch/MUCSearchRoomItem.cpp", "MUCSearch/MUCSearchEmptyItem.cpp", "MUCSearch/MUCSearchDelegate.cpp", "UserSearch/ContactListDelegate.cpp", "UserSearch/ContactListModel.cpp", "UserSearch/QtContactListWidget.cpp", "UserSearch/QtSuggestingJIDInput.cpp", "UserSearch/QtUserSearchFirstPage.cpp", "UserSearch/QtUserSearchFirstMultiJIDPage.cpp", "UserSearch/QtUserSearchFieldsPage.cpp", "UserSearch/QtUserSearchResultsPage.cpp", "UserSearch/QtUserSearchDetailsPage.cpp", "UserSearch/QtUserSearchWindow.cpp", "UserSearch/UserSearchModel.cpp", "UserSearch/UserSearchDelegate.cpp", "Whiteboard/FreehandLineItem.cpp", "Whiteboard/GView.cpp", "Whiteboard/TextDialog.cpp", "Whiteboard/QtWhiteboardWindow.cpp", "Whiteboard/ColorWidget.cpp", "QtSubscriptionRequestWindow.cpp", "QtRosterHeader.cpp", "QtWebView.cpp", "qrc_DefaultTheme.cc", "qrc_Swift.cc", "QtChatWindowJSBridge.cpp", "QtMUCConfigurationWindow.cpp", "QtAffiliationEditor.cpp", "QtUISettingConstants.cpp", "QtURLValidator.cpp", "QtResourceHelper.cpp" ] # QtVCardWidget sources.extend([ "QtVCardWidget/QtCloseButton.cpp", "QtVCardWidget/QtRemovableItemDelegate.cpp", "QtVCardWidget/QtResizableLineEdit.cpp", "QtVCardWidget/QtTagComboBox.cpp", "QtVCardWidget/QtVCardHomeWork.cpp", "QtVCardWidget/QtVCardAddressField.cpp", "QtVCardWidget/QtVCardAddressLabelField.cpp", "QtVCardWidget/QtVCardBirthdayField.cpp", "QtVCardWidget/QtVCardDescriptionField.cpp", "QtVCardWidget/QtVCardInternetEMailField.cpp", "QtVCardWidget/QtVCardJIDField.cpp", "QtVCardWidget/QtVCardOrganizationField.cpp", "QtVCardWidget/QtVCardPhotoAndNameFields.cpp", "QtVCardWidget/QtVCardRoleField.cpp", "QtVCardWidget/QtVCardTelephoneField.cpp", "QtVCardWidget/QtVCardTitleField.cpp", "QtVCardWidget/QtVCardURLField.cpp", "QtVCardWidget/QtVCardGeneralField.cpp", "QtVCardWidget/QtVCardWidget.cpp" ]) myenv.Uic4("QtVCardWidget/QtVCardPhotoAndNameFields.ui") myenv.Uic4("QtVCardWidget/QtVCardWidget.ui") myenv.Uic4("QtProfileWindow.ui") # Determine the version myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift") if env["PLATFORM"] == "win32" : swift_windows_version = Version.convertToWindowsVersion(myenv["SWIFT_VERSION"]) myenv["SWIFT_VERSION_MAJOR"] = swift_windows_version[0] myenv["SWIFT_VERSION_MINOR"] = swift_windows_version[1] myenv["SWIFT_VERSION_PATCH"] = swift_windows_version[2] if env["PLATFORM"] == "win32" : res_env = myenv.Clone() res_env.Append(CPPDEFINES = [ ("SWIFT_COPYRIGHT_YEAR", "\"\\\"2010-%s\\\"\"" % str(time.localtime()[0])), ("SWIFT_VERSION_MAJOR", "${SWIFT_VERSION_MAJOR}"), ("SWIFT_VERSION_MINOR", "${SWIFT_VERSION_MINOR}"), ("SWIFT_VERSION_PATCH", "${SWIFT_VERSION_PATCH}"), ]) res = res_env.RES("#/Swift/resources/Windows/Swift.rc") # For some reason, SCons isn't picking up the dependency correctly # Adding it explicitly until i figure out why myenv.Depends(res, "../Controllers/BuildVersion.h") sources += [ "WinUIHelpers.cpp", "CAPICertificateSelector.cpp", "WindowsNotifier.cpp", "#/Swift/resources/Windows/Swift.res" ] if env["PLATFORM"] == "posix" : sources += [ "FreeDesktopNotifier.cpp", "QtDBUSURIHandler.cpp", ] if env["PLATFORM"] == "darwin" : sources += ["CocoaApplicationActivateHelper.mm"] sources += ["CocoaUIHelpers.mm"] if env["PLATFORM"] == "darwin" or env["PLATFORM"] == "win32" : swiftProgram = myenv.Program("Swift", sources) else : sources += ["QtCertificateViewerDialog.cpp"]; myenv.Uic4("QtCertificateViewerDialog.ui"); swiftProgram = myenv.Program("swift-im", sources) if env["PLATFORM"] != "darwin" and env["PLATFORM"] != "win32" : openURIProgram = myenv.Program("swift-open-uri", "swift-open-uri.cpp") else : openURIProgram = [] myenv.Uic4("MUCSearch/QtMUCSearchWindow.ui") myenv.Uic4("UserSearch/QtUserSearchWizard.ui") myenv.Uic4("UserSearch/QtUserSearchFirstPage.ui") myenv.Uic4("UserSearch/QtUserSearchFirstMultiJIDPage.ui") myenv.Uic4("UserSearch/QtUserSearchFieldsPage.ui") myenv.Uic4("UserSearch/QtUserSearchResultsPage.ui") myenv.Uic4("QtBookmarkDetailWindow.ui") myenv.Uic4("QtAffiliationEditor.ui") myenv.Uic4("QtJoinMUCWindow.ui") myenv.Uic4("QtHistoryWindow.ui") myenv.Uic4("QtConnectionSettings.ui") -myenv.Uic4("QtHighlightRuleWidget.ui") -myenv.Uic4("QtHighlightEditorWidget.ui") +myenv.Uic4("QtHighlightEditor.ui") myenv.Uic4("QtBlockListEditorWindow.ui") myenv.Uic4("QtSpellCheckerWindow.ui") myenv.Qrc("DefaultTheme.qrc") myenv.Qrc("Swift.qrc") # Resources commonResources = { "": ["#/Swift/resources/sounds"] } myenv["TEXTFILESUFFIX"] = "" myenv.MyTextfile(target = "COPYING", source = [myenv.File("../../COPYING.gpl"), myenv.File("../../COPYING.thirdparty")], LINESEPARATOR = "\n\n========\n\n\n") ################################################################################ # Translation ################################################################################ # Collect available languages translation_languages = [] for file in os.listdir(Dir("#/Swift/Translations").abspath) : if file.startswith("swift_") and file.endswith(".ts") : translation_languages.append(file[6:-3]) # Generate translation modules translation_sources = [env.File("#/Swift/Translations/swift.ts").abspath] translation_modules = [] for lang in translation_languages : translation_resource = "#/Swift/resources/translations/swift_" + lang + ".qm" translation_source = "#/Swift/Translations/swift_" + lang + ".ts" translation_sources.append(env.File(translation_source).abspath) translation_modules.append(env.File(translation_resource).abspath) myenv.Qm(translation_resource, translation_source) commonResources["translations"] = commonResources.get("translations", []) + [translation_resource] # LUpdate translation (if requested) if ARGUMENTS.get("update_translations", False) : myenv.Precious(translation_sources) remove_obsolete_option = "" if ARGUMENTS.get("remove_obsolete_translations", False) : remove_obsolete_option = " -no-obsolete" for translation_source in filter(lambda x: not x.endswith("_en.ts"), translation_sources) : t = myenv.Command([translation_source], [], [myenv.Action("$QT4_LUPDATE -I " + env.Dir("#").abspath + remove_obsolete_option + " -silent -codecfortr utf-8 -recursive Swift -ts " + translation_source, cmdstr = "$QT4_LUPDATECOMSTR")]) myenv.AlwaysBuild(t) # NSIS installation script if env["PLATFORM"] == "win32" : nsis_translation_install_script = "" nsis_translation_uninstall_script = "" for lang in translation_languages : nsis_translation_install_script += "File \"..\\..\\QtUI\\Swift\\translations\\swift_" + lang + ".qm\"\n" nsis_translation_uninstall_script += "delete $INSTDIR\\translations\\swift_" + lang + ".qm\n" myenv.WriteVal("../Packaging/nsis/translations-install.nsh", myenv.Value(nsis_translation_install_script)) myenv.WriteVal("../Packaging/nsis/translations-uninstall.nsh", myenv.Value(nsis_translation_uninstall_script)) ################################################################################ if env["PLATFORM"] == "darwin" : frameworks = [] if env["HAVE_SPARKLE"] : frameworks.append(env["SPARKLE_FRAMEWORK"]) if env["HAVE_GROWL"] : frameworks.append(env["GROWL_FRAMEWORK"]) commonResources[""] = commonResources.get("", []) + ["#/Swift/resources/MacOSX/Swift.icns"] app = myenv.AppBundle("Swift", version = myenv["SWIFT_VERSION"], resources = commonResources, frameworks = frameworks, handlesXMPPURIs = True) if env["DIST"] : myenv.Command(["#/Packages/Swift/Swift-${SWIFT_VERSION}.dmg"], [app], ["Swift/Packaging/MacOSX/package.sh " + app.path + " Swift/Packaging/MacOSX/Swift.dmg.gz $TARGET $QTDIR"]) dsym = myenv.Command(["Swift-${SWIFT_VERSION}.dSYM"], ["Swift"], ["dsymutil -o ${TARGET} ${SOURCE}"]) myenv.Command(["#/Packages/Swift/Swift-${SWIFT_VERSION}.dSYM.zip"], dsym, ["cd ${SOURCE.dir} && zip -r ${TARGET.abspath} ${SOURCE.name}"]) if env.get("SWIFT_INSTALLDIR", "") : env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "bin"), swiftProgram + openURIProgram) env.InstallAs(os.path.join(env["SWIFT_INSTALLDIR"], "share", "pixmaps", "swift.xpm"), "#/Swift/resources/logo/logo-icon-32.xpm") icons_path = os.path.join(env["SWIFT_INSTALLDIR"], "share", "icons", "hicolor") env.InstallAs(os.path.join(icons_path, "32x32", "apps", "swift.xpm"), "#/Swift/resources/logo/logo-icon-32.xpm") env.InstallAs(os.path.join(icons_path, "scalable", "apps", "swift.svg"), "#/Swift/resources/logo/logo-icon.svg") for i in ["16", "22", "24", "64", "128"] : env.InstallAs(os.path.join(icons_path, i + "x" + i, "apps", "swift.png"), "#/Swift/resources/logo/logo-icon-" + i + ".png") env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "share", "applications"), "#/Swift/resources/swift.desktop") for dir, resource in commonResources.items() : env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "share", "swift", dir), resource) if env["PLATFORM"] == "win32" : if env["DIST"] or ARGUMENTS.get("dump_trace") : commonResources[""] = commonResources.get("", []) + [ #os.path.join(env["OPENSSL_DIR"], "bin", "ssleay32.dll"), #os.path.join(env["OPENSSL_DIR"], "bin", "libeay32.dll"), "#/Swift/resources/images", ] if env["SWIFTEN_DLL"] : commonResources[""] = commonResources.get("", []) + ["#/Swiften/${SWIFTEN_LIBRARY_FILE}"] qtplugins = {} qtplugins["imageformats"] = ["gif", "ico", "jpeg", "mng", "svg", "tiff"] qtlibs = ["QtCore", "QtGui", "QtNetwork", "QtWebKit", "QtXMLPatterns"] if qt_version == '4' : qtlibs.append("phonon") qtlibs = [lib + '4' for lib in qtlibs] else : qtlibs += ['QtQuick', 'QtQml', 'QtPositioning', 'QtMultimedia', 'QtSql', 'QtSensors', 'QtWidgets', 'QtWebKitWidgets', 'QtMultimediaWidgets', 'QtOpenGL', 'QtPrintSupport'] qtlibs = [lib.replace('Qt', 'Qt5') for lib in qtlibs] qtlibs += ['icuin51', 'icuuc51', 'icudt51', 'libGLESv2', 'libEGL'] qtplugins["platforms"] = ['windows'] qtplugins["accessible"] = ["taccessiblewidgets"] windowsBundleFiles = myenv.WindowsBundle("Swift", resources = commonResources, qtplugins = qtplugins, qtlibs = qtlibs, qtversion = qt_version) if env["DIST"] : #myenv.Append(NSIS_OPTIONS = [ # "/DmsvccRedistributableDir=\"" + env["vcredist"] + "\"", # "/DbuildVersion=" + myenv["SWIFT_VERSION"] # ]) #myenv.Nsis("../Packaging/nsis/swift.nsi") if env["SCONS_STAGE"] == "build" and env.get("wix_bindir", None): def convertToRTF(env, target, source) : infile = open(source[0].abspath, 'r') outfile = open(target[0].abspath, 'w') outfile.write('{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\fs16\\f0\\pard\n') for line in infile: for char in line.decode("utf-8") : if ord(char) > 127 : # FIXME: This is incorrect, because it only works for latin1. # The correct way is \u<decimal utf16 point>? , but this is more # work outfile.write("\\'%X" % ord(char)) else : outfile.write(char) outfile.write('\\par ') outfile.write('}') outfile.close() infile.close() copying = env.Command(["Swift/COPYING.rtf"], ["COPYING"], convertToRTF) wixvariables = { 'VCCRTFile': env["vcredist"], 'Version': str(myenv["SWIFT_VERSION_MAJOR"]) + "." + str(myenv["SWIFT_VERSION_MINOR"]) + "." + str(myenv["SWIFT_VERSION_PATCH"]) } wixincludecontent = "<Include>" for key in wixvariables: wixincludecontent += "<?define %s = \"%s\" ?>" % (key, wixvariables[key]) wixincludecontent += "</Include>" myenv.WriteVal("..\\Packaging\\Wix\\variables.wxs", env.Value(wixincludecontent)) myenv["WIX_SOURCE_OBJECT_DIR"] = "Swift\\QtUI\\Swift" myenv.WiX_Heat('..\\Packaging\\WiX\\gen_files.wxs', windowsBundleFiles + copying) myenv.WiX_Candle('..\\Packaging\\WiX\\Swift.wixobj', '..\\Packaging\\WiX\\Swift.wxs') myenv.WiX_Candle('..\\Packaging\\WiX\\gen_files.wixobj', '..\\Packaging\\WiX\\gen_files.wxs') myenv.WiX_Light('#/Packages/Swift/Swift-' + myenv["SWIFT_VERSION"] + '.msi', ['..\\Packaging\\WiX\\gen_files.wixobj','..\\Packaging\\WiX\\Swift.wixobj']) if myenv["debug"] : myenv.InstallAs('#/Packages/Swift/Swift-' + myenv["SWIFT_VERSION"] + '.pdb', "Swift.pdb") |
Swift