[prev in list] [next in list] [prev in thread] [next in thread]
List: kopete-devel
Subject: [kopete-devel] History Import extension
From: Timo_Schlüßler <timo () schluessler ! org>
Date: 2008-12-03 16:03:22
Message-ID: 4936ADCA.7010205 () schluessler ! org
[Download RAW message or body]
Hello,
attached is a patch and the two files which add the ability to import
logs from pidgin to kopete. The source-code files belong to
kdenetwork/kopete/plugins/history.
I changed HistoryLogger slightly so that appendMessage() uses the
timestamp from the message that is to be imported but not
QDate::currentDate().
I don't know what happens if you want to import multiuser chatlogs
because i haven't got any ... i would be glad if you can check that.
For me it works just fine, but the logs from pidgin are very unsteady.
anyway i hope it works for all pidgin logs.
thanks for testing,
timo
["historyimport.cpp" (text/plain)]
/*
historyimport.cpp
Copyright (c) 2008 by Timo Schluessler
Kopete (c) 2008 by the Kopete developers <kopete-devel@kde.org>
*************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
*************************************************************************
*/
#include "historyimport.h"
#include "historylogger.h"
#include <kdebug.h>
#include <kopetecontactlist.h>
#include <kopetemetacontact.h>
#include <kopeteprotocol.h>
#include <kopeteaccount.h>
HistoryImport::HistoryImport(QWidget *parent)
: KDialog(parent)
{
// set dialog settings
setButtons(KDialog::Ok | KDialog::Details | KDialog::Cancel);
setWindowTitle(KDialog::makeStandardCaption("Import History"));
setButtonText(KDialog::Ok, "Import listed logs");
// create widgets
QWidget *w = new QWidget(this);
QGridLayout *l = new QGridLayout(w);
display = new QTextEdit(w);
display->setReadOnly(true);
treeView = new QTreeView(w);
QPushButton *fromPidgin = new QPushButton("Get history from &Pidgin", w);
l->addWidget(treeView, 0, 0, 1, 3);
l->addWidget(display, 0, 4, 1, 10);
l->addWidget(fromPidgin, 1, 0);
setMainWidget(w);
// create details widget
QWidget *details = new QWidget(w);
QVBoxLayout *dL = new QVBoxLayout(w);
QTextEdit *detailsEdit = new QTextEdit(details);
detailsEdit->setReadOnly(true);
selectByHand = new QCheckBox(tr("Select log directory by hand"), details);
dL->addWidget(selectByHand);
dL->addWidget(detailsEdit);
details->setLayout(dL);
setDetailsWidget(details);
detailsCursor = QTextCursor(detailsEdit->document());
// create model for treeView
QStandardItemModel *model = new QStandardItemModel(treeView);
treeView->setModel(model);
model->setHorizontalHeaderLabels(QStringList("Parsed History"));
// connect everything
connect(treeView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(itemClicked(const \
QModelIndex &))); connect(fromPidgin, SIGNAL(clicked()), this, \
SLOT(importPidgin())); connect(this, SIGNAL(okClicked()), this, SLOT(save()));
// define variables
amount = 0;
cancel = false;
timeFormats << "(MM/dd/yyyy hh:mm:ss)" << "(MM/dd/yyyy hh:mm:ss AP)" << "(MM/dd/yy \
hh:mm:ss)" << "(MM/dd/yy hh:mm:ss AP)" << "(dd.MM.yyyy hh:mm:ss)" << "(dd.MM.yyyy \
hh:mm:ss AP)" << "(dd.MM.yy hh:mm:ss)" << "(dd.MM.yyyy hh:mm:ss AP)" << "(dd/MM/yyyy \
hh:mm:ss)" << "(dd/MM/yyyy hh:mm:ss AP)" << "(dd/MM/yy hh:mm:ss)" << "(dd/MM/yy \
hh:mm:ss AP)";
show();
}
HistoryImport::~HistoryImport(void)
{
}
void HistoryImport::save(void)
{
QProgressDialog progress("Saving logs to disk ...", "Abort Saving", 0, amount, \
this); progress.setWindowTitle("Saving");
Log log;
foreach (log, logs) {
HistoryLogger logger(log.other, this);
Message message;
foreach (message, log.messages) {
Kopete::Message kMessage;
if (message.incoming) {
kMessage = Kopete::Message(log.other, log.me);
kMessage.setDirection(Kopete::Message::Inbound);
} else {
kMessage = Kopete::Message(log.me, log.other);
kMessage.setDirection(Kopete::Message::Outbound);
}
kMessage.setPlainBody(message.text);
kMessage.setTimestamp(message.timestamp);
logger.appendMessage(kMessage, log.other);
progress.setValue(progress.value()+1);
qApp->processEvents();
if (progress.wasCanceled()) {
cancel = true;
break;
}
}
if (cancel)
break;
}
}
void HistoryImport::displayLog(struct Log *log)
{
Message message;
QStandardItem *root = \
dynamic_cast<QStandardItemModel*>(treeView->model())->invisibleRootItem(), *item;
foreach(message, log->messages) {
amount++; // for QProgressDialog in save()
item = findItem(log->other->protocol()->pluginId().append(" \
(").append(log->other->account()->accountId()).append(")"), root); item = \
findItem(log->other->nickName(), item); item = \
findItem(message.timestamp.toString("yyyy-MM-dd"), item);
if (!item->data(Qt::UserRole).isValid())
item->setData((int)logs.indexOf(*log), Qt::UserRole);
}
}
QStandardItem * HistoryImport::findItem(QString text, QStandardItem *parent)
{
int i;
bool found = false;
QStandardItem *child = 0L;
for (i=0; i < parent->rowCount(); i++) {
child = parent->child(i, 0);
if (child->data(Qt::DisplayRole) == text) {
found = true;
break;
}
}
if (!found) {
child = new QStandardItem(text);
parent->appendRow(child);
}
return child;
}
void HistoryImport::itemClicked(const QModelIndex & index)
{
QVariant id = index.data(Qt::UserRole);
if (id.canConvert<int>()) {
Log log = logs.at(id.toInt());
display->document()->clear();
QTextCursor cursor(display->document());
Message message;
QDate date = QDate::fromString(index.data(Qt::DisplayRole).toString(), \
"yyyy-MM-dd"); foreach (message, log.messages) {
if (date != message.timestamp.date())
continue;
cursor.insertText(message.timestamp.toString("hh:mm:ss "));
if (message.incoming)
cursor.insertText(log.other->nickName().append(": "));
else
cursor.insertText(log.me->nickName().append(": "));
cursor.insertText(message.text);
cursor.insertBlock();
}
}
}
int HistoryImport::countLogs(QDir dir, int depth)
{
int res = 0;
QStack<int> pos;
QStringList files;
pos.push(0);
depth++;
forever {
files = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
if (pos.size() == depth) {
res += dir.entryList(QDir::Files).size();
}
if (files.isEmpty() || files.size() <= pos.top() || pos.size() == depth) {
dir.cdUp();
pos.pop();
if (pos.isEmpty())
break;
pos.top()++;
}
else if (pos.size() != depth) {
dir.cd(files.at(pos.top()));
pos.push(0);
}
}
return res;
}
void HistoryImport::importPidgin()
{
QDir logDir = QDir::homePath();
if (selectByHand->isChecked() || !logDir.cd(".purple/logs"))
logDir = QFileDialog::getExistingDirectory(mainWidget(), tr("Select log \
directory"), QDir::homePath());
int total = countLogs(logDir, 3);
QProgressDialog progress("Parsing history from pidgin ...", "Abort parsing", 0, \
total, mainWidget()); progress.setWindowTitle("Parsing history");
progress.show();
cancel = false;
QString protocolFolder;
foreach (protocolFolder, logDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
logDir.cd(protocolFolder);
QString accountFolder;
foreach (accountFolder, logDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
logDir.cd(accountFolder);
Kopete::ContactList * cList = Kopete::ContactList::self();
QList<Kopete::Contact *> meList = cList->myself()->contacts();
Kopete::Contact *me;
bool found = false;
foreach (me, meList) {
qDebug() << me->protocol()->pluginId() << protocolFolder << \
me->account()->accountId() << accountFolder; if \
(me->protocol()->pluginId().contains(protocolFolder, \
Qt::CaseInsensitive) &&
me->account()->accountId().contains(accountFolder, Qt::CaseInsensitive)) {
found = true;
break;
}
}
if (!found) {
detailsCursor.insertText(QString("WARNING: Can't find matching account for %1 \
(%2)!\n").arg(accountFolder).arg(protocolFolder)); qDebug() << logDir.path();
logDir.cdUp();
qDebug() << logDir.path();
continue;
}
QString chatPartner;
foreach (chatPartner, logDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
logDir.cd(chatPartner);
Kopete::Contact *other = cList->findContact(me->protocol()->pluginId(), \
me->account()->accountId(), chatPartner); struct Log log;
if (!other) {
detailsCursor.insertText(QString("WARNING: Can't find %1 (%2) in your contact \
list. Found logs will not be imported!\n").arg(chatPartner).arg(protocolFolder)); \
logDir.cdUp(); continue;
}
else {
log.me = me;
log.other = other;
}
QString logFile;
QStringList filter;
filter << "*.html" << "*.txt";
foreach(logFile, logDir.entryList(filter, QDir::Files)) {
QFile file(logDir.filePath(logFile));
if (!file.open(QIODevice::ReadOnly)) {
detailsCursor.insertText(QString("WARNING: Can't open file %1. \
Skipping.\n").arg(logDir.filePath(logFile))); continue;
}
if (logFile.endsWith(".html"))
parsePidginXml(file, &log, QDate::fromString(logFile.left(10), "yyyy-MM-dd"));
else if (logFile.endsWith(".txt"))
parsePidginTxt(file, &log, QDate::fromString(logFile.left(10), "yyyy-MM-dd"));
file.close();
progress.setValue(progress.value()+1);
qApp->processEvents();
if (cancel || progress.wasCanceled()) {
cancel = true;
break;
}
}
logs.append(log);
displayLog(&log);
if (cancel)
break;
logDir.cdUp();
}
if (cancel)
break;
logDir.cdUp();
}
if (cancel)
break;
logDir.cdUp();
}
}
bool HistoryImport::isNickIncoming(QString nick, struct Log *log)
{
bool incoming;
if (nick == log->me->nickName())
incoming = false;
else if (nick == log->other->nickName())
incoming = true;
else if (knownNicks.contains(nick))
incoming = knownNicks.value(nick);
else {
int r = QMessageBox::question(NULL,
tr("Can't map nickname to account"),
tr("Did you use \"%1\" as nickname in history?").arg(nick),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Abort);
if (r == QMessageBox::Yes) {
knownNicks.insert(nick, true);
incoming = true;
}
else if (r == QMessageBox::No) {
knownNicks.insert(nick, false);
incoming = false;
}
else {
cancel = true;
return false;
}
}
return incoming;
}
QDateTime HistoryImport::extractTime(QString string, QDate ref)
{
QDateTime dateTime;
QTime time;
// try some formats used by pidgin
if ((time = QTime::fromString(string, "(hh:mm:ss)")) .isValid());
else if ((time = QTime::fromString(string, "(hh:mm:ss AP)")) .isValid());
else {
QString format;
foreach (format, timeFormats) {
if ((dateTime = QDateTime::fromString(string, format)).isValid())
break;
}
}
// check if the century in dateTime is equal to that of our date reference
if (dateTime.isValid()) {
int diff = ref.year() - dateTime.date().year();
dateTime = dateTime.addYears(diff - (diff % 100));
}
// if string contains only a time we use ref as date
if (time.isValid())
dateTime = QDateTime(ref, time);
// inform the user about the date problems
if (!dateTime.isValid())
detailsCursor.insertText(tr("WARNING: can't parse date \"%1\". You may want to edit \
the file containing this date manually. (example for recognized date strings: \
\"05/31/2008 15:24:30\")\n").arg(string, dateTime.toString("yyyy-MM-dd hh:mm:ss")));
return dateTime;
}
void HistoryImport::parsePidginTxt(QFile & file, struct Log *log, QDate date)
{
QByteArray line;
QTime time;
QDateTime dateTime;
QString messageText, nick;
bool incoming = false; // =false to make the compiler not complain
while (!file.atEnd()) {
line = file.readLine();
if (line[0] == '(') {
if (!messageText.isEmpty()) {
// messageText contains an unwished newline at the end
if (messageText.endsWith("\n"))
messageText.remove(-1, 1);
struct Message message;
message.incoming = incoming;
message.text = messageText;
message.timestamp = dateTime;
log->messages.append(message);
messageText.clear();
}
int endTime = line.indexOf(")")+1;
dateTime = extractTime(line.left(endTime), date);
int nickEnd = QRegExp("\\s").indexIn(line, endTime + 1);
// TODO what if a nickname consists of two words? is this possible?
// the following while can't be used because in status logs there is no : after \
the nickname :( //while (line[nickEnd-1] != ':')
// nickEnd = QRegExp("\\").indexIn(line, nickEnd);
if (line[nickEnd -1] != ':') // this line is a status message
continue;
nick = line.mid(endTime+1, nickEnd - endTime - 2); // -2 to delete the colon
incoming = isNickIncoming(nick, log);
if (cancel)
return;
messageText = line.mid(nickEnd + 1);
}
else if (line[0] == ' ') {
// an already started message is continued in this line
int start = QRegExp("\\S").indexIn(line);
messageText.append("\n" + line.mid(start));
}
}
if (!messageText.isEmpty()) {
struct Message message;
message.incoming = incoming;
message.text = messageText;
message.timestamp = dateTime;
log->messages.append(message);
messageText.clear();
}
}
void HistoryImport::parsePidginXml(QFile & file, struct Log * log, QDate date)
{
bool incoming = false, inMessage = false, textComes = false;
QString messageText, status;
QTime time;
QDateTime dateTime;
// unfortunately pidgin doesn't write <... /> for the <meta> tag
QByteArray data = file.readAll();
if (data.contains("<meta")) {
int metaEnd = data.indexOf(">", data.indexOf("<meta"));
if (data.at(metaEnd-1) != '/')
data.insert(metaEnd, "/");
}
QXmlStreamReader reader(data);
while (!reader.atEnd()) {
reader.readNext();
if (reader.isStartElement() && reader.name() == "font" && \
!reader.attributes().value("color").isEmpty()) { if \
(reader.attributes().value("color") == "#A82F2F") incoming = true;
else
incoming = false;
while (reader.readNext() != QXmlStreamReader::Characters);
dateTime = extractTime(reader.text().toString(), date);
inMessage = true;
}
if (inMessage && reader.isStartElement() && reader.name() == "b") {
reader.readNext();
status = reader.text().toString();
textComes = true;
}
else if (textComes && reader.isCharacters()) {
messageText += reader.text().toString();
}
else if (reader.isStartElement() && reader.name() == "br" && \
!messageText.isEmpty()) { messageText.remove(0, 1); // remove the leading blank
struct Message message;
message.incoming = incoming;
message.text = messageText;
message.timestamp = dateTime;
log->messages.append(message);
messageText.clear();
textComes = false;
inMessage = false;
}
}
if (reader.hasError()) {
// we ignore error 4: premature end of document
if (reader.error() != 4) {
int i, pos = 0;
for (i=1;i<reader.lineNumber();i++)
pos = data.indexOf('\n', pos) + 1;
detailsCursor.insertText(tr("WARNING: XML parser error in %1 at line %2, character \
%3: %4"). arg(file.fileName()).arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
detailsCursor.insertBlock();
detailsCursor.insertText(tr("\t%1").arg(QString(data.mid(pos, data.indexOf('\n', \
pos) - pos)))); detailsCursor.insertBlock();
}
}
}
#include "historyimport.moc"
["historyimport.h" (text/plain)]
/*
historylogger.cpp
Copyright (c) 2008 by Timo Schluessler <timo@schluessler.org>
Kopete (c) 2008 by the Kopete developers <kopete-devel@kde.org>
*************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
*************************************************************************
*/
#ifndef HISTORYIMPORT_H
#define HISTORYIMPORT_H
#include <QtGui>
#include <kdialog.h>
#include "kopeteglobal.h"
#include "kopetecontact.h"
#include "kopetemessage.h"
/**
* @author Timo Schluessler <timo@schluessler.org>
*/
class HistoryImport : public KDialog
{
Q_OBJECT
public:
HistoryImport(QWidget *parent);
~HistoryImport();
private:
/**
* Used for internal storage of a message.
*/
struct Message {
bool incoming;
QString text;
QDateTime timestamp;
};
/**
* Holds the messages sent and received between two specified contacts.
*/
struct Log {
Kopete::Contact *me;
Kopete::Contact *other;
QList<Message> messages;
/**
* Comparison between the Message lists is not neccessary because we need this operator
* only in displayLog() to get the index of this log in logs.
*/
bool operator==(const struct Log & cmp) {
if (cmp.me == me && cmp.other == other/* && other.messages == messages*/)
return true;
else
return false;
}
};
/**
* Parses @param file and appends the found messages to @param log.
*/
void parsePidginXml(QFile & file, struct Log *log, QDate date);
/**
* Parses @param file and appends the found messages to @param log.
*/
void parsePidginTxt(QFile & file, struct Log *log, QDate date);
/**
* Inserts @param log into treeView and prepares to display it when clicking on it.
*/
void displayLog(struct Log *log);
/**
* Looks up if an item with @param text exists already as child of @param parent.
* If not, it creates one.
*/
QStandardItem * findItem(QString text, QStandardItem *parent);
/**
* @return the number of files found in @param depth th subdir of @param dir.
*/
int countLogs(QDir dir, int depth);
/**
* Checks if nickname @param nick can be mapped to either @param log ->me or @param log ->other.
* If not it asks the user if it is his nickname or someone elses.
*/
bool isNickIncoming(QString nick, struct Log *log);
/**
* Trys to extract the time from @param string using formats specified in timeFormats.
* @param ref is used when @param string doesn't contain a date or to adjust a found date.
* @param ref is taken from the filename of the log.
*/
QDateTime extractTime(QString string, QDate ref);
QStringList timeFormats;
QTreeView *treeView;
QTextEdit *display;
/**
* Used for adding details to the details widget.
*/
QTextCursor detailsCursor;
/**
* Enables/disables auto search for log dirs.
*/
QCheckBox *selectByHand;
/**
* Contains all already parsed logs.
*/
QList<Log> logs;
/**
* Used for mapping nickname to account. See isNickIncoming().
*/
QHash<QString, bool> knownNicks;
int amount;
bool cancel;
private slots:
/**
* Starts parsing history from pidgin.
* Default path is ~/.purple/logs.
*/
void importPidgin(void);
void save(void);
void itemClicked(const QModelIndex & index);
};
#endif
["kopete-history-import.patch" (text/plain)]
Index: kopete/plugins/history/historylogger.cpp
===================================================================
--- kopete/plugins/history/historylogger.cpp (revision 890701)
+++ kopete/plugins/history/historylogger.cpp (working copy)
@@ -117,16 +117,16 @@
}
-QDomDocument HistoryLogger::getDocument(const Kopete::Contact *c, unsigned int month \
, bool canLoad , bool* contain) +QDomDocument HistoryLogger::getDocument(const \
Kopete::Contact *c, unsigned int month , const QDate date , bool canLoad , bool* \
contain) {
- if(m_realMonth!=QDate::currentDate().month())
+ if(m_realMonth!=date.month() /*QDate::currentDate().month()*/)
{ //We changed month, our index is not correct anymore, clean memory.
// or we will see what i called "the 31 midnight bug"(TM) :-) -Olivier
m_documents.clear();
m_cachedMonth=-1;
m_currentMonth++; //Not usre it's ok, but should work;
m_oldMonth++; // idem
- m_realMonth=QDate::currentDate().month();
+ m_realMonth=date.month();//QDate::currentDate().month();
}
if(!m_metaContact)
@@ -149,7 +149,7 @@
return documents[month];
- QDomDocument doc = getDocument(c, QDate::currentDate().addMonths(0-month), \
canLoad, contain); + QDomDocument doc = getDocument(c, date.addMonths(0-month) \
/*QDate::currentDate().addMonths(0-month)*/, canLoad, contain);
documents.insert(month, doc);
m_documents[c]=documents;
@@ -255,7 +255,9 @@
return;
}
- QDomDocument doc=getDocument(c,0);
+
+
+ QDomDocument doc=getDocument(c, 0, msg.timestamp().date());
QDomElement docElem = doc.documentElement();
if(docElem.isNull())
@@ -293,7 +295,7 @@
// On hight-traffic channel, saving can take lots of CPU. (because the file is big)
// So i wait a time proportional to the time needed to save..
- const QString filename=getFileName(c,QDate::currentDate());
+ const QString filename=getFileName(c, msg.timestamp().date());
if(!m_toSaveFileName.isEmpty() && m_toSaveFileName != filename)
{ //that mean the contact or the month has changed, save it now.
saveToDisk();
Index: kopete/plugins/history/historylogger.h
===================================================================
--- kopete/plugins/history/historylogger.h (revision 890701)
+++ kopete/plugins/history/historylogger.h (working copy)
@@ -20,6 +20,7 @@
#include <QObject>
#include <QList>
+#include <QDate>
#include <QtCore/QMap>
#include <QtXml/QDomDocument>
@@ -148,7 +149,7 @@
* Get the document, open it is @param canload is true, contain is set to false if \
the document
* is not already contained
*/
- QDomDocument getDocument(const Kopete::Contact *c, unsigned int month , bool \
canLoad=true , bool* contain=0L); + QDomDocument getDocument(const Kopete::Contact \
*c, unsigned int month , const QDate date = QDate::currentDate(), bool canLoad=true , \
bool* contain=0L);
QDomDocument getDocument(const Kopete::Contact *c, const QDate date, bool \
canLoad=true, bool* contain=0L);
Index: kopete/plugins/history/historypreferences.cpp
===================================================================
--- kopete/plugins/history/historypreferences.cpp (revision 890701)
+++ kopete/plugins/history/historypreferences.cpp (working copy)
@@ -19,6 +19,7 @@
#include "historypreferences.h"
#include "historyconfig.h"
#include "ui_historyprefsui.h"
+#include "historyimport.h"
#include <kgenericfactory.h>
@@ -51,6 +52,7 @@
this, SLOT(slotModified()));
connect(p->History_color, SIGNAL(changed(const QColor&)),
this, SLOT(slotModified()));
+ connect(p->importLogs, SIGNAL(clicked(void)), this, SLOT(importLogs(void)));
}
HistoryPreferences::~HistoryPreferences()
@@ -94,4 +96,10 @@
emit KCModule::changed(true);
}
+void HistoryPreferences::importLogs(void)
+{
+ HistoryImport importer(this);
+ importer.exec();
+}
+
#include "historypreferences.moc"
Index: kopete/plugins/history/historypreferences.h
===================================================================
--- kopete/plugins/history/historypreferences.h (revision 890701)
+++ kopete/plugins/history/historypreferences.h (working copy)
@@ -42,6 +42,7 @@
private slots:
void slotModified();
void slotShowPreviousChanged(bool);
+ void importLogs(void);
private:
Ui::HistoryPrefsUI *p;
Index: kopete/plugins/history/historyprefsui.ui
===================================================================
--- kopete/plugins/history/historyprefsui.ui (revision 890701)
+++ kopete/plugins/history/historyprefsui.ui (working copy)
@@ -14,42 +14,21 @@
<property name="spacing" >
<number>6</number>
</property>
- <property name="leftMargin" >
+ <property name="margin" >
<number>0</number>
</property>
- <property name="topMargin" >
- <number>0</number>
- </property>
- <property name="rightMargin" >
- <number>0</number>
- </property>
- <property name="bottomMargin" >
- <number>0</number>
- </property>
<item>
<widget class="QGroupBox" name="groupBox" >
<property name="title" >
<string>Chat History</string>
</property>
<layout class="QGridLayout" >
- <property name="leftMargin" >
+ <property name="margin" >
<number>9</number>
</property>
- <property name="topMargin" >
- <number>9</number>
- </property>
- <property name="rightMargin" >
- <number>9</number>
- </property>
- <property name="bottomMargin" >
- <number>9</number>
- </property>
- <property name="horizontalSpacing" >
+ <property name="spacing" >
<number>6</number>
</property>
- <property name="verticalSpacing" >
- <number>6</number>
- </property>
<item row="2" column="0" >
<spacer>
<property name="orientation" >
@@ -58,7 +37,7 @@
<property name="sizeType" >
<enum>QSizePolicy::Fixed</enum>
</property>
- <property name="sizeHint" >
+ <property name="sizeHint" stdset="0" >
<size>
<width>16</width>
<height>20</height>
@@ -164,6 +143,13 @@
</property>
</widget>
</item>
+ <item row="4" column="1" >
+ <widget class="QPushButton" name="importLogs" >
+ <property name="text" >
+ <string>Import History</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
@@ -175,7 +161,7 @@
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
- <property name="sizeHint" >
+ <property name="sizeHint" stdset="0" >
<size>
<width>341</width>
<height>16</height>
Index: kopete/plugins/history/CMakeLists.txt
===================================================================
--- kopete/plugins/history/CMakeLists.txt (revision 890701)
+++ kopete/plugins/history/CMakeLists.txt (working copy)
@@ -28,7 +28,7 @@
########### next target ###############
-set(kcm_kopete_history_PART_SRCS historypreferences.cpp )
+set(kcm_kopete_history_PART_SRCS historypreferences.cpp historyimport.cpp \
historylogger.cpp )
kde4_add_ui_files(kcm_kopete_history_PART_SRCS historyprefsui.ui )
_______________________________________________
kopete-devel mailing list
kopete-devel@kde.org
https://mail.kde.org/mailman/listinfo/kopete-devel
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic