diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:17:32 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:17:32 +0000 |
commit | e38d2351b83fa65c66ccde443777647ef5cb6cff (patch) | |
tree | 1897fc20e9f73a81c520a5b9f76f8ed042124883 /src/gui | |
download | tellico-e38d2351b83fa65c66ccde443777647ef5cb6cff.tar.gz tellico-e38d2351b83fa65c66ccde443777647ef5cb6cff.zip |
Added KDE3 version of Tellico
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/tellico@1097620 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/gui')
55 files changed, 5158 insertions, 0 deletions
diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am new file mode 100644 index 0000000..cb6f5e8 --- /dev/null +++ b/src/gui/Makefile.am @@ -0,0 +1,43 @@ +AM_CPPFLAGS = $(all_includes) + +noinst_LIBRARIES = libgui.a +libgui_a_SOURCES = combobox.cpp counteditem.cpp datewidget.cpp \ + tabcontrol.cpp kwidgetlister.cpp stringmapdialog.cpp listview.cpp richtextlabel.cpp \ + lineedit.cpp boolfieldwidget.cpp choicefieldwidget.cpp linefieldwidget.cpp \ + numberfieldwidget.cpp parafieldwidget.cpp urlfieldwidget.cpp tablefieldwidget.cpp \ + imagefieldwidget.cpp datefieldwidget.cpp imagewidget.cpp fieldwidget.cpp ratingwidget.cpp \ + ratingfieldwidget.cpp overlaywidget.cpp progress.cpp listboxtext.cpp collectiontypecombo.cpp \ + previewdialog.cpp + +libgui_a_METASOURCES = AUTO +KDE_OPTIONS = noautodist +EXTRA_DIST = combobox.h combobox.cpp \ +counteditem.h counteditem.cpp \ +datewidget.h datewidget.cpp \ +kwidgetlister.h kwidgetlister.cpp \ +listview.h listview.cpp \ +richtextlabel.h richtextlabel.cpp \ +stringmapdialog.h stringmapdialog.cpp \ +tabcontrol.h tabcontrol.cpp \ +lineedit.h lineedit.cpp \ +boolfieldwidget.h boolfieldwidget.cpp \ +choicefieldwidget.h choicefieldwidget.cpp \ +datefieldwidget.h datefieldwidget.cpp \ +imagefieldwidget.h imagefieldwidget.cpp \ +linefieldwidget.h linefieldwidget.cpp \ +numberfieldwidget.h numberfieldwidget.cpp \ +parafieldwidget.h parafieldwidget.cpp \ +tablefieldwidget.h tablefieldwidget.cpp \ +urlfieldwidget.h urlfieldwidget.cpp \ +ratingwidget.h ratingwidget.cpp \ +imagewidget.h imagewidget.cpp \ +fieldwidget.h fieldwidget.cpp \ +ratingfieldwidget.h ratingfieldwidget.cpp \ +overlaywidget.h overlaywidget.cpp \ +progress.h progress.cpp \ +listboxtext.h listboxtext.cpp \ +collectiontypecombo.h collectiontypecombo.cpp \ +previewdialog.h previewdialog.cpp + +CLEANFILES = *~ + diff --git a/src/gui/boolfieldwidget.cpp b/src/gui/boolfieldwidget.cpp new file mode 100644 index 0000000..5a8e88b --- /dev/null +++ b/src/gui/boolfieldwidget.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "boolfieldwidget.h" +#include "../field.h" +#include "../latin1literal.h" + +#include <qlabel.h> +#include <qcheckbox.h> +#include <qlayout.h> + +using Tellico::GUI::BoolFieldWidget; + +BoolFieldWidget::BoolFieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : FieldWidget(field_, parent_, name_) { + + m_checkBox = new QCheckBox(this); + connect(m_checkBox, SIGNAL(clicked()), SIGNAL(modified())); + registerWidget(); +} + +QString BoolFieldWidget::text() const { + if(m_checkBox->isChecked()) { + return QString::fromLatin1("true"); + } + + return QString(); +} + +void BoolFieldWidget::setText(const QString& text_) { + blockSignals(true); + + m_checkBox->blockSignals(true); + // be lax, don't have to check for "1" or "true" + // just check for a non-empty string + m_checkBox->setChecked(!text_.isEmpty()); + m_checkBox->blockSignals(false); + + blockSignals(false); +} + +void BoolFieldWidget::clear() { + m_checkBox->setChecked(false); + editMultiple(false); +} + +QWidget* BoolFieldWidget::widget() { + return m_checkBox; +} + +#include "boolfieldwidget.moc" diff --git a/src/gui/boolfieldwidget.h b/src/gui/boolfieldwidget.h new file mode 100644 index 0000000..81af5a6 --- /dev/null +++ b/src/gui/boolfieldwidget.h @@ -0,0 +1,51 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef BOOLFIELDWIDGET_H +#define BOOLFIELDWIDGET_H + +#include "fieldwidget.h" +#include "../datavectors.h" + +class QCheckBox; +class QString; + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class BoolFieldWidget : public FieldWidget { +Q_OBJECT + +public: + BoolFieldWidget(Data::FieldPtr field, QWidget* parent, const char* name=0); + virtual ~BoolFieldWidget() {} + + virtual QString text() const; + virtual void setText(const QString& text); + +public slots: + virtual void clear(); + +protected: + virtual QWidget* widget(); + +private: + QCheckBox* m_checkBox; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/choicefieldwidget.cpp b/src/gui/choicefieldwidget.cpp new file mode 100644 index 0000000..e9c6870 --- /dev/null +++ b/src/gui/choicefieldwidget.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "choicefieldwidget.h" +#include "../field.h" + +#include <kcombobox.h> + +#include <qlabel.h> +#include <qlayout.h> + +using Tellico::GUI::ChoiceFieldWidget; + +ChoiceFieldWidget::ChoiceFieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : FieldWidget(field_, parent_, name_), m_comboBox(0) { + + m_comboBox = new KComboBox(this); + connect(m_comboBox, SIGNAL(activated(int)), SIGNAL(modified())); + // always have empty choice + m_comboBox->insertItem(QString::null); + m_comboBox->insertStringList(field_->allowed()); + m_comboBox->setMinimumWidth(5*fontMetrics().maxWidth()); + + registerWidget(); +} + +QString ChoiceFieldWidget::text() const { + return m_comboBox->currentText(); +} + +void ChoiceFieldWidget::setText(const QString& text_) { + blockSignals(true); + + m_comboBox->blockSignals(true); + m_comboBox->setCurrentItem(text_); + m_comboBox->blockSignals(false); + + blockSignals(false); +} + +void ChoiceFieldWidget::clear() { + m_comboBox->setCurrentItem(0); // first item is empty + editMultiple(false); +} + +void ChoiceFieldWidget::updateFieldHook(Data::FieldPtr, Data::FieldPtr newField_) { + QString value = text(); + m_comboBox->clear(); + // always have empty choice + m_comboBox->insertItem(QString::null); + m_comboBox->insertStringList(newField_->allowed()); + m_comboBox->setCurrentText(value); +} + +QWidget* ChoiceFieldWidget::widget() { + return m_comboBox; +} + +#include "choicefieldwidget.moc" diff --git a/src/gui/choicefieldwidget.h b/src/gui/choicefieldwidget.h new file mode 100644 index 0000000..a2c40f5 --- /dev/null +++ b/src/gui/choicefieldwidget.h @@ -0,0 +1,51 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef CHOICEFIELDWIDGET_H +#define CHOICEFIELDWIDGET_H + +#include "fieldwidget.h" +#include "../datavectors.h" + +class KComboBox; + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class ChoiceFieldWidget : public FieldWidget { +Q_OBJECT + +public: + ChoiceFieldWidget(Data::FieldPtr field, QWidget* parent, const char* name=0); + virtual ~ChoiceFieldWidget() {} + + virtual QString text() const; + virtual void setText(const QString& text); + +public slots: + virtual void clear(); + +protected: + virtual QWidget* widget(); + virtual void updateFieldHook(Data::FieldPtr oldField, Data::FieldPtr newField); + +private: + KComboBox* m_comboBox; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/collectiontypecombo.cpp b/src/gui/collectiontypecombo.cpp new file mode 100644 index 0000000..66749a3 --- /dev/null +++ b/src/gui/collectiontypecombo.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** + copyright : (C) 2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "collectiontypecombo.h" +#include "../collection.h" +#include "../collectionfactory.h" + +using Tellico::GUI::CollectionTypeCombo; + +CollectionTypeCombo::CollectionTypeCombo(QWidget* parent_) : ComboBox(parent_) { + reset(); +} + +void CollectionTypeCombo::reset() { + clear(); + // I want to sort the collection names + const CollectionNameMap nameMap = CollectionFactory::nameMap(); + QMap<QString, int> rNameMap; + for(CollectionNameMap::ConstIterator it = nameMap.begin(); it != nameMap.end(); ++it) { + rNameMap.insert(it.data(), it.key()); + } + const QValueList<int> collTypes = rNameMap.values(); + const QStringList collNames = rNameMap.keys(); + int custom = -1; + const int total = collTypes.count(); + // when i equals the size, then go back and do custom + for(int i = 0; i <= total; ++i) { + // put custom last + if(custom > -1 && count() >= total) { + break; // already done it! + } else if(i == total) { + i = custom; + } else if(collTypes[i] == Data::Collection::Base) { + custom = i; + continue; + } + insertItem(collNames[i], collTypes[i]); + } +} + +void CollectionTypeCombo::setCurrentType(int type_) { + setCurrentData(type_); +} diff --git a/src/gui/collectiontypecombo.h b/src/gui/collectiontypecombo.h new file mode 100644 index 0000000..8d3ef80 --- /dev/null +++ b/src/gui/collectiontypecombo.h @@ -0,0 +1,33 @@ +/*************************************************************************** + copyright : (C) 2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICO_GUI_COLLECTIONTYPECOMBO_H +#define TELLICO_GUI_COLLECTIONTYPECOMBO_H + +#include "combobox.h" + +namespace Tellico { + +namespace GUI { + +class CollectionTypeCombo : public ComboBox { +public: + CollectionTypeCombo(QWidget* parent); + void reset(); + void setCurrentType(int type); + int currentType() const { return currentData().toInt(); } +}; + + } +} +#endif diff --git a/src/gui/combobox.cpp b/src/gui/combobox.cpp new file mode 100644 index 0000000..1aff29b --- /dev/null +++ b/src/gui/combobox.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** + copyright : (C) 2001-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "combobox.h" + +#include <kdebug.h> + +using Tellico::GUI::ComboBox; + +ComboBox::ComboBox(QWidget* parent_) : KComboBox(parent_) { + setEditable(false); +} + +void ComboBox::clear() { + KComboBox::clear(); + m_data.clear(); +} + +void ComboBox::insertItem(const QString& s_, const QVariant& t_, int idx_/* =-1 */) { + KComboBox::insertItem(s_, idx_); + if(idx_ < 0) { + m_data.push_back(t_); + } else { + while(idx_ > static_cast<int>(m_data.count())) { + m_data.push_back(QVariant()); + } + m_data.insert(m_data.at(idx_), t_); + } +} + +void ComboBox::insertItems(const QStringList& s_, const QValueList<QVariant>& t_, int idx_ /*=-1*/) { + if(s_.count() != t_.count()) { + kdWarning() << "ComboBox::insertItems() - must have equal number of items in list!" << endl; + return; + } + + for(uint i = 0; i < s_.count(); ++i) { + insertItem(s_[i], t_[i], idx_+i); + } +} + +const QVariant& ComboBox::currentData() const { + return data(currentItem()); +} + +const QVariant& ComboBox::data(uint idx_) const { + if(idx_ >= m_data.count()) { + static QVariant t; // inescapable + return t; + } + return m_data[idx_]; +} + +void ComboBox::setCurrentData(const QVariant& data_) { + for(uint i = 0; i < m_data.count(); ++i) { + if(m_data[i] == data_) { + setCurrentItem(i); + break; + } + } +} diff --git a/src/gui/combobox.h b/src/gui/combobox.h new file mode 100644 index 0000000..d02dbb8 --- /dev/null +++ b/src/gui/combobox.h @@ -0,0 +1,52 @@ +/*************************************************************************** + copyright : (C) 2001-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICO_GUI_COMBOBOX_H +#define TELLICO_GUI_COMBOBOX_H + +#include <kcombobox.h> + +#include <qvariant.h> +#include <qvaluelist.h> + +class QString; + +namespace Tellico { + namespace GUI { + +/** + * A combobox for mapping a QVariant to each item. + * + * @author Robby Stephenson + */ +class ComboBox : public KComboBox { +public: + ComboBox(QWidget* parent_); + + void clear(); + const QVariant& currentData() const; + const QVariant& data(uint index) const; + void insertItem(const QString& string, const QVariant& datum, int index = -1); + void insertItems(const QStringList& strings, const QValueList<QVariant>& data, int index = -1); + + // set current item to match data + void setCurrentData(const QVariant& data); + +private: + QValueList<QVariant> m_data; +}; + + } // end namespace +} //end namespace + +#endif diff --git a/src/gui/counteditem.cpp b/src/gui/counteditem.cpp new file mode 100644 index 0000000..08b4f25 --- /dev/null +++ b/src/gui/counteditem.cpp @@ -0,0 +1,113 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "counteditem.h" +#include "../tellico_utils.h" +#include "../tellico_debug.h" + +#include <kglobalsettings.h> +#include <kstringhandler.h> + +#include <qpainter.h> +#include <qpixmap.h> + +using Tellico::GUI::CountedItem; + +int CountedItem::compare(QListViewItem* item_, int col_, bool asc_) const { + GUI::ListView* lv = listView(); + GUI::CountedItem* item = static_cast<GUI::CountedItem*>(item_); + if(lv->sortStyle() == GUI::ListView::SortByCount) { + if(count() < item->count()) { + return -1; + } else if(count() > item->count()) { + return 1; + } else { + return GUI::ListViewItem::compare(item, col_, asc_); + } + } + // for now, only other style is by text + return GUI::ListViewItem::compare(item, col_, asc_); +} + +void CountedItem::paintCell(QPainter* p_, const QColorGroup& cg_, + int column_, int width_, int align_) { + if(!p_) { + return; + } + + // always paint the cell + + // show count is only for first column + if(column_ != 0) { + ListViewItem::paintCell(p_, cg_, column_, width_, align_); + return; + } + + // set a catchable text so that we can have our own implementation (see further down) + // but still benefit from KListView::paintCell + QString oldText = text(column_); +// if(oldText.isEmpty()) { + if(oldText == '\t') { + return; // avoid endless loop! + } + + setText(column_, QChar('\t')); + ListViewItem::paintCell(p_, cg_, column_, width_, align_); + setText(column_, oldText); + + int marg = listView()->itemMargin(); + int r = marg; + const QPixmap* icon = pixmap(column_); + if(icon) { + r += icon->width() + marg; + } + + QFontMetrics fm = p_->fontMetrics(); + QString numText = QString::fromLatin1(" (%1)").arg(count()); + // don't call CountedListViewItem::width() because that includes the count already + int w = ListViewItem::width(fm, listView(), column_); + int countWidth = fm.width(numText); + if(w+marg+r+countWidth > width_) { + oldText = KStringHandler::rPixelSqueeze(oldText, fm, width_-marg-r-countWidth); + } + if(isSelected()) { + p_->setPen(cg_.highlightedText()); + } else { + p_->setPen(cg_.text()); + } + QRect br(0, height(), r, 0); + if(!oldText.isEmpty() && !oldText.startsWith(QChar('\t'))) { + p_->drawText(r, 0, width_-marg-r, height(), align_ | AlignVCenter, oldText, -1, &br); + } + + if(isSelected()) { + p_->setPen(cg_.highlightedText()); + } else { + if(!Tellico::contrastColor.isValid()) { + updateContrastColor(cg_); + } + p_->setPen(Tellico::contrastColor); + } + p_->drawText(br.right(), 0, width_-marg-br.right(), height(), align_ | Qt::AlignVCenter, numText); +} + +int CountedItem::width(const QFontMetrics& fm_, const QListView* lv_, int column_) const { + int w = ListViewItem::width(fm_, lv_, column_); + + // show count is only for first column + if(column_ == 0) { + QString numText = QString::fromLatin1(" (%1)").arg(count()); + w += fm_.width(numText) + 2; // add a little pad + } + return w; +} diff --git a/src/gui/counteditem.h b/src/gui/counteditem.h new file mode 100644 index 0000000..9ea0138 --- /dev/null +++ b/src/gui/counteditem.h @@ -0,0 +1,49 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef COUNTEDITEM_H +#define COUNTEDITEM_H + +#include "listview.h" + +class QPainter; +class QColorGroup; +class QFontMetrics; + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class CountedItem : public GUI::ListViewItem { +public: + CountedItem(ListView* parent) : ListViewItem(parent) {} + CountedItem(ListViewItem* parent) : ListViewItem(parent) {} + + virtual int compare(QListViewItem* item, int col, bool ascending) const; + /** + * Paints the cell, adding the number count. + */ + virtual void paintCell(QPainter* p, const QColorGroup& cg, + int column, int width, int align); + virtual int width(const QFontMetrics& fm, const QListView* lv, int c) const; + + virtual int count() const { return childCount(); } + virtual int realChildCount() const { return count(); } +}; + + } // end namespace +} // end namespace + +#endif diff --git a/src/gui/datefieldwidget.cpp b/src/gui/datefieldwidget.cpp new file mode 100644 index 0000000..d0609d6 --- /dev/null +++ b/src/gui/datefieldwidget.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "datefieldwidget.h" +#include "datewidget.h" +#include "../field.h" + +using Tellico::GUI::DateFieldWidget; + +DateFieldWidget::DateFieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : FieldWidget(field_, parent_, name_) { + + m_widget = new DateWidget(this); + connect(m_widget, SIGNAL(signalModified()), SIGNAL(modified())); + + registerWidget(); +} + +QString DateFieldWidget::text() const { + return m_widget->text(); +} + +void DateFieldWidget::setText(const QString& text_) { + blockSignals(true); + m_widget->blockSignals(true); + + m_widget->setDate(text_); + + m_widget->blockSignals(false); + blockSignals(false); +} + +void DateFieldWidget::clear() { + m_widget->clear(); + editMultiple(false); +} + +QWidget* DateFieldWidget::widget() { + return m_widget; +} + +#include "datefieldwidget.moc" diff --git a/src/gui/datefieldwidget.h b/src/gui/datefieldwidget.h new file mode 100644 index 0000000..e3c2d55 --- /dev/null +++ b/src/gui/datefieldwidget.h @@ -0,0 +1,51 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef DATEFIELDWIDGET_H +#define DATEFIELDWIDGET_H + +#include "fieldwidget.h" +#include "../datavectors.h" + +class QString; + +namespace Tellico { + namespace GUI { + class DateWidget; + +/** + * @author Robby Stephenson + */ +class DateFieldWidget : public FieldWidget { +Q_OBJECT + +public: + DateFieldWidget(Data::FieldPtr field, QWidget* parent, const char* name=0); + virtual ~DateFieldWidget() {} + + virtual QString text() const; + virtual void setText(const QString& text); + +public slots: + virtual void clear(); + +protected: + virtual QWidget* widget(); + +private: + DateWidget* m_widget; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/datewidget.cpp b/src/gui/datewidget.cpp new file mode 100644 index 0000000..42e2d4c --- /dev/null +++ b/src/gui/datewidget.cpp @@ -0,0 +1,279 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +// this class borrows heavily from kdateedit.h in the kdepim module +// which is Copyright (c) 2002 Cornelius Schumacher <[email protected]> +// and published under the LGPL + +#include "datewidget.h" + +#include <kdebug.h> +#include <kcombobox.h> +#include <kpushbutton.h> +#include <kdatepicker.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kglobalsettings.h> +#include <kcalendarsystem.h> + +#include <qvbox.h> +#include <qlayout.h> + +using Tellico::GUI::SpinBox; +using Tellico::GUI::DateWidget; + +SpinBox::SpinBox(int min, int max, QWidget *parent) : QSpinBox(min, max, 1, parent) +{ + editor()->setAlignment(AlignRight); + // I want to be able to omit the day + // an empty string just removes the special value, so set white space + setSpecialValueText(QChar(' ')); +} + +DateWidget::DateWidget(QWidget* parent_, const char* name_) : QWidget(parent_, name_) { + QHBoxLayout* l = new QHBoxLayout(this, 0, 4); + + KLocale* locale = KGlobal::locale(); + + // 0 allows empty value + m_daySpin = new SpinBox(0, 31, this); + l->addWidget(m_daySpin, 1); + + m_monthCombo = new KComboBox(false, this); + l->addWidget(m_monthCombo, 1); + // allow empty item + m_monthCombo->insertItem(QString::null); + QDate d; + for(int i = 1; ; ++i) { + QString str = locale->calendar()->monthName(i, locale->calendar()->year(d)); + if(str.isNull()) { + break; + } + m_monthCombo->insertItem(str); + } + + m_yearSpin = new SpinBox(locale->calendar()->minValidYear(), + locale->calendar()->maxValidYear(), this); + l->addWidget(m_yearSpin, 1); + + connect(m_daySpin, SIGNAL(valueChanged(int)), SLOT(slotDateChanged())); + connect(m_monthCombo, SIGNAL(activated(int)), SLOT(slotDateChanged())); + connect(m_yearSpin, SIGNAL(valueChanged(int)), SLOT(slotDateChanged())); + + m_dateButton = new KPushButton(this); + m_dateButton->setIconSet(SmallIconSet(QString::fromLatin1("date"))); + connect(m_dateButton, SIGNAL(clicked()), SLOT(slotShowPicker())); + l->addWidget(m_dateButton, 0); + + m_frame = new QVBox(0, 0, WType_Popup); + m_frame->setFrameStyle(QFrame::PopupPanel | QFrame::Raised); + m_frame->setLineWidth(3); + m_frame->hide(); + + m_picker = new KDatePicker(m_frame, 0); // must include name to get correct constructor + connect(m_picker, SIGNAL(dateEntered(QDate)), SLOT(slotDateEntered(QDate))); + connect(m_picker, SIGNAL(dateSelected(QDate)), SLOT(slotDateSelected(QDate))); +} + +void DateWidget::slotDateChanged() { + int day = m_daySpin->value(); + day = QMIN(QMAX(day, m_daySpin->minValue()), m_daySpin->maxValue()); + + int m = m_monthCombo->currentItem(); + m = QMIN(QMAX(m, 0), m_monthCombo->count()-1); + + int y = m_yearSpin->value(); + y = QMIN(QMAX(y, m_yearSpin->minValue()), m_yearSpin->maxValue()); + + // if all are valid, set this date + if(day > m_daySpin->minValue() && m > 0 && y > m_yearSpin->minValue()) { + QDate d(y, m, day); + setDate(d); + } + emit signalModified(); +} + +QDate DateWidget::date() const { + // possible for either day, month, or year to be empty + // in which case a null date is returned + int day = m_daySpin->value(); + // min value is the empty one + if(day == m_daySpin->minValue()) { + return QDate(); + } + int month = m_monthCombo->currentItem(); + if(month == 0) { + return QDate(); + } + int year = m_yearSpin->value(); + if(year == m_yearSpin->minValue()) { + return QDate(); + } + return QDate(year, month, day); +} + +QString DateWidget::text() const { + // possible for either day, month, or year to be empty + // but not all three + bool empty = true; + // format is "year-month-day" + QString s; + if(m_yearSpin->value() > m_yearSpin->minValue()) { + s += QString::number(m_yearSpin->value()); + empty = false; + } + s += '-'; + // first item is empty + if(m_monthCombo->currentItem() > 0) { + s += QString::number(m_monthCombo->currentItem()); + empty = false; + } + s += '-'; + if(m_daySpin->value() > m_daySpin->minValue()) { + s += QString::number(m_daySpin->value()); + empty = false; + } + return empty ? QString() : s; +} + +void DateWidget::setDate(const QDate& date_) { + m_daySpin->blockSignals(true); + m_monthCombo->blockSignals(true); + m_yearSpin->blockSignals(true); + + const KCalendarSystem * calendar = KGlobal::locale()->calendar(); + m_daySpin->setMaxValue(calendar->daysInMonth(date_)); + m_daySpin->setValue(calendar->day(date_)); + m_monthCombo->setCurrentItem(calendar->month(date_)); // don't subtract 1 since there's the blank first item + m_yearSpin->setValue(calendar->year(date_)); + + m_daySpin->blockSignals(false); + m_monthCombo->blockSignals(false); + m_yearSpin->blockSignals(false); +} + +void DateWidget::setDate(const QString& date_) { + m_daySpin->blockSignals(true); + m_monthCombo->blockSignals(true); + m_yearSpin->blockSignals(true); + + QStringList s = QStringList::split('-', date_, true); + bool ok = true; + int y = s.count() > 0 ? s[0].toInt(&ok) : m_yearSpin->minValue(); + if(!ok) { + y = m_yearSpin->minValue(); + ok = true; + } + y = QMIN(QMAX(y, m_yearSpin->minValue()), m_yearSpin->maxValue()); + m_yearSpin->setValue(y); + + int m = s.count() > 1 ? s[1].toInt(&ok) : 0; + if(!ok) { + m = 0; + ok = true; + } + m = QMIN(QMAX(m, 0), m_monthCombo->count()-1); + m_monthCombo->setCurrentItem(m); + + // need to update number of days in month + // for now set date to 1 + QDate date(y, (m == 0 ? 1 : m), 1); + m_daySpin->blockSignals(true); + m_daySpin->setMaxValue(KGlobal::locale()->calendar()->daysInMonth(date)); + m_daySpin->blockSignals(false); + + int day = s.count() > 2 ? s[2].toInt(&ok) : m_daySpin->minValue(); + if(!ok) { + day = m_daySpin->minValue(); + } + day = QMIN(QMAX(day, m_daySpin->minValue()), m_daySpin->maxValue()); + m_daySpin->setValue(day); + + m_daySpin->blockSignals(false); + m_monthCombo->blockSignals(false); + m_yearSpin->blockSignals(false); + + // if all are valid, set this date + if(day > m_daySpin->minValue() && m > 0 && y > m_yearSpin->minValue()) { + QDate d(y, m, day); + m_picker->blockSignals(true); + m_picker->setDate(d); + m_picker->blockSignals(false); + } +} + +void DateWidget::clear() { + m_daySpin->blockSignals(true); + m_monthCombo->blockSignals(true); + m_yearSpin->blockSignals(true); + m_picker->blockSignals(true); + + m_daySpin->setValue(m_daySpin->minValue()); + m_monthCombo->setCurrentItem(0); + m_yearSpin->setValue(m_yearSpin->minValue()); + m_picker->setDate(QDate::currentDate()); + + m_daySpin->blockSignals(false); + m_monthCombo->blockSignals(false); + m_yearSpin->blockSignals(false); + m_picker->blockSignals(false); +} + +void DateWidget::slotShowPicker() { + QRect desk = KGlobalSettings::desktopGeometry(this); + QPoint popupPoint = mapToGlobal(QPoint(0, 0)); + + int dateFrameHeight = m_frame->sizeHint().height(); + if(popupPoint.y() + height() + dateFrameHeight > desk.bottom()) { + popupPoint.setY(popupPoint.y() - dateFrameHeight); + } else { + popupPoint.setY(popupPoint.y() + height()); + } + int dateFrameWidth = m_frame->sizeHint().width(); + if(popupPoint.x() + dateFrameWidth > desk.right()) { + popupPoint.setX(desk.right() - dateFrameWidth); + } + + if(popupPoint.x() < desk.left()) { + popupPoint.setX( desk.left()); + } + if(popupPoint.y() < desk.top()) { + popupPoint.setY(desk.top()); + } + + m_frame->move(popupPoint); + + QDate d = date(); + if(d.isValid()) { + m_picker->setDate(d); + } + + m_frame->show(); +} + +void DateWidget::slotDateSelected(QDate date_) { + if(date_.isValid()) { + setDate(date_); + emit signalModified(); + m_frame->hide(); + } +} + +void DateWidget::slotDateEntered(QDate date_) { + if(date_.isValid()) { + setDate(date_); + emit signalModified(); + } +} + +#include "datewidget.moc" diff --git a/src/gui/datewidget.h b/src/gui/datewidget.h new file mode 100644 index 0000000..93d7bcb --- /dev/null +++ b/src/gui/datewidget.h @@ -0,0 +1,74 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICODATEWIDGET_H +#define TELLICODATEWIDGET_H + +#include <qspinbox.h> +#include <qdatetime.h> + +class KComboBox; +class KPushButton; +class KDatePicker; + +class QVBox; +class QString; + +namespace Tellico { + namespace GUI { + +class SpinBox : public QSpinBox { +Q_OBJECT + +public: + SpinBox(int min, int max, QWidget *parent); +}; + +/** + * @author Robby Stephenson + */ +class DateWidget : public QWidget { +Q_OBJECT + +public: + DateWidget(QWidget* parent, const char* name = 0); + ~DateWidget() {} + + QDate date() const; + QString text() const; + void setDate(const QDate& date); + void setDate(const QString& date); + void clear(); + +signals: + void signalModified(); + +private slots: + void slotDateChanged(); + void slotShowPicker(); + void slotDateSelected(QDate newDate); + void slotDateEntered(QDate newDate); + +private: + SpinBox* m_daySpin; + KComboBox* m_monthCombo; + SpinBox* m_yearSpin; + KPushButton* m_dateButton; + + QVBox* m_frame; + KDatePicker* m_picker; +}; + + } // end namespace +} // end namespace +#endif diff --git a/src/gui/fieldwidget.cpp b/src/gui/fieldwidget.cpp new file mode 100644 index 0000000..6a9fc66 --- /dev/null +++ b/src/gui/fieldwidget.cpp @@ -0,0 +1,201 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "fieldwidget.h" +#include "linefieldwidget.h" +#include "parafieldwidget.h" +#include "boolfieldwidget.h" +#include "choicefieldwidget.h" +#include "numberfieldwidget.h" +#include "urlfieldwidget.h" +#include "imagefieldwidget.h" +#include "datefieldwidget.h" +#include "tablefieldwidget.h" +#include "ratingfieldwidget.h" +#include "../field.h" + +#include <kdebug.h> +#include <kurllabel.h> +#include <klocale.h> + +#include <qlayout.h> +#include <qwhatsthis.h> +#include <qregexp.h> +#include <qlabel.h> +#include <qcheckbox.h> +#include <qstyle.h> +#include <qtimer.h> + +namespace { + // if you change this, update numberfieldwidget, too + const int FIELD_EDIT_WIDGET_INDEX = 2; +} + +using Tellico::GUI::FieldWidget; + +const QRegExp FieldWidget::s_semiColon = QRegExp(QString::fromLatin1("\\s*;\\s*")); + +FieldWidget* FieldWidget::create(Data::FieldPtr field_, QWidget* parent_, const char* name_) { + switch (field_->type()) { + case Data::Field::Line: + return new GUI::LineFieldWidget(field_, parent_, name_); + + case Data::Field::Para: + return new GUI::ParaFieldWidget(field_, parent_, name_); + + case Data::Field::Bool: + return new GUI::BoolFieldWidget(field_, parent_, name_); + + case Data::Field::Number: + return new GUI::NumberFieldWidget(field_, parent_, name_); + + case Data::Field::Choice: + return new GUI::ChoiceFieldWidget(field_, parent_, name_); + + case Data::Field::Table: + case Data::Field::Table2: + return new GUI::TableFieldWidget(field_, parent_, name_); + + case Data::Field::Date: + return new GUI::DateFieldWidget(field_, parent_, name_); + + case Data::Field::URL: + return new GUI::URLFieldWidget(field_, parent_, name_); + + case Data::Field::Image: + return new GUI::ImageFieldWidget(field_, parent_, name_); + + case Data::Field::Rating: + return new GUI::RatingFieldWidget(field_, parent_, name_); + + case Data::Field::ReadOnly: + case Data::Field::Dependent: + kdWarning() << "FieldWidget::create() - read-only/dependent field, this shouldn't have been called" << endl; + return 0; + + default: + kdWarning() << "FieldWidget::create() - unknown field type = " << field_->type() << endl; + return 0; + } +} + +FieldWidget::FieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : QWidget(parent_, name_), m_field(field_) { + QHBoxLayout* l = new QHBoxLayout(this, 2, 2); // parent, margin, spacing + l->addSpacing(4); // add some more space in the columns between widgets + if(QCString(style().name()).lower().find("keramik", 0, false) > -1) { + l->setMargin(1); + } + + Data::Field::Type type = field_->type(); + QString s = i18n("Edit Label", "%1:").arg(field_->title()); + if(type == Data::Field::URL) { + // set URL to null for now + m_label = new KURLLabel(QString::null, s, this); + } else { + m_label = new QLabel(s, this); + } + m_label->setFixedWidth(m_label->sizeHint().width()); + l->addWidget(m_label); + + // expands indicates if the edit widget should expand to full width of widget + m_expands = (type == Data::Field::Line + || type == Data::Field::Para + || type == Data::Field::Number + || type == Data::Field::URL + || type == Data::Field::Table + || type == Data::Field::Table2 + || type == Data::Field::Image + || type == Data::Field::Date); + + m_editMultiple = new QCheckBox(this); + m_editMultiple->setChecked(true); + m_editMultiple->setFixedWidth(m_editMultiple->sizeHint().width()); // don't let it have any extra space + connect(m_editMultiple, SIGNAL(toggled(bool)), SLOT(setEnabled(bool))); + l->addWidget(m_editMultiple); + + QWhatsThis::add(this, field_->description()); + // after letting the subclass get created, insert default value + QTimer::singleShot(0, this, SLOT(insertDefault())); +} + +void FieldWidget::insertDefault() { + setText(m_field->defaultValue()); +} + +bool FieldWidget::isEnabled() { + return widget()->isEnabled(); +} + +void FieldWidget::setEnabled(bool enabled_) { + if(enabled_ == isEnabled()) { + return; + } + + widget()->setEnabled(enabled_); + m_editMultiple->setChecked(enabled_); +} + +int FieldWidget::labelWidth() const { + return m_label->sizeHint().width(); +} + +void FieldWidget::setLabelWidth(int width_) { + m_label->setFixedWidth(width_); +} + +bool FieldWidget::expands() const { + return m_expands; +} + +void FieldWidget::editMultiple(bool show_) { + if(show_ == m_editMultiple->isShown()) { + return; + } + + // FIXME: maybe modified should only be signaled when the button is toggle on + if(show_) { + m_editMultiple->show(); + connect(m_editMultiple, SIGNAL(clicked()), this, SIGNAL(modified())); + } else { + m_editMultiple->hide(); + disconnect(m_editMultiple, SIGNAL(clicked()), this, SIGNAL(modified())); + } + // the widget length needs to be updated since it gets shorter + widget()->updateGeometry(); +} + +void FieldWidget::registerWidget() { + QWidget* w = widget(); + m_label->setBuddy(w); + if(w->isFocusEnabled()) { + setFocusProxy(w); + } + + QHBoxLayout* l = static_cast<QHBoxLayout*>(layout()); + l->insertWidget(FIELD_EDIT_WIDGET_INDEX, w, m_expands ? 1 : 0 /*stretch*/); + if(!m_expands) { + l->insertStretch(FIELD_EDIT_WIDGET_INDEX+1, 1 /*stretch*/); + } + updateGeometry(); +} + +void FieldWidget::updateField(Data::FieldPtr oldField_, Data::FieldPtr newField_) { + m_field = newField_; + m_label->setText(i18n("Edit Label", "%1:").arg(newField_->title())); + updateGeometry(); + QWhatsThis::add(this, newField_->description()); + updateFieldHook(oldField_, newField_); +} + +#include "fieldwidget.moc" diff --git a/src/gui/fieldwidget.h b/src/gui/fieldwidget.h new file mode 100644 index 0000000..dd34ebb --- /dev/null +++ b/src/gui/fieldwidget.h @@ -0,0 +1,91 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef FIELDWIDGET_H +#define FIELDWIDGET_H + +#include "../datavectors.h" + +#include <qwidget.h> +#include <qregexp.h> + +class QLabel; +class QCheckBox; +class QString; + +namespace Tellico { + namespace Data { + class Field; + } + namespace GUI { + +/** + * The FieldWidget class is a box that shows a label, then a widget which depends + * on the field type, and then a checkbox for multiple editing. + * + * @author Robby Stephenson + */ +class FieldWidget : public QWidget { +Q_OBJECT + +public: + FieldWidget(Data::FieldPtr field, QWidget* parent, const char* name=0); + virtual ~FieldWidget() {} + + Data::FieldPtr field() const { return m_field; } + virtual QString text() const = 0; + virtual void setText(const QString& text) = 0; + + int labelWidth() const; + void setLabelWidth(int width); + bool isEnabled(); + bool expands() const; + void editMultiple(bool show); + // calls updateFieldHook() + void updateField(Data::FieldPtr oldField, Data::FieldPtr newField); + + // only used by LineFieldWidget, really + virtual void addCompletionObjectItem(const QString&) {} + + // factory function + static FieldWidget* create(Data::FieldPtr field, QWidget* parent, const char* name=0); + +public slots: + virtual void insertDefault(); + virtual void clear() = 0; + void setEnabled(bool enabled); + +signals: + virtual void modified(); + +protected: + QLabel* label() { return m_label; } // needed so the URLField can handle clicks on the label + virtual QWidget* widget() = 0; + void registerWidget(); + + // not all widgets have to be updated when the field changes + virtual void updateFieldHook(Data::FieldPtr, Data::FieldPtr) {} + + static const QRegExp s_semiColon; + +private: + Data::FieldPtr m_field; + QLabel* m_label; + QCheckBox* m_editMultiple; + + bool m_expands; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/imagefieldwidget.cpp b/src/gui/imagefieldwidget.cpp new file mode 100644 index 0000000..2cb9b40 --- /dev/null +++ b/src/gui/imagefieldwidget.cpp @@ -0,0 +1,54 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "imagefieldwidget.h" +#include "imagewidget.h" +#include "../field.h" +#include "../latin1literal.h" + +using Tellico::GUI::ImageFieldWidget; + +ImageFieldWidget::ImageFieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : FieldWidget(field_, parent_, name_) { + + m_widget = new ImageWidget(this); + m_widget->setLinkOnlyChecked(field_->property(QString::fromLatin1("link")) == Latin1Literal("true")); + connect(m_widget, SIGNAL(signalModified()), SIGNAL(modified())); + + registerWidget(); +} + +QString ImageFieldWidget::text() const { + return m_widget->id(); +} + +void ImageFieldWidget::setText(const QString& text_) { + blockSignals(true); + m_widget->blockSignals(true); + + m_widget->setImage(text_); + + m_widget->blockSignals(false); + blockSignals(false); +} + +void ImageFieldWidget::clear() { + m_widget->slotClear(); + editMultiple(false); +} + +QWidget* ImageFieldWidget::widget() { + return m_widget; +} + +#include "imagefieldwidget.moc" diff --git a/src/gui/imagefieldwidget.h b/src/gui/imagefieldwidget.h new file mode 100644 index 0000000..b5ebea5 --- /dev/null +++ b/src/gui/imagefieldwidget.h @@ -0,0 +1,51 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef IMAGEFIELDWIDGET_H +#define IMAGEFIELDWIDGET_H + +#include "fieldwidget.h" +#include "../datavectors.h" + +class QString; + +namespace Tellico { + namespace GUI { + class ImageWidget; + +/** + * @author Robby Stephenson + */ +class ImageFieldWidget : public FieldWidget { +Q_OBJECT + +public: + ImageFieldWidget(Data::FieldPtr field, QWidget* parent, const char* name=0); + virtual ~ImageFieldWidget() {} + + virtual QString text() const; + virtual void setText(const QString& text); + +public slots: + virtual void clear(); + +protected: + virtual QWidget* widget(); + +private: + ImageWidget* m_widget; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/imagewidget.cpp b/src/gui/imagewidget.cpp new file mode 100644 index 0000000..92f75c3 --- /dev/null +++ b/src/gui/imagewidget.cpp @@ -0,0 +1,257 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "imagewidget.h" +#include "../imagefactory.h" +#include "../image.h" +#include "../filehandler.h" +#include "../tellico_debug.h" +#include "../tellico_utils.h" + +#include <kfiledialog.h> +#include <klocale.h> +#include <kbuttonbox.h> +#include <kurldrag.h> +#include <kmessagebox.h> + +#include <qwmatrix.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qcheckbox.h> +#include <qdragobject.h> +#include <qapplication.h> // needed for drag distance + +namespace { + static const uint IMAGE_WIDGET_BUTTON_MARGIN = 8; + static const uint IMAGE_WIDGET_IMAGE_MARGIN = 4; + static const uint MAX_UNSCALED_WIDTH = 640; + static const uint MAX_UNSCALED_HEIGHT = 640; +} + +using Tellico::GUI::ImageWidget; + +ImageWidget::ImageWidget(QWidget* parent_, const char* name_) : QWidget(parent_, name_) { + QHBoxLayout* l = new QHBoxLayout(this); + l->setMargin(IMAGE_WIDGET_BUTTON_MARGIN); + m_label = new QLabel(this); + m_label->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored)); + m_label->setFrameStyle(QFrame::Panel | QFrame::Sunken); + m_label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + l->addWidget(m_label, 1); + l->addSpacing(IMAGE_WIDGET_BUTTON_MARGIN); + + QVBoxLayout* boxLayout = new QVBoxLayout(l); + boxLayout->addStretch(1); + + KButtonBox* box = new KButtonBox(this, Vertical); + box->addButton(i18n("Select Image..."), this, SLOT(slotGetImage())); + box->addButton(i18n("Clear"), this, SLOT(slotClear())); + box->layout(); + boxLayout->addWidget(box); + + boxLayout->addSpacing(8); + m_cbLinkOnly = new QCheckBox(i18n("Save link only"), this); + connect(m_cbLinkOnly, SIGNAL(clicked()), SLOT(slotLinkOnlyClicked())); + boxLayout->addWidget(m_cbLinkOnly); + + boxLayout->addStretch(1); + slotClear(); + + // accept image drops + setAcceptDrops(true); +} + +void ImageWidget::setImage(const QString& id_) { + if(id_.isEmpty()) { + slotClear(); + return; + } + m_imageID = id_; + m_pixmap = ImageFactory::pixmap(id_, MAX_UNSCALED_WIDTH, MAX_UNSCALED_HEIGHT); + const bool link = ImageFactory::imageInfo(id_).linkOnly; + m_cbLinkOnly->setChecked(link); + m_cbLinkOnly->setEnabled(link); + // if we're using a link, then the original URL _is_ the id + m_originalURL = link ? id_ : KURL(); + m_scaled = QPixmap(); + scale(); + + update(); +} + +void ImageWidget::setLinkOnlyChecked(bool link_) { + m_cbLinkOnly->setChecked(link_); +} + +void ImageWidget::slotClear() { +// m_image = Data::Image(); + m_imageID = QString(); + m_pixmap = QPixmap(); + m_scaled = m_pixmap; + m_originalURL = KURL(); + + m_label->setPixmap(m_scaled); + m_cbLinkOnly->setChecked(false); + m_cbLinkOnly->setEnabled(true); + update(); + emit signalModified(); +} + +void ImageWidget::scale() { + int ww = m_label->width() - 2*IMAGE_WIDGET_IMAGE_MARGIN; + int wh = m_label->height() - 2*IMAGE_WIDGET_IMAGE_MARGIN; + int pw = m_pixmap.width(); + int ph = m_pixmap.height(); + + if(ww < pw || wh < ph) { + int newWidth, newHeight; + if(pw*wh < ph*ww) { + newWidth = static_cast<int>(static_cast<double>(pw)*wh/static_cast<double>(ph)); + newHeight = wh; + } else { + newWidth = ww; + newHeight = static_cast<int>(static_cast<double>(ph)*ww/static_cast<double>(pw)); + } + + QWMatrix wm; + wm.scale(static_cast<double>(newWidth)/pw, static_cast<double>(newHeight)/ph); + m_scaled = m_pixmap.xForm(wm); + } else { + m_scaled = m_pixmap; + } + m_label->setPixmap(m_scaled); +} + +void ImageWidget::resizeEvent(QResizeEvent *) { + if(m_pixmap.isNull()) { + return; + } + + scale(); + update(); +} + +void ImageWidget::slotGetImage() { + KURL url = KFileDialog::getImageOpenURL(QString::null, this); + if(url.isEmpty() || !url.isValid()) { + return; + } + loadImage(url); +} + +void ImageWidget::slotLinkOnlyClicked() { + if(m_imageID.isEmpty()) { + // nothing to do, it has an empty image; + return; + } + + bool link = m_cbLinkOnly->isChecked(); + // if the user is trying to link and can't before there's no information about the url + // the let him know that + if(link && m_originalURL.isEmpty()) { + KMessageBox::sorry(this, i18n("Saving a link is only possible for newly added images.")); + m_cbLinkOnly->setChecked(false); + return; + } + // need to reset image id to be the original url + // if we're linking only, then we want the image id to be the same as the url + // so it needs to be added to the cache all over again + // probably could do this without downloading the image all over again, + // but I'm not going to do that right now + const QString& id = ImageFactory::addImage(m_originalURL, false, KURL(), link); + // same image, so no need to call setImage + m_imageID = id; + emit signalModified(); +} + +void ImageWidget::mousePressEvent(QMouseEvent* event_) { + // Only interested in LMB + if(event_->button() == Qt::LeftButton) { + // Store the position of the mouse press. + // check if position is inside the label + if(m_label->geometry().contains(event_->pos())) { + m_dragStart = event_->pos(); + } else { + m_dragStart = QPoint(); + } + } +} + +void ImageWidget::mouseMoveEvent(QMouseEvent* event_) { + int delay = QApplication::startDragDistance(); + // Only interested in LMB + if(event_->state() & Qt::LeftButton) { + // only allow drag is the image is non-null, and the drag start point isn't null and the user dragged far enough + if(!m_imageID.isEmpty() && !m_dragStart.isNull() && (m_dragStart - event_->pos()).manhattanLength() > delay) { + const Data::Image& img = ImageFactory::imageById(m_imageID); + if(!img.isNull()) { + QImageDrag* drag = new QImageDrag(img, this); + drag->dragCopy(); + } + } + } +} + +void ImageWidget::dragEnterEvent(QDragEnterEvent* event_) { + event_->accept(KURLDrag::canDecode(event_) || QImageDrag::canDecode(event_) || QTextDrag::canDecode(event_)); +} + +void ImageWidget::dropEvent(QDropEvent* event_) { + QImage image; + KURL::List urls; + QString text; + + GUI::CursorSaver cs(Qt::busyCursor); + if(QImageDrag::decode(event_, image)) { + // Qt reads PNG data by default + const QString& id = ImageFactory::addImage(image, QString::fromLatin1("PNG")); + if(!id.isEmpty() && id != m_imageID) { + setImage(id); + emit signalModified(); + } + } else if(KURLDrag::decode(event_, urls)) { + if(urls.isEmpty()) { + return; + } + // only care about the first one + const KURL& url = urls[0]; + if(url.isEmpty() || !url.isValid()) { + return; + } +// kdDebug() << "ImageWidget::dropEvent() - " << url.prettyURL() << endl; + loadImage(url); + } else if(QTextDrag::decode(event_, text)) { + KURL url(text); + if(url.isEmpty() || !url.isValid()) { + return; + } + loadImage(url); + } +} + +void ImageWidget::loadImage(const KURL& url_) { + const bool link = m_cbLinkOnly->isChecked(); + + GUI::CursorSaver cs; + // if we're linking only, then we want the image id to be the same as the url + const QString& id = ImageFactory::addImage(url_, false, KURL(), link); + if(id != m_imageID) { + setImage(id); + emit signalModified(); + } + // at the end, cause setImage() resets it + m_originalURL = url_; + m_cbLinkOnly->setEnabled(true); +} + +#include "imagewidget.moc" diff --git a/src/gui/imagewidget.h b/src/gui/imagewidget.h new file mode 100644 index 0000000..7780be4 --- /dev/null +++ b/src/gui/imagewidget.h @@ -0,0 +1,78 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICOIMAGEWIDGET_H +#define TELLICOIMAGEWIDGET_H + +#include <kurl.h> +#include <qwidget.h> +#include <qpixmap.h> +#include <qpoint.h> + +class QLabel; +class QResizeEvent; +class QMouseEvent; +class QDragEnterEvent; +class QDropEvent; +class QCheckBox; + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class ImageWidget : public QWidget { +Q_OBJECT + +public: + ImageWidget(QWidget* parent, const char* name = 0); + virtual ~ImageWidget() {} + + const QString& id() const { return m_imageID; } + void setImage(const QString& id); + void setLinkOnlyChecked(bool l); + +public slots: + void slotClear(); + +signals: + void signalModified(); + +protected: + virtual void resizeEvent(QResizeEvent* ev); + virtual void mousePressEvent(QMouseEvent* ev); + virtual void mouseMoveEvent(QMouseEvent* ev); + virtual void dragEnterEvent(QDragEnterEvent* ev); + virtual void dropEvent(QDropEvent* ev); + +private slots: + void slotGetImage(); + void slotLinkOnlyClicked(); + +private: + void scale(); + void loadImage(const KURL& url); + + QString m_imageID; + QPixmap m_pixmap; + QPixmap m_scaled; + QLabel* m_label; + QCheckBox* m_cbLinkOnly; + KURL m_originalURL; + QPoint m_dragStart; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/kwidgetlister.cpp b/src/gui/kwidgetlister.cpp new file mode 100644 index 0000000..80bf31b --- /dev/null +++ b/src/gui/kwidgetlister.cpp @@ -0,0 +1,178 @@ +/* -*- c++ -*- + kwidgetlister.cpp + + This file is part of libkdenetwork. + Copyright (c) 2001 Marc Mutz <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifh Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this library with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "kwidgetlister.h" + +#include <klocale.h> +#include <kdebug.h> +#include <kpushbutton.h> +#include <kiconloader.h> + +#include <qlayout.h> +#include <qhbox.h> + +#include <assert.h> + +KWidgetLister::KWidgetLister( int minWidgets, int maxWidgets, QWidget *parent, const char* name ) + : QWidget( parent, name ) +{ + mWidgetList.setAutoDelete(TRUE); + + mMinWidgets = QMAX( minWidgets, 1 ); + mMaxWidgets = QMAX( maxWidgets, mMinWidgets + 1 ); + + //--------- the button box + mLayout = new QVBoxLayout(this, 0, 4); + mButtonBox = new QHBox(this); + mButtonBox->setSpacing(4); + mLayout->addWidget( mButtonBox ); + + mBtnMore = new KPushButton( i18n("more widgets","More"), mButtonBox ); + mBtnMore->setIconSet(SmallIconSet(QString::fromLatin1("down"))); + mButtonBox->setStretchFactor( mBtnMore, 0 ); + + mBtnFewer = new KPushButton( i18n("fewer widgets","Fewer"), mButtonBox ); + mBtnFewer->setIconSet(SmallIconSet(QString::fromLatin1("up"))); + mButtonBox->setStretchFactor( mBtnFewer, 0 ); + + QWidget *spacer = new QWidget( mButtonBox ); + mButtonBox->setStretchFactor( spacer, 1 ); + + mBtnClear = new KPushButton( i18n("clear widgets","Clear"), mButtonBox ); + mBtnClear->setIconSet(SmallIconSet(QString::fromLatin1("locationbar_erase"))); + mButtonBox->setStretchFactor( mBtnClear, 0 ); + + //---------- connect everything + connect( mBtnMore, SIGNAL(clicked()), + this, SLOT(slotMore()) ); + connect( mBtnFewer, SIGNAL(clicked()), + this, SLOT(slotFewer()) ); + connect( mBtnClear, SIGNAL(clicked()), + this, SLOT(slotClear()) ); + + enableControls(); +} + +KWidgetLister::~KWidgetLister() +{ +} + +void KWidgetLister::slotMore() +{ + // the class should make certain that slotMore can't + // be called when mMaxWidgets are on screen. + assert( (int)mWidgetList.count() < mMaxWidgets ); + + addWidgetAtEnd(); + // adjustSize(); + enableControls(); +} + +void KWidgetLister::slotFewer() +{ + // the class should make certain that slotFewer can't + // be called when mMinWidgets are on screen. + assert( (int)mWidgetList.count() > mMinWidgets ); + + removeLastWidget(); + // adjustSize(); + enableControls(); +} + +void KWidgetLister::slotClear() +{ + setNumberOfShownWidgetsTo( mMinWidgets ); + + // clear remaining widgets + QPtrListIterator<QWidget> it( mWidgetList ); + for ( it.toFirst() ; it.current() ; ++it ) + clearWidget( (*it) ); + + // adjustSize(); + enableControls(); + emit clearWidgets(); +} + +void KWidgetLister::addWidgetAtEnd(QWidget *w) +{ + if (!w) w = this->createWidget(this); + + mLayout->insertWidget( mLayout->findWidget( mButtonBox ), w ); + mWidgetList.append( w ); + w->show(); + enableControls(); + emit widgetAdded(); + emit widgetAdded(w); +} + +void KWidgetLister::removeLastWidget() +{ + // The layout will take care that the + // widget is removed from screen, too. + mWidgetList.removeLast(); + enableControls(); + emit widgetRemoved(); +} + +void KWidgetLister::clearWidget( QWidget* /*aWidget*/ ) +{ +} + +QWidget* KWidgetLister::createWidget( QWidget* parent ) +{ + return new QWidget( parent ); +} + +void KWidgetLister::setNumberOfShownWidgetsTo( int aNum ) +{ + int superfluousWidgets = QMAX( (int)mWidgetList.count() - aNum, 0 ); + int missingWidgets = QMAX( aNum - (int)mWidgetList.count(), 0 ); + + // remove superfluous widgets + for ( ; superfluousWidgets ; superfluousWidgets-- ) + removeLastWidget(); + + // add missing widgets + for ( ; missingWidgets ; missingWidgets-- ) + addWidgetAtEnd(); +} + +void KWidgetLister::enableControls() +{ + int count = mWidgetList.count(); + bool isMaxWidgets = ( count >= mMaxWidgets ); + bool isMinWidgets = ( count <= mMinWidgets ); + + mBtnMore->setEnabled( !isMaxWidgets ); + mBtnFewer->setEnabled( !isMinWidgets ); +} + +#include "kwidgetlister.moc" diff --git a/src/gui/kwidgetlister.h b/src/gui/kwidgetlister.h new file mode 100644 index 0000000..e02b54c --- /dev/null +++ b/src/gui/kwidgetlister.h @@ -0,0 +1,150 @@ +/* -*- c++ -*- + kwidgetlister.h + + This file is part of libkdenetwork. + Copyright (c) 2001 Marc Mutz <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifh Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this library with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef _KWIDGETLISTER_H_ +#define _KWIDGETLISTER_H_ + +#include <qwidget.h> +#include <qptrlist.h> + +class KPushButton; +class QVBoxLayout; +class QHBox; + +/** Simple widget that nonetheless does a lot of the dirty work for + the filter edit widgets (@ref KMSearchpatternEdit and @ref + KMFilterActionEdit). It provides a growable and shrinkable area + where widget may be displayed in rows. Widgets can be added by + hitting the provided 'More' button, removed by the 'Fewer' button + and cleared (e.g. reset, if an derived class implements that and + removed for all but @ref mMinWidgets). + + To use this widget, derive from it with the template changed to + the type of widgets this class should list. Then reimplement @ref + addWidgetAtEnd, @ref removeLastWidget, calling the original + implementation as necessary. Instantate an object of the class and + put it in your dialog. + + @short Widget that manages a list of other widgets (incl. 'more', 'fewer' and 'clear' buttons). + @author Marc Mutz <[email protected]> + @see KMSearchPatternEdit::WidgetLister KMFilterActionEdit::WidgetLister + +*/ + +class KWidgetLister : public QWidget +{ + Q_OBJECT +public: + KWidgetLister( int minWidgets=1, int maxWidgets=8, QWidget* parent=0, const char* name=0 ); + virtual ~KWidgetLister(); + +protected slots: + /** Called whenever the user clicks on the 'more' button. + Reimplementations should call this method, because this + implementation does all the dirty work with adding the widgets + to the layout (through @ref addWidgetAtEnd) and enabling/disabling + the control buttons. */ + virtual void slotMore(); + /** Called whenever the user clicks on the 'fewer' button. + Reimplementations should call this method, because this + implementation does all the dirty work with removing the widgets + from the layout (through @ref removelastWidget) and + enabling/disabling the control buttons. */ + virtual void slotFewer(); + /** Called whenever the user clicks on the 'clear' button. + Reimplementations should call this method, because this + implementation does all the dirty work with removing all but + @ref mMinWidets widgets from the layout and enabling/disabling + the control buttons. */ + virtual void slotClear(); + + + +protected: + /** Adds a single widget. Doesn't care if there are already @ref + mMaxWidgets on screen and whether it should enable/disable any + controls. It simply does what it is asked to do. You want to + reimplement this method if you want to initialize the the widget + when showing it on screen. Make sure you call this + implementaion, though, since you cannot put the widget on screen + from derived classes (@p mLayout is private). + Make sure the parent of the QWidget to add is this KWidgetLister. */ + virtual void addWidgetAtEnd(QWidget *w =0); + /** Removes a single (always the last) widget. Doesn't care if there + are still only @ref mMinWidgets left on screen and whether it + should enable/disable any controls. It simply does what it is + asked to do. You want to reimplement this method if you want to + save the the widget's state before removing it from screen. Make + sure you call this implementaion, though, since you should not + remove the widget from screen from derived classes. */ + virtual void removeLastWidget(); + /** Called to clear a given widget. The default implementation does + nothing. */ + virtual void clearWidget( QWidget* ); + /** Because QT 2.x does not support signals/slots in template + classes, we are forced to emulate this by forcing the + implementers of subclasses of KWidgetLister to reimplement this + function which replaces the "@p new @p T" call. */ + virtual QWidget* createWidget( QWidget *parent ); + /** Sets the number of widgets on scrren to exactly @p aNum. Doesn't + check if @p aNum is inside the range @p + [mMinWidgets,mMaxWidgets]. */ + virtual void setNumberOfShownWidgetsTo( int aNum ); + /** The list of widgets. Note that this list is set to auto-delete, + meaning that widgets that are removed from the screen by either + @ref slotFewer or @ref slotClear will be destroyed! */ + QPtrList<QWidget> mWidgetList; + /** The minimum number of widgets that are to stay on screen. */ + int mMinWidgets; + /** The maximum number of widgets that are to be shown on screen. */ + int mMaxWidgets; + +signals: + /** This signal is emitted whenever a widget was added */ + void widgetAdded(); + /** This signal is emitted whenever a widget was added */ + void widgetAdded(QWidget *); + /** This signal is emitted whenever a widget was removed */ + void widgetRemoved(); + /** This signal is emitted whenever the clear button is clicked */ + void clearWidgets(); + +private: + void enableControls(); + + KPushButton *mBtnMore, *mBtnFewer, *mBtnClear; + QVBoxLayout *mLayout; + QHBox *mButtonBox; +}; + + + +#endif /* _KWIDGETLISTER_H_ */ diff --git a/src/gui/lineedit.cpp b/src/gui/lineedit.cpp new file mode 100644 index 0000000..6248880 --- /dev/null +++ b/src/gui/lineedit.cpp @@ -0,0 +1,150 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "lineedit.h" + +#include <kstdaction.h> +#include <kactioncollection.h> +#include <kspell.h> + +#include <qapplication.h> +#include <qpainter.h> +#include <qpopupmenu.h> + +using Tellico::GUI::LineEdit; + +LineEdit::LineEdit(QWidget* parent_, const char* name_) : KLineEdit(parent_, name_) + , m_drawHint(false) + , m_allowSpellCheck(false) + , m_enableSpellCheck(true) + , m_spell(0) { + m_spellAction = KStdAction::spelling(this, SLOT(slotCheckSpelling()), new KActionCollection(this)); +} + +void LineEdit::clear() { + KLineEdit::clear(); + m_drawHint = true; + repaint(); +} + +void LineEdit::setText(const QString& text_) { + m_drawHint = text_.isEmpty(); + repaint(); + KLineEdit::setText(text_); +} + +void LineEdit::setHint(const QString& hint_) { + m_hint = hint_; + m_drawHint = text().isEmpty(); + repaint(); +} + +void LineEdit::focusInEvent(QFocusEvent* event_) { + if(m_drawHint) { + m_drawHint = false; + repaint(); + } + KLineEdit::focusInEvent(event_); +} + +void LineEdit::focusOutEvent(QFocusEvent* event_) { + if(text().isEmpty()) { + m_drawHint = true; + repaint(); + } + KLineEdit::focusOutEvent(event_); +} + +void LineEdit::drawContents(QPainter* painter_) { + // draw the regular line edit first + KLineEdit::drawContents(painter_); + + // no need to draw anything else if in focus or no hint + if(hasFocus() || !m_drawHint || m_hint.isEmpty() || !text().isEmpty()) { + return; + } + + // save current pen + QPen oldPen = painter_->pen(); + + // follow lead of kdepim and amarok, use disabled text color + painter_->setPen(palette().color(QPalette::Disabled, QColorGroup::Text)); + + QRect rect = contentsRect(); + // again, follow kdepim and amarok lead, and pad by 2 pixels + rect.rLeft() += 2; + painter_->drawText(rect, AlignAuto | AlignVCenter, m_hint); + + // reset pen + painter_->setPen(oldPen); +} + +QPopupMenu* LineEdit::createPopupMenu() { + QPopupMenu* popup = KLineEdit::createPopupMenu(); + + if(!popup) { + return popup; + } + + if(m_allowSpellCheck && echoMode() == QLineEdit::Normal && !isReadOnly()) { + popup->insertSeparator(); + + m_spellAction->plug(popup); + m_spellAction->setEnabled(m_enableSpellCheck && !text().isEmpty()); + } + + return popup; +} + +void LineEdit::slotCheckSpelling() { + delete m_spell; + // re-use the action string to get translations + m_spell = new KSpell(this, m_spellAction->text(), + this, SLOT(slotSpellCheckReady(KSpell*)), 0, true, true); + + connect(m_spell, SIGNAL(death()), + SLOT(spellCheckerFinished())); + connect(m_spell, SIGNAL(misspelling( const QString &, const QStringList &, unsigned int)), + SLOT(spellCheckerMisspelling( const QString &, const QStringList &, unsigned int))); + connect(m_spell, SIGNAL(corrected(const QString &, const QString &, unsigned int)), + SLOT(spellCheckerCorrected(const QString &, const QString &, unsigned int))); +} + +void LineEdit::slotSpellCheckReady(KSpell* spell) { + spell->check(text()); + connect(spell, SIGNAL(done(const QString&)), SLOT(slotSpellCheckDone(const QString&))); +} + +void LineEdit::slotSpellCheckDone(const QString& newText) { + if(newText != text()) { + setText(newText); + } +} + +void LineEdit::spellCheckerFinished() { + delete m_spell; + m_spell = 0; +} + +void LineEdit::spellCheckerMisspelling(const QString &text, const QStringList&, unsigned int pos) { + setSelection(pos, pos + text.length()); +} + +void LineEdit::spellCheckerCorrected(const QString& oldWord, const QString& newWord, unsigned int pos) { + if(oldWord != newWord) { + setSelection(pos, pos + oldWord.length()); + insert(newWord); + } +} + +#include "lineedit.moc" diff --git a/src/gui/lineedit.h b/src/gui/lineedit.h new file mode 100644 index 0000000..af7d81b --- /dev/null +++ b/src/gui/lineedit.h @@ -0,0 +1,72 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICO_GUILINEEDIT_H +#define TELLICO_GUILINEEDIT_H + +#include <klineedit.h> + +#include <qstring.h> + +class KAction; +class KSpell; + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class LineEdit : public KLineEdit { +Q_OBJECT + +public: + LineEdit(QWidget* parent = 0, const char* name = 0); + + virtual void setText(const QString& text); + void setHint(const QString& hint); + + // by default, spell check is not allowed, and no popupmenu item is created + void setAllowSpellCheck(bool b) { m_allowSpellCheck = b; } + // spell check may be allowed but disabled + void setEnableSpellCheck(bool b) { m_enableSpellCheck = b; } + +public slots: + void clear(); + +protected: + virtual void focusInEvent(QFocusEvent* event); + virtual void focusOutEvent(QFocusEvent* event); + virtual void drawContents(QPainter* painter); + virtual QPopupMenu* createPopupMenu(); + +private slots: + void slotCheckSpelling(); + void slotSpellCheckReady(KSpell* spell); + void slotSpellCheckDone(const QString& text); + void spellCheckerMisspelling(const QString& text, const QStringList&, unsigned int pos); + void spellCheckerCorrected(const QString& oldText, const QString& newText, unsigned int pos); + void spellCheckerFinished(); + +private: + QString m_hint; + bool m_drawHint; + KAction* m_spellAction; + bool m_allowSpellCheck; + bool m_enableSpellCheck; + KSpell* m_spell; +}; + + } // end namespace +} // end namespace +#endif diff --git a/src/gui/linefieldwidget.cpp b/src/gui/linefieldwidget.cpp new file mode 100644 index 0000000..1535832 --- /dev/null +++ b/src/gui/linefieldwidget.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "linefieldwidget.h" +#include "../field.h" +#include "../isbnvalidator.h" +#include "../fieldcompletion.h" +#include "../latin1literal.h" +#include "../tellico_kernel.h" +#include "../gui/lineedit.h" + +using Tellico::GUI::LineFieldWidget; + +LineFieldWidget::LineFieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : FieldWidget(field_, parent_, name_) { + + m_lineEdit = new GUI::LineEdit(this); + m_lineEdit->setAllowSpellCheck(true); + m_lineEdit->setEnableSpellCheck(field_->formatFlag() != Data::Field::FormatName); + connect(m_lineEdit, SIGNAL(textChanged(const QString&)), SIGNAL(modified())); + + registerWidget(); + + if(field_->flags() & Data::Field::AllowCompletion) { + FieldCompletion* completion = new FieldCompletion(field_->flags() & Data::Field::AllowMultiple); + completion->setItems(Kernel::self()->valuesByFieldName(field_->name())); + completion->setIgnoreCase(true); + m_lineEdit->setCompletionObject(completion); + m_lineEdit->setAutoDeleteCompletionObject(true); + } + + if(field_->name() == Latin1Literal("isbn")) { + m_lineEdit->setValidator(new ISBNValidator(this)); + } +} + +QString LineFieldWidget::text() const { + QString text = m_lineEdit->text(); + if(field()->flags() & Data::Field::AllowMultiple) { + text.replace(s_semiColon, QString::fromLatin1("; ")); + } + return text.stripWhiteSpace(); +} + +void LineFieldWidget::setText(const QString& text_) { + blockSignals(true); + m_lineEdit->blockSignals(true); + m_lineEdit->setText(text_); + m_lineEdit->blockSignals(false); + blockSignals(false); +} + +void LineFieldWidget::clear() { + m_lineEdit->clear(); + editMultiple(false); +} + +void LineFieldWidget::addCompletionObjectItem(const QString& text_) { + m_lineEdit->completionObject()->addItem(text_); +} + +void LineFieldWidget::updateFieldHook(Data::FieldPtr oldField_, Data::FieldPtr newField_) { + bool wasComplete = (oldField_->flags() & Data::Field::AllowCompletion); + bool isComplete = (newField_->flags() & Data::Field::AllowCompletion); + if(!wasComplete && isComplete) { + FieldCompletion* completion = new FieldCompletion(isComplete); + completion->setItems(Kernel::self()->valuesByFieldName(newField_->name())); + completion->setIgnoreCase(true); + m_lineEdit->setCompletionObject(completion); + m_lineEdit->setAutoDeleteCompletionObject(true); + } else if(wasComplete && !isComplete) { + m_lineEdit->completionObject()->clear(); + } +} + +QWidget* LineFieldWidget::widget() { + return m_lineEdit; +} + +#include "linefieldwidget.moc" diff --git a/src/gui/linefieldwidget.h b/src/gui/linefieldwidget.h new file mode 100644 index 0000000..70dc175 --- /dev/null +++ b/src/gui/linefieldwidget.h @@ -0,0 +1,51 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef LINEFIELDWIDGET_H +#define LINEFIELDWIDGET_H + +#include "fieldwidget.h" +#include "../datavectors.h" + +namespace Tellico { + namespace GUI { + class LineEdit; + +/** + * @author Robby Stephenson + */ +class LineFieldWidget : public FieldWidget { +Q_OBJECT + +public: + LineFieldWidget(Data::FieldPtr field, QWidget* parent, const char* name=0); + virtual ~LineFieldWidget() {} + + virtual QString text() const; + virtual void setText(const QString& text); + virtual void addCompletionObjectItem(const QString& text); + +public slots: + virtual void clear(); + +protected: + virtual QWidget* widget(); + virtual void updateFieldHook(Data::FieldPtr oldField, Data::FieldPtr newField); + +private: + GUI::LineEdit* m_lineEdit; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/listboxtext.cpp b/src/gui/listboxtext.cpp new file mode 100644 index 0000000..8ec6fa2 --- /dev/null +++ b/src/gui/listboxtext.cpp @@ -0,0 +1,74 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : $EMAIL + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "listboxtext.h" +#include "../tellico_utils.h" + +#include <qpainter.h> + +using Tellico::GUI::ListBoxText; + +ListBoxText::ListBoxText(QListBox* listbox_, const QString& text_) + : QListBoxText(listbox_, text_), m_colored(false) { +} + +ListBoxText::ListBoxText(QListBox* listbox_, const QString& text_, QListBoxItem* after_) + : QListBoxText(listbox_, text_, after_), m_colored(false) { +} + +int ListBoxText::width(const QListBox* listbox_) const { + if(m_colored) { + QFont font = listbox_->font(); + font.setBold(true); + font.setItalic(true); + QFontMetrics fm(font); + return fm.width(text()) + 6; + } else { + return QListBoxText::width(listbox_); + } +} + +// I don't want the height to change when colored +// so all the items are at the same level for multi-column boxes +int ListBoxText::height(const QListBox* listbox_) const { + return QListBoxText::height(listbox_); +} + +void ListBoxText::setColored(bool colored_) { + if(m_colored != colored_) { + m_colored = colored_; + listBox()->triggerUpdate(false); + } +} + +void ListBoxText::setText(const QString& text_) { + QListBoxText::setText(text_); + listBox()->triggerUpdate(true); +} + +// mostly copied from QListBoxText::paint() in Qt 3.1.1 +void ListBoxText::paint(QPainter* painter_) { + if(m_colored) { + QFont font = painter_->font(); + font.setBold(true); + font.setItalic(true); + painter_->setFont(font); + if(!isSelected()) { + painter_->setPen(Tellico::contrastColor); + } + } + int itemHeight = height(listBox()); + QFontMetrics fm = painter_->fontMetrics(); + int yPos = ((itemHeight - fm.height()) / 2) + fm.ascent(); + painter_->drawText(3, yPos, text()); +} diff --git a/src/gui/listboxtext.h b/src/gui/listboxtext.h new file mode 100644 index 0000000..3cfe2db --- /dev/null +++ b/src/gui/listboxtext.h @@ -0,0 +1,50 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICO_GUI_LISTBOXTEXT_H +#define TELLICO_GUI_LISTBOXTEXT_H + +#include <qlistbox.h> + +namespace Tellico { + namespace GUI { + +/** + * ListBoxText subclasses QListBoxText so that @ref setText() can be made public, + * and the font color can be changed + * + * @author Robby Stephenson + */ +class ListBoxText : public QListBoxText { +public: + ListBoxText(QListBox* listbox, const QString& text); + ListBoxText(QListBox* listbox, const QString& text, QListBoxItem* after); + + virtual int width(const QListBox* box) const; + virtual int height(const QListBox* box) const; + + bool isColored() const { return m_colored; } + void setColored(bool colored); + void setText(const QString& text); + +protected: + virtual void paint(QPainter* painter); + +private: + bool m_colored; +}; + + } // end namespace +} // end namespace + +#endif diff --git a/src/gui/listview.cpp b/src/gui/listview.cpp new file mode 100644 index 0000000..d93174c --- /dev/null +++ b/src/gui/listview.cpp @@ -0,0 +1,347 @@ +/*************************************************************************** + copyright : (C) 2001-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "listview.h" +#include "../controller.h" +#include "../tellico_utils.h" +#include "../tellico_debug.h" + +#include <kapplication.h> + +#include <qpainter.h> +#include <qpixmap.h> +#include <qheader.h> + +using Tellico::GUI::ListView; +using Tellico::GUI::ListViewItem; + +ListView::ListView(QWidget* parent_, const char* name_) : KListView(parent_, name_/*=0*/), + m_sortStyle(SortByText), m_isClear(true) { + setSelectionMode(QListView::Extended); + connect(this, SIGNAL(selectionChanged()), + SLOT(slotSelectionChanged())); + connect(this, SIGNAL(doubleClicked(QListViewItem*)), + SLOT(slotDoubleClicked(QListViewItem*))); +#if !KDE_IS_VERSION(3,3,90) + m_shadeSortColumn = false; + // call it once to initialize + slotUpdateColors(); +#endif + connect(kapp, SIGNAL(kdisplayPaletteChanged()), SLOT(slotUpdateColors())); + m_comparisons.setAutoDelete(true); +} + +ListView::~ListView() { +} + +void ListView::clearSelection() { + if(m_selectedItems.isEmpty()) { + // nothing to do; + return; + } + bool b = signalsBlocked(); + blockSignals(true); + selectAll(false); + blockSignals(b); + emit selectionChanged(); +} + +void ListView::updateSelected(ListViewItem* item_, bool selected_) { + if(selected_) { + m_selectedItems.append(item_); + } else { + m_selectedItems.removeRef(item_); + } +} + +bool ListView::isSelectable(ListViewItem* item_) const { + // don't allow hidden items to be selected + if(!item_->isVisible()) { + return false; + } + + // selecting multiple items is ok + // only when parent is open. Be careful to check for existence of parent + if(item_->parent() && !item_->parent()->isOpen()) { + return false; + } + + // just selecting a single item is always ok + if(m_selectedItems.isEmpty()) { + return true; + } + + // not allowed is something other than an entry is selected and current is entry + if(m_selectedItems.getFirst()->isEntryItem() != item_->isEntryItem()) { + return false; + } + + return true; +} + +int ListView::firstVisibleColumn() const { + int col = 0; + while(col < columns() && columnWidth(header()->mapToSection(col)) == 0) { + ++col; + } + if(col == columns()) { + return -1; + } + return header()->mapToSection(col); +} + +int ListView::lastVisibleColumn() const { + int col = columns()-1; + while(col < columns() && columnWidth(header()->mapToSection(col)) == 0) { + --col; + } + if(col == columns()) { + return -1; + } + return header()->mapToSection(col); +} + +void ListView::setColumnText(int column, const QString& label) { + ListViewComparison* comp = m_comparisons.take(columnText(column)); + KListView::setColumnText(column, label); + if(comp) { + m_comparisons.insert(columnText(column), comp); + } +} + +void ListView::setComparison(int column, ListViewComparison* comp) { + if(comp) { + m_comparisons.replace(columnText(column), comp); + } +} + +void ListView::removeComparison(int column) { + m_comparisons.remove(columnText(column)); +} + +void ListView::clearComparisons() { + m_comparisons.clear(); +} + +int ListView::compare(int col, const GUI::ListViewItem* item1, GUI::ListViewItem* item2, bool asc) { + if(col >= 0 && col < static_cast<int>(m_comparisons.count())) { + ListViewComparison* com = m_comparisons.find(columnText(col)); + if(com) { + return com->compare(col, item1, item2, asc); + } + } + return 0; +} + +#if !KDE_IS_VERSION(3,3,90) +void ListView::setShadeSortColumn(bool shade_) { + if(m_shadeSortColumn != shade_) { + m_shadeSortColumn = shade_; + repaint(); + } +} +#endif + +void ListView::slotUpdateColors() { +#if !KDE_IS_VERSION(3,3,90) + m_backColor2 = viewport()->colorGroup().base(); + if(m_backColor2 == Qt::black) { + m_backColor2 = QColor(50, 50, 50); // dark gray + } else { + int h,s,v; + m_backColor2.hsv(&h, &s, &v); + if(v > 175) { + m_backColor2 = m_backColor2.dark(105); + } else { + m_backColor2 = m_backColor2.light(120); + } + } + + m_altColor2 = alternateBackground(); + if(m_altColor2 == Qt::black) { + m_altColor2 = QColor(50, 50, 50); // dark gray + } else { + int h,s,v; + m_altColor2.hsv(&h, &s, &v); + if(v > 175) { + m_altColor2 = m_altColor2.dark(105); + } else { + m_altColor2 = m_altColor2.light(120); + } + } +#endif + Tellico::updateContrastColor(viewport()->colorGroup()); + repaint(); +} + +void ListView::slotSelectionChanged() { + if(m_selectedItems.isEmpty()) { + if(m_isClear) { + return; // nothing to do + } + m_isClear = true; + Controller::self()->slotClearSelection(); + return; + } + m_isClear = false; + + Data::EntryVec entries; + // now just find all the children or grandchildren that are entry items + for(GUI::ListViewItemListIt it(m_selectedItems); it.current(); ++it) { + Data::EntryVec more = it.current()->entries(); + for(Data::EntryVecIt entry = more.begin(); entry != more.end(); ++entry) { + if(!entries.contains(entry)) { + entries.append(entry); + } + } + } +// Controller::self()->slotUpdateCurrent(entries); // just update current, don't change selection + Controller::self()->slotUpdateSelection(this, entries); +} + +void ListView::slotDoubleClicked(QListViewItem* item_) { + if(!item_) { + return; + } + + // if it has children, just open it + // but some items delay children creation + if(static_cast<ListViewItem*>(item_)->realChildCount() > 0) { + item_->setOpen(!item_->isOpen()); + } + + GUI::ListViewItem* item = static_cast<GUI::ListViewItem*>(item_); + item->doubleClicked(); +} + +void ListView::drawContentsOffset(QPainter* p, int ox, int oy, int cx, int cy, int cw, int ch) { + bool oldUpdatesEnabled = isUpdatesEnabled(); + setUpdatesEnabled(false); + KListView::drawContentsOffset(p, ox, oy, cx, cy, cw, ch); + setUpdatesEnabled(oldUpdatesEnabled); +} + +/* ****************** ListViewItem ********************* */ + +ListViewItem::~ListViewItem() { + // I think there's a bug in qt where the children of this item are deleted after the item itself + // as a result, there is no listView() pointer for the children, that obvious causes + // a problem with updating the selection. So we MUST call clear() here ourselves! + clear(); + // be sure to remove from selected list when it's deleted + ListView* lv = listView(); + if(lv) { + lv->updateSelected(this, false); + } +} + +void ListViewItem::clear() { + QListViewItem* item = firstChild(); + while(item) { + delete item; + item = firstChild(); + } +} + +int ListViewItem::compare(QListViewItem* item_, int col_, bool asc_) const { + int res = compareWeight(item_, col_, asc_); + if(res != 0) { + return res; + } + res = listView()->compare(col_, this, static_cast<GUI::ListViewItem*>(item_), asc_); + return res == 0 ? KListViewItem::compare(item_, col_, asc_) : res; +} + +int ListViewItem::compareWeight(QListViewItem* item_, int col_, bool asc_) const { + Q_UNUSED(col_); + // I want the sorting to be independent of sort order + GUI::ListViewItem* i = static_cast<GUI::ListViewItem*>(item_); + int res = 0; + if(m_sortWeight < i->sortWeight()) { + res = -1; + } else if(m_sortWeight > i->sortWeight()) { + res = 1; + } + if(asc_) { + res *= -1; // reverse, heavier weights will come first always + } + return res; +} + +void ListViewItem::setSelected(bool s_) { + ListView* lv = listView(); + if(!lv) { + return; + } + if(s_ && !lv->isSelectable(this)) { + return; + } + if(s_ != isSelected()) { + lv->updateSelected(this, s_); + KListViewItem::setSelected(s_); + } +} + +QColor ListViewItem::backgroundColor(int column_) { +#if KDE_IS_VERSION(3,3,90) + return KListViewItem::backgroundColor(column_); +#else + ListView* view = listView(); + if(view->columns() > 1 && view->shadeSortColumn() && column_ == view->sortColumn()) { + return isAlternate() ? view->alternateBackground2() : view->background2(); + } + return isAlternate() ? view->alternateBackground() : view->viewport()->colorGroup().base(); +#endif +} + +void ListViewItem::paintCell(QPainter* p_, const QColorGroup& cg_, + int column_, int width_, int align_) { + // taken from klistview.cpp + // I can't call KListViewItem::paintCell since KListViewItem::backgroundCOlor(int) is + // not virtual. I need to be sure to call ListViewItem::backgroundColor(int); + QColorGroup cg = cg_; + const QPixmap* pm = listView()->viewport()->backgroundPixmap(); + if(pm && !pm->isNull()) { + cg.setBrush(QColorGroup::Base, QBrush(backgroundColor(column_), *pm)); + QPoint o = p_->brushOrigin(); + p_->setBrushOrigin(o.x()-listView()->contentsX(), o.y()-listView()->contentsY()); + } else { + cg.setColor(listView()->viewport()->backgroundMode() == Qt::FixedColor ? + QColorGroup::Background : QColorGroup::Base, + backgroundColor(column_)); + } + + // don't call KListViewItem::paintCell() since that also does alternate painting, etc... + QListViewItem::paintCell(p_, cg, column_, width_, align_); + + // borrowed from amarok, draw line to left of cell + if(!isSelected()) { + p_->setPen(QPen(listView()->alternateBackground(), 0, Qt::SolidLine)); + p_->drawLine(width_-1, 0, width_-1, height()-1); + } +} + +Tellico::Data::EntryVec ListViewItem::entries() const { + Data::EntryVec entries; + for(QListViewItem* child = firstChild(); child; child = child->nextSibling()) { + Data::EntryVec more = static_cast<GUI::ListViewItem*>(child)->entries(); + for(Data::EntryVecIt entry = more.begin(); entry != more.end(); ++entry) { + if(!entries.contains(entry)) { + entries.append(entry); + } + } + } + return entries; +} + +#include "listview.moc" diff --git a/src/gui/listview.h b/src/gui/listview.h new file mode 100644 index 0000000..4c5ade7 --- /dev/null +++ b/src/gui/listview.h @@ -0,0 +1,180 @@ +/*************************************************************************** + copyright : (C) 2001-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICO_LISTVIEW_H +#define TELLICO_LISTVIEW_H + +#include "../datavectors.h" +#include "../listviewcomparison.h" + +#include <klistview.h> +#include <kdeversion.h> + +#include <qdict.h> + +namespace Tellico { + class ListViewComparison; + namespace GUI { + +class ListViewItem; +typedef QPtrList<ListViewItem> ListViewItemList; +typedef QPtrListIterator<ListViewItem> ListViewItemListIt; + +/** + * A ListView keeps track of the selected items and allows subclasses to determine + * whether child items may be selected or not. In addition, it provides alternating + * background colors and shaded sort columns. + * + * @see GroupView + * @see DetailedListView + * + * @author Robby Stephenson + */ +class ListView : public KListView { +Q_OBJECT + +friend class ListViewItem; // needed so the ListViewItem d'tor can update selection list + +public: + enum SortStyle { + SortByText = 0, // don't change these values, they're saved as ints in the config + SortByCount = 1 + }; + + ListView(QWidget* parent, const char* name = 0); + virtual ~ListView(); + + void clearSelection(); + /** + * Returns a list of the currently selected items; + * + * @return The list of selected items + */ + const ListViewItemList& selectedItems() const { return m_selectedItems; } + /** + * Used to determine whether an item may be selected without having to setSelectable on + * every child item. + * + * @return Whether the item may be selected + */ + virtual bool isSelectable(ListViewItem*) const; + SortStyle sortStyle() const { return m_sortStyle; } + void setSortStyle(SortStyle style) { m_sortStyle = style; } + + int firstVisibleColumn() const; + int lastVisibleColumn() const; + + virtual void setColumnText(int column, const QString& label); + + void setComparison(int column, ListViewComparison* comp); + void removeComparison(int column); + void clearComparisons(); + virtual int compare(int col, const GUI::ListViewItem* item1, GUI::ListViewItem* item2, bool asc); + +#if !KDE_IS_VERSION(3,3,90) + // taken from KDE bug 59791 + void setShadeSortColumn(bool shade_); + bool shadeSortColumn() const { return m_shadeSortColumn; } + const QColor& background2() const { return m_backColor2; } + const QColor& alternateBackground2() const { return m_altColor2; } +#endif + +protected slots: + /** + * Handles everything when an item is selected. The proper signal is emitted, depending + * on whether the item refers to a collection, a group, or a entry. + */ + virtual void slotSelectionChanged(); + +protected: + virtual void drawContentsOffset(QPainter* p, int ox, int oy, int cx, int cy, int cw, int ch); + +private slots: + void slotUpdateColors(); + void slotDoubleClicked(QListViewItem* item); + +private: + /** + * Updates the pointer list. + * + * @param item The item being selected or deselected + * @param s Selected or not + */ + void updateSelected(ListViewItem* item, bool s); + + SortStyle m_sortStyle; + bool m_isClear; + ListViewItemList m_selectedItems; + QDict<ListViewComparison> m_comparisons; +#if !KDE_IS_VERSION(3,3,90) + bool m_shadeSortColumn; + QColor m_backColor2; + QColor m_altColor2; +#endif +}; + +/** + * The ListViewItem keeps track of what kind of specialized listview item it is, as + * well as taking care of the selection tracking. + * + * @author Robby Stephenson + */ +class ListViewItem : public KListViewItem { +public: + ListViewItem(ListView* parent) : KListViewItem(parent), m_sortWeight(-1) {} + ListViewItem(ListView* parent, QListViewItem* after) : KListViewItem(parent, after), m_sortWeight(-1) {} + ListViewItem(ListViewItem* parent) : KListViewItem(parent), m_sortWeight(-1) {} + ListViewItem(ListView* parent, const QString& text) : KListViewItem(parent, text), m_sortWeight(-1) {} + ListViewItem(ListViewItem* parent, const QString& text) : KListViewItem(parent, text), m_sortWeight(-1) {} + virtual ~ListViewItem(); + + virtual int realChildCount() const { return childCount(); } + virtual void clear(); + + virtual bool isEntryGroupItem() const { return false; } + virtual bool isEntryItem() const { return false; } + virtual bool isFilterItem() const { return false; } + virtual bool isBorrowerItem() const { return false; } + virtual bool isLoanItem() const { return false; } + + int sortWeight() const { return m_sortWeight; } + void setSortWeight(int w) { m_sortWeight = w; } + virtual int compare(QListViewItem* item, int col, bool ascending) const; + + virtual void setSelected(bool selected); + /** + * Returns the background color for the column, which depends on whether the item + * is an alternate row and whether the column is selected. + * + * @param column The column number + * @param alternate The alternate row color can be forced + */ + virtual QColor backgroundColor(int column); // not virtual in KListViewItem!!! + virtual void paintCell(QPainter* painter, const QColorGroup& colorGroup, + int column, int width, int alignment); + + ListView* listView () const { return static_cast<ListView*>(KListViewItem::listView()); } + + virtual void doubleClicked() {} + virtual Data::EntryVec entries() const; + +private: + int compareWeight(QListViewItem* item, int col, bool ascending) const; + + int m_sortWeight; +}; + + } // end namespace +} // end namespace + +#endif diff --git a/src/gui/numberfieldwidget.cpp b/src/gui/numberfieldwidget.cpp new file mode 100644 index 0000000..027b1e9 --- /dev/null +++ b/src/gui/numberfieldwidget.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "numberfieldwidget.h" +#include "datewidget.h" +#include "../field.h" + +#include <klineedit.h> + +#include <qlayout.h> +#include <qvalidator.h> + +using Tellico::GUI::NumberFieldWidget; + +NumberFieldWidget::NumberFieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : FieldWidget(field_, parent_, name_), m_lineEdit(0), m_spinBox(0) { + + if(field_->flags() & Data::Field::AllowMultiple) { + initLineEdit(); + } else { + initSpinBox(); + } + + registerWidget(); +} + +void NumberFieldWidget::initLineEdit() { + m_lineEdit = new KLineEdit(this); + connect(m_lineEdit, SIGNAL(textChanged(const QString&)), SIGNAL(modified())); + // connect(kl, SIGNAL(returnPressed(const QString&)), this, SLOT(slotHandleReturn())); + + // regexp is any number of digits followed optionally by any number of + // groups of a semi-colon followed optionally by a space, followed by digits + QRegExp rx(QString::fromLatin1("-?\\d*(; ?-?\\d*)*")); + m_lineEdit->setValidator(new QRegExpValidator(rx, this)); +} + +void NumberFieldWidget::initSpinBox() { + // intentionally allow only positive numbers + m_spinBox = new GUI::SpinBox(-1, INT_MAX, this); + connect(m_spinBox, SIGNAL(valueChanged(int)), SIGNAL(modified())); + // QSpinBox doesn't emit valueChanged if you edit the value with + // the lineEdit until you change the keyboard focus + connect(m_spinBox->child("qt_spinbox_edit"), SIGNAL(textChanged(const QString&)), SIGNAL(modified())); + // I want to allow no value, so set space as special text. Empty text is ignored + m_spinBox->setSpecialValueText(QChar(' ')); +} + +QString NumberFieldWidget::text() const { + if(isSpinBox()) { + // minValue = special empty text + if(m_spinBox->value() > m_spinBox->minValue()) { + return QString::number(m_spinBox->value()); + } + return QString(); + } + + QString text = m_lineEdit->text(); + if(field()->flags() & Data::Field::AllowMultiple) { + text.replace(s_semiColon, QString::fromLatin1("; ")); + } + return text.simplifyWhiteSpace(); +} + +void NumberFieldWidget::setText(const QString& text_) { + blockSignals(true); + + if(isSpinBox()) { + bool ok; + int n = text_.toInt(&ok); + if(ok) { + // did just allow positive + if(n < 0) { + m_spinBox->setMinValue(INT_MIN+1); + } + m_spinBox->blockSignals(true); + m_spinBox->setValue(n); + m_spinBox->blockSignals(false); + } + } else { + m_lineEdit->blockSignals(true); + m_lineEdit->setText(text_); + m_lineEdit->blockSignals(false); + } + + blockSignals(false); +} + +void NumberFieldWidget::clear() { + if(isSpinBox()) { + // show empty special value text + m_spinBox->setValue(m_spinBox->minValue()); + } else { + m_lineEdit->clear(); + } + editMultiple(false); +} + +void NumberFieldWidget::updateFieldHook(Data::FieldPtr, Data::FieldPtr newField_) { + bool wasLineEdit = !isSpinBox(); + bool nowLineEdit = newField_->flags() & Data::Field::AllowMultiple; + + if(wasLineEdit == nowLineEdit) { + return; + } + + QString value = text(); + if(wasLineEdit && !nowLineEdit) { + layout()->remove(m_lineEdit); + delete m_lineEdit; + m_lineEdit = 0; + initSpinBox(); + } else if(!wasLineEdit && nowLineEdit) { + layout()->remove(m_spinBox); + delete m_spinBox; + m_spinBox = 0; + initLineEdit(); + } + + // should really be FIELD_EDIT_WIDGET_INDEX from fieldwidget.cpp + static_cast<QBoxLayout*>(layout())->insertWidget(2, widget(), 1 /*stretch*/); + widget()->show(); + setText(value); +} + +QWidget* NumberFieldWidget::widget() { + if(isSpinBox()) { + return m_spinBox; + } + return m_lineEdit; +} + +#include "numberfieldwidget.moc" diff --git a/src/gui/numberfieldwidget.h b/src/gui/numberfieldwidget.h new file mode 100644 index 0000000..c0fed9d --- /dev/null +++ b/src/gui/numberfieldwidget.h @@ -0,0 +1,57 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef NUMBERFIELDWIDGET_H +#define NUMBERFIELDWIDGET_H + +class KLineEdit; + +#include "fieldwidget.h" +#include "../datavectors.h" + +namespace Tellico { + namespace GUI { + class SpinBox; + +/** + * @author Robby Stephenson + */ +class NumberFieldWidget : public FieldWidget { +Q_OBJECT + +public: + NumberFieldWidget(Data::FieldPtr field, QWidget* parent, const char* name=0); + virtual ~NumberFieldWidget() {} + + virtual QString text() const; + virtual void setText(const QString& text); + +public slots: + virtual void clear(); + +protected: + virtual QWidget* widget(); + virtual void updateFieldHook(Data::FieldPtr oldField, Data::FieldPtr newField); + +private: + bool isSpinBox() const { return (m_spinBox != 0); } + void initLineEdit(); + void initSpinBox(); + + KLineEdit* m_lineEdit; + SpinBox* m_spinBox; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/overlaywidget.cpp b/src/gui/overlaywidget.cpp new file mode 100644 index 0000000..6214ca8 --- /dev/null +++ b/src/gui/overlaywidget.cpp @@ -0,0 +1,104 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "overlaywidget.h" + +#include <qlayout.h> + +using Tellico::GUI::OverlayWidget; + +OverlayWidget::OverlayWidget(QWidget* parent, QWidget* anchor) : QFrame(parent) + , m_anchor(anchor) + , m_corner(TopRight) { + m_anchor->installEventFilter(this); + reposition(); + hide(); +} + +void OverlayWidget::setCorner(Corner corner_) { + if(corner_ == m_corner) { + return; + } + m_corner = corner_; + reposition(); +} + +void OverlayWidget::addWidget(QWidget* widget_) { + layout()->add(widget_); + adjustSize(); +} + +void OverlayWidget::reposition() { + if(!m_anchor) { + return; + } + + setMaximumSize(parentWidget()->size()); + adjustSize(); + + QPoint p; + + switch(m_corner) { + case BottomLeft: + p.setX(0); + p.setY(m_anchor->height()); + break; + + case BottomRight: + p.setX(m_anchor->width() - width()); + p.setY(m_anchor->height()); + break; + + case TopLeft: + p.setX(0); + p.setY(-1 * height()); + break; + + case TopRight: + p.setX(m_anchor->width() - width()); + p.setY(-1 * height()); + } + + // Position in the toplevelwidget's coordinates + QPoint pTopLevel = m_anchor->mapTo(topLevelWidget(), p); + // Position in the widget's parentWidget coordinates + QPoint pParent = parentWidget()->mapFrom(topLevelWidget(), pTopLevel); + // keep it on the screen + if(pParent.x() < 0) { + pParent.rx() = 0; + } + move(pParent); +} + +bool OverlayWidget::eventFilter(QObject* object_, QEvent* event_) { + if(object_ == m_anchor && (event_->type() == QEvent::Move || event_->type() == QEvent::Resize)) { + reposition(); + } + + return QFrame::eventFilter(object_, event_); +} + +void OverlayWidget::resizeEvent(QResizeEvent* event_) { + reposition(); + QFrame::resizeEvent(event_); +} + +bool OverlayWidget::event(QEvent* event_) { + if(event_->type() == QEvent::ChildInserted) { + adjustSize(); + } + + return QFrame::event(event_); +} + +#include "overlaywidget.moc" diff --git a/src/gui/overlaywidget.h b/src/gui/overlaywidget.h new file mode 100644 index 0000000..7b4453e --- /dev/null +++ b/src/gui/overlaywidget.h @@ -0,0 +1,54 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +// much of this code is adapted from libkdepim +// which is GPL licensed, Copyright (c) 2004 David Faure + +#ifndef TELLICO_GUI_OVERLAYWIDGET_H +#define TELLICO_GUI_OVERLAYWIDGET_H + +#include <qframe.h> + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class OverlayWidget : public QFrame { +Q_OBJECT + +public: + OverlayWidget(QWidget* parent, QWidget* anchor); + + void setCorner(Corner corner); + Corner corner() const { return m_corner; } + + void addWidget(QWidget* widget); + +protected: + void resizeEvent(QResizeEvent* event); + bool eventFilter(QObject* object, QEvent* event); + bool event(QEvent* event); + +private: + void reposition(); + + QWidget* m_anchor; + Corner m_corner; +}; + + } // end namespace +} // end namespace + +#endif diff --git a/src/gui/parafieldwidget.cpp b/src/gui/parafieldwidget.cpp new file mode 100644 index 0000000..9941e34 --- /dev/null +++ b/src/gui/parafieldwidget.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "parafieldwidget.h" +#include "../field.h" +#include "../latin1literal.h" + +#include <ktextedit.h> + +using Tellico::GUI::ParaFieldWidget; + +ParaFieldWidget::ParaFieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : FieldWidget(field_, parent_, name_) { + + m_textEdit = new KTextEdit(this); + m_textEdit->setTextFormat(Qt::PlainText); + if(field_->property(QString::fromLatin1("spellcheck")) != Latin1Literal("false")) { + m_textEdit->setCheckSpellingEnabled(true); + } + connect(m_textEdit, SIGNAL(textChanged()), SIGNAL(modified())); + + registerWidget(); +} + +QString ParaFieldWidget::text() const { + QString text = m_textEdit->text(); + text.replace('\n', QString::fromLatin1("<br/>")); + return text; +} + +void ParaFieldWidget::setText(const QString& text_) { + blockSignals(true); + m_textEdit->blockSignals(true); + + QRegExp rx(QString::fromLatin1("<br/?>"), false /*case-sensitive*/); + QString s = text_; + s.replace(rx, QChar('\n')); + m_textEdit->setText(s); + + m_textEdit->blockSignals(false); + blockSignals(false); +} + +void ParaFieldWidget::clear() { + m_textEdit->clear(); + editMultiple(false); +} + +QWidget* ParaFieldWidget::widget() { + return m_textEdit; +} + +#include "parafieldwidget.moc" diff --git a/src/gui/parafieldwidget.h b/src/gui/parafieldwidget.h new file mode 100644 index 0000000..ac9801f --- /dev/null +++ b/src/gui/parafieldwidget.h @@ -0,0 +1,50 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef PARAFIELDWIDGET_H +#define PARAFIELDWIDGET_H + +#include "fieldwidget.h" +#include "../datavectors.h" + +class KTextEdit; + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class ParaFieldWidget : public FieldWidget { +Q_OBJECT + +public: + ParaFieldWidget(Data::FieldPtr field, QWidget* parent, const char* name=0); + virtual ~ParaFieldWidget() {} + + virtual QString text() const; + virtual void setText(const QString& text); + +public slots: + virtual void clear(); + +protected: + virtual QWidget* widget(); + +private: + KTextEdit* m_textEdit; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/previewdialog.cpp b/src/gui/previewdialog.cpp new file mode 100644 index 0000000..4c0d8e1 --- /dev/null +++ b/src/gui/previewdialog.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + copyright : (C) 2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "previewdialog.h" +#include "../entryview.h" +#include "../entry.h" +#include "../imagefactory.h" // for StyleOptions + +#include <klocale.h> +#include <ktempdir.h> +#include <khtmlview.h> + +using Tellico::GUI::PreviewDialog; + +PreviewDialog::PreviewDialog(QWidget* parent_) + : KDialogBase(parent_, "template preview dialog", false /* modal */, + i18n("Template Preview"), KDialogBase::Ok) + , m_tempDir(new KTempDir()) { + m_tempDir->setAutoDelete(true); + connect(this, SIGNAL(finished()), SLOT(delayedDestruct())); + + m_view = new EntryView(this); + setMainWidget(m_view->view()); + setInitialSize(QSize(600, 500)); +} + +PreviewDialog::~PreviewDialog() { + delete m_tempDir; + m_tempDir = 0; +} + +void PreviewDialog::setXSLTFile(const QString& file_) { + m_view->setXSLTFile(file_); +} + +void PreviewDialog::setXSLTOptions(StyleOptions options_) { + options_.imgDir = m_tempDir->name(); // images always get written to temp dir + ImageFactory::createStyleImages(options_); + m_view->setXSLTOptions(options_); +} + +void PreviewDialog::showEntry(Data::EntryPtr entry_) { + m_view->showEntry(entry_); +} + +#include "previewdialog.moc" diff --git a/src/gui/previewdialog.h b/src/gui/previewdialog.h new file mode 100644 index 0000000..a386a94 --- /dev/null +++ b/src/gui/previewdialog.h @@ -0,0 +1,47 @@ +/*************************************************************************** + copyright : (C) 2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICO_GUI_PREVIEWDIALOG_H +#define TELLICO_GUI_PREVIEWDIALOG_H + +#include <kdialogbase.h> + +#include "../datavectors.h" + +class KTempDir; + +namespace Tellico { + class EntryView; + class StyleOptions; + + namespace GUI { + +class PreviewDialog : public KDialogBase { +Q_OBJECT + +public: + PreviewDialog(QWidget* parent); + ~PreviewDialog(); + + void setXSLTFile(const QString& file); + void setXSLTOptions(StyleOptions options); + void showEntry(Data::EntryPtr entry); + +private: + KTempDir* m_tempDir; + EntryView* m_view; +}; + + } +} +#endif diff --git a/src/gui/progress.cpp b/src/gui/progress.cpp new file mode 100644 index 0000000..36baaf1 --- /dev/null +++ b/src/gui/progress.cpp @@ -0,0 +1,33 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "progress.h" +#include "../tellico_debug.h" + +using Tellico::GUI::Progress; + +Progress::Progress(QWidget* parent_) : KProgress(parent_) { +} + +Progress::Progress(int totalSteps_, QWidget* parent_) : KProgress(totalSteps_, parent_) { +} + +bool Progress::isDone() const { + return progress() == totalSteps(); +} + +void Progress::setDone() { + setProgress(totalSteps()); +} + +#include "progress.moc" diff --git a/src/gui/progress.h b/src/gui/progress.h new file mode 100644 index 0000000..b58de2f --- /dev/null +++ b/src/gui/progress.h @@ -0,0 +1,39 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICO_GUI_PROGRESS_H +#define TELLICO_GUI_PROGRESS_H + +#include <kprogress.h> + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class Progress : public KProgress { +Q_OBJECT + +public: + Progress(QWidget* parent); + Progress(int totalSteps, QWidget* parent); + + bool isDone() const; + void setDone(); +}; + + } // end namespace +} // end namespace + +#endif diff --git a/src/gui/ratingfieldwidget.cpp b/src/gui/ratingfieldwidget.cpp new file mode 100644 index 0000000..4b0de71 --- /dev/null +++ b/src/gui/ratingfieldwidget.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "ratingfieldwidget.h" +#include "ratingwidget.h" +#include "../field.h" + +using Tellico::GUI::RatingFieldWidget; + +RatingFieldWidget::RatingFieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : FieldWidget(field_, parent_, name_) { + m_rating = new RatingWidget(field_, this); + connect(m_rating, SIGNAL(modified()), SIGNAL(modified())); + + registerWidget(); +} + +QString RatingFieldWidget::text() const { + return m_rating->text(); +} + +void RatingFieldWidget::setText(const QString& text_) { + blockSignals(true); + + m_rating->blockSignals(true); + m_rating->setText(text_); + m_rating->blockSignals(false); + + blockSignals(false); + + if(m_rating->text() != text_) { + emit modified(); + } +} + +void RatingFieldWidget::clear() { + m_rating->clear(); + editMultiple(false); +} + +void RatingFieldWidget::updateFieldHook(Data::FieldPtr, Data::FieldPtr newField_) { + m_rating->updateField(newField_); +} + +QWidget* RatingFieldWidget::widget() { + return m_rating; +} + +#include "ratingfieldwidget.moc" diff --git a/src/gui/ratingfieldwidget.h b/src/gui/ratingfieldwidget.h new file mode 100644 index 0000000..369dbec --- /dev/null +++ b/src/gui/ratingfieldwidget.h @@ -0,0 +1,50 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef RATINGFIELDWIDGET_H +#define RATINGFIELDWIDGET_H + +#include "fieldwidget.h" +#include "../datavectors.h" + +namespace Tellico { + namespace GUI { + class RatingWidget; + +/** + * @author Robby Stephenson + */ +class RatingFieldWidget : public FieldWidget { +Q_OBJECT + +public: + RatingFieldWidget(Data::FieldPtr field, QWidget* parent, const char* name=0); + virtual ~RatingFieldWidget() {} + + virtual QString text() const; + virtual void setText(const QString& text); + +public slots: + virtual void clear(); + +protected: + virtual QWidget* widget(); + virtual void updateFieldHook(Data::FieldPtr oldField, Data::FieldPtr newField); + +private: + RatingWidget* m_rating; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/ratingwidget.cpp b/src/gui/ratingwidget.cpp new file mode 100644 index 0000000..4921f21 --- /dev/null +++ b/src/gui/ratingwidget.cpp @@ -0,0 +1,170 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "ratingwidget.h" +#include "../field.h" +#include "../latin1literal.h" +#include "../tellico_utils.h" +#include "../tellico_debug.h" + +#include <kiconloader.h> + +#include <qintdict.h> +#include <qlayout.h> + +namespace { + static const int RATING_WIDGET_MAX_ICONS = 10; // same as in Field::ratingValues() + static const int RATING_WIDGET_MAX_STAR_SIZE = 24; +} + +using Tellico::GUI::RatingWidget; + +const QPixmap& RatingWidget::pixmap(const QString& value_) { + static QIntDict<QPixmap> pixmaps; + if(pixmaps.isEmpty()) { + pixmaps.insert(-1, new QPixmap()); + } + bool ok; + int n = Tellico::toUInt(value_, &ok); + if(!ok || n < 1 || n > 10) { + return *pixmaps[-1]; + } + if(pixmaps[n]) { + return *pixmaps[n]; + } + + QString picName = QString::fromLatin1("stars%1").arg(n); + QPixmap* pix = new QPixmap(UserIcon(picName)); + pixmaps.insert(n, pix); + return *pix; +} + +RatingWidget::RatingWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : QHBox(parent_, name_), m_field(field_), m_currIndex(-1) { + m_pixOn = UserIcon(QString::fromLatin1("star_on")); + m_pixOff = UserIcon(QString::fromLatin1("star_off")); + setSpacing(0); + + // find maximum width and height + int w = QMAX(RATING_WIDGET_MAX_STAR_SIZE, QMAX(m_pixOn.width(), m_pixOff.width())); + int h = QMAX(RATING_WIDGET_MAX_STAR_SIZE, QMAX(m_pixOn.height(), m_pixOff.height())); + for(int i = 0; i < RATING_WIDGET_MAX_ICONS; ++i) { + QLabel* l = new QLabel(this); + l->setFixedSize(w, h); + m_widgets.append(l); + } + init(); + + QBoxLayout* l = dynamic_cast<QBoxLayout*>(layout()); + if(l) { + l->addStretch(1); + } +} + +void RatingWidget::init() { + updateBounds(); + m_total = QMIN(m_max, static_cast<int>(m_widgets.count())); + uint i = 0; + for( ; static_cast<int>(i) < m_total; ++i) { + m_widgets.at(i)->setPixmap(m_pixOff); + } + for( ; i < m_widgets.count(); ++i) { + m_widgets.at(i)->setPixmap(QPixmap()); + } + update(); +} + +void RatingWidget::updateBounds() { + bool ok; // not used; + m_min = Tellico::toUInt(m_field->property(QString::fromLatin1("minimum")), &ok); + m_max = Tellico::toUInt(m_field->property(QString::fromLatin1("maximum")), &ok); + if(m_max > RATING_WIDGET_MAX_ICONS) { + myDebug() << "RatingWidget::updateBounds() - max is too high: " << m_max << endl; + m_max = RATING_WIDGET_MAX_ICONS; + } + if(m_min < 1) { + m_min = 1; + } +} + +void RatingWidget::update() { + int i = 0; + for( ; i <= m_currIndex; ++i) { + m_widgets.at(i)->setPixmap(m_pixOn); + } + for( ; i < m_total; ++i) { + m_widgets.at(i)->setPixmap(m_pixOff); + } + + QHBox::update(); +} + +void RatingWidget::mousePressEvent(QMouseEvent* event_) { + // only react to left button + if(event_->button() != Qt::LeftButton) { + return; + } + + int idx; + QWidget* child = childAt(event_->pos()); + if(child) { + idx = m_widgets.findRef(static_cast<QLabel*>(child)); + // if the widget is clicked beyond the maximum value, clear it + // remember total and min are values, but index is zero-based! + if(idx > m_total-1) { + idx = -1; + } else if(idx < m_min-1) { + idx = m_min-1; // limit to minimum, remember index is zero-based + } + } else { + idx = -1; + } + if(m_currIndex != idx) { + m_currIndex = idx; + update(); + emit modified(); + } +} + +void RatingWidget::clear() { + m_currIndex = -1; + update(); +} + +QString RatingWidget::text() const { + // index is index of the list, which is zero-based. Add 1! + return m_currIndex == -1 ? QString::null : QString::number(m_currIndex+1); +} + +void RatingWidget::setText(const QString& text_) { + bool ok; + // text is value, subtract one to get index + m_currIndex = Tellico::toUInt(text_, &ok)-1; + if(ok) { + if(m_currIndex > m_total-1) { + m_currIndex = -1; + } else if(m_currIndex < m_min-1) { + m_currIndex = m_min-1; // limit to minimum, remember index is zero-based + } + } else { + m_currIndex = -1; + } + update(); +} + +void RatingWidget::updateField(Data::FieldPtr field_) { + m_field = field_; + init(); +} + +#include "ratingwidget.moc" diff --git a/src/gui/ratingwidget.h b/src/gui/ratingwidget.h new file mode 100644 index 0000000..99665b3 --- /dev/null +++ b/src/gui/ratingwidget.h @@ -0,0 +1,75 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef RATINGWIDGET_H +#define RATINGWIDGET_H + +#include "../datavectors.h" + +#include <qhbox.h> +#include <qptrlist.h> +#include <qlabel.h> +#include <qpixmap.h> + +namespace Tellico { + namespace Data { + class Field; + } + namespace GUI { + +/** + * @author Robby Stephenson + */ +class RatingWidget : public QHBox { +Q_OBJECT + +typedef QPtrList<QLabel> LabelList; + +public: + RatingWidget(Data::FieldPtr field, QWidget* parent, const char* name = 0); + + void clear(); + QString text() const; + void setText(const QString& text); + void updateField(Data::FieldPtr field); + + static const QPixmap& pixmap(const QString& value); + +public slots: + void update(); + +signals: + void modified(); + +protected: + virtual void mousePressEvent(QMouseEvent* e); + +private: + void init(); + void updateBounds(); + + Data::ConstFieldPtr m_field; + LabelList m_widgets; + + int m_currIndex; + int m_total; + int m_min; + int m_max; + + QPixmap m_pixOn; + QPixmap m_pixOff; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/richtextlabel.cpp b/src/gui/richtextlabel.cpp new file mode 100644 index 0000000..a4cdde4 --- /dev/null +++ b/src/gui/richtextlabel.cpp @@ -0,0 +1,47 @@ +/*************************************************************************** + copyright : (C) 2001-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "richtextlabel.h" + +#include <kdebug.h> + +#include <qlayout.h> + +using Tellico::GUI::RichTextLabel; + +RichTextLabel::RichTextLabel(QWidget* parent) : QTextEdit(parent) { + init(); +} + +RichTextLabel::RichTextLabel(const QString& text, QWidget* parent) : QTextEdit(text, QString::null, parent) { + init(); +} + +QSize RichTextLabel::sizeHint() const { + return minimumSizeHint(); +} + +void RichTextLabel::init() { + setReadOnly(true); + setTextFormat(Qt::RichText); + + setFrameShape(QFrame::NoFrame); + viewport()->setMouseTracking(false); + + setPaper(colorGroup().background()); + + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); + viewport()->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); +} + +#include "richtextlabel.moc" diff --git a/src/gui/richtextlabel.h b/src/gui/richtextlabel.h new file mode 100644 index 0000000..f45a328 --- /dev/null +++ b/src/gui/richtextlabel.h @@ -0,0 +1,46 @@ +/*************************************************************************** + copyright : (C) 2001-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICO_GUI_RICHTEXTLABEL_H +#define TELLICO_GUI_RICHTEXTLABEL_H + +#include <qtextedit.h> + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class RichTextLabel : public QTextEdit { +Q_OBJECT + +public: + RichTextLabel(QWidget* parent); + RichTextLabel(const QString& text, QWidget* parent); + + virtual QSize sizeHint() const; + +private: + void init(); + // redefine these to disable selection + void contentsMousePressEvent(QMouseEvent*) {} + void contentsMouseMoveEvent(QMouseEvent*) {} + void contentsMouseReleaseEvent(QMouseEvent*) {} + void contentsMouseDoubleClickEvent(QMouseEvent*) {} +}; + + } // end namespace +} // end namespace + +#endif diff --git a/src/gui/stringmapdialog.cpp b/src/gui/stringmapdialog.cpp new file mode 100644 index 0000000..4a5374f --- /dev/null +++ b/src/gui/stringmapdialog.cpp @@ -0,0 +1,125 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "stringmapdialog.h" + +#include <klistview.h> +#include <klocale.h> +#include <klineedit.h> +#include <kbuttonbox.h> +#include <kiconloader.h> + +#include <qlayout.h> +#include <qheader.h> +#include <qhbox.h> +#include <qwhatsthis.h> +#include <qpushbutton.h> + +using Tellico::StringMapDialog; + +StringMapDialog::StringMapDialog(const QMap<QString, QString>& map_, QWidget* parent_, const char* name_/*=0*/, bool modal_/*=false*/) + : KDialogBase(parent_, name_, modal_, QString::null, Ok|Cancel) { + QWidget* page = new QWidget(this); + QVBoxLayout* l = new QVBoxLayout(page, 0, KDialog::spacingHint()); + + m_listView = new KListView(page); + m_listView->setAllColumnsShowFocus(true); + m_listView->setShowSortIndicator(true); + m_listView->addColumn(QString::null); + m_listView->addColumn(QString::null); + m_listView->header()->hide(); // hide header since neither column has a label initially + m_listView->setColumnWidthMode(0, QListView::Maximum); + m_listView->setColumnWidthMode(1, QListView::Maximum); + m_listView->setResizeMode(QListView::AllColumns); + connect(m_listView, SIGNAL(currentChanged(QListViewItem*)), SLOT(slotUpdate(QListViewItem*))); + connect(m_listView, SIGNAL(clicked(QListViewItem*)), SLOT(slotUpdate(QListViewItem*))); + l->addWidget(m_listView); + + QHBox* box = new QHBox(page); + box->setMargin(4); + box->setSpacing(KDialog::spacingHint()); + + m_edit1 = new KLineEdit(box); + m_edit1->setFocus(); + m_edit2 = new KLineEdit(box); + KButtonBox* bb = new KButtonBox(box); + bb->addStretch(); + QPushButton* btn = bb->addButton(i18n("&Set"), this, SLOT(slotAdd())); + btn->setIconSet(BarIcon(QString::fromLatin1("filenew"), KIcon::SizeSmall)); + btn = bb->addButton(i18n("&Delete"), this, SLOT(slotDelete())); + btn->setIconSet(BarIcon(QString::fromLatin1("editdelete"), KIcon::SizeSmall)); + + l->addWidget(box); + l->addStretch(1); + setMainWidget(page); + + for(QMap<QString, QString>::ConstIterator it = map_.begin(); it != map_.end(); ++it) { + if(!it.data().isEmpty()) { + (void) new KListViewItem(m_listView, it.key(), it.data()); + } + } + + setMinimumWidth(400); + enableButtonSeparator(true); +} + +void StringMapDialog::slotAdd() { + QString s1 = m_edit1->text(); + QString s2 = m_edit2->text(); + if(s1.isEmpty() && s2.isEmpty()) { + return; + } + QListViewItem* item = m_listView->currentItem(); + if(item && s1 == item->text(0)) { // only update values if same key + item->setText(1, s2); + } else { + item = new KListViewItem(m_listView, s1, s2); + } + m_listView->ensureItemVisible(item); + m_listView->setSelected(item, true); + m_listView->setCurrentItem(item); +} + +void StringMapDialog::slotDelete() { + delete m_listView->currentItem(); + m_edit1->clear(); + m_edit2->clear(); + m_listView->setSelected(m_listView->currentItem(), true); +} + +void StringMapDialog::slotUpdate(QListViewItem* item_) { + if(item_) { + m_edit1->setText(item_->text(0)); + m_edit2->setText(item_->text(1)); + m_listView->header()->adjustHeaderSize(); + } else { + m_edit1->clear(); + m_edit2->clear(); + } +} + +void StringMapDialog::setLabels(const QString& label1_, const QString& label2_) { + m_listView->header()->setLabel(0, label1_); + m_listView->header()->setLabel(1, label2_); + m_listView->header()->show(); +} + +QMap<QString, QString> StringMapDialog::stringMap() { + QMap<QString, QString> map; + for(QListViewItem* item = m_listView->firstChild(); item; item = item->nextSibling()) { + map.insert(item->text(0), item->text(1)); + } + return map; +} + +#include "stringmapdialog.moc" diff --git a/src/gui/stringmapdialog.h b/src/gui/stringmapdialog.h new file mode 100644 index 0000000..311df70 --- /dev/null +++ b/src/gui/stringmapdialog.h @@ -0,0 +1,71 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef STRINGMAPDIALOG_H +#define STRINGMAPDIALOG_H + +class KLineEdit; +class KListView; +class QListViewItem; + +#include <kdialogbase.h> + +template <typename T1, typename T2> +class QMap; + +namespace Tellico { + +/** + * @short A simple dialog for editing a map between two strings. + * + * A \ref KListView is used with the map keys in the first column and + * the map values in the second. Two edit boxes are below the list view. + * When an item is selected, the key-value is pair is placed in the edit + * boxes. Add and Delete buttons are used to add a new pair, or to remove + * an existing one. + * + * @author Robby Stephenson + */ +class StringMapDialog : public KDialogBase { +Q_OBJECT + +public: + StringMapDialog(const QMap<QString, QString>& stringMap, QWidget* parent, const char* name=0, bool modal=false); + + /** + * Sets the titles for the key and value columns. + * + * @param label1 The name of the key string + * @param label2 The name of the value string + */ + void setLabels(const QString& label1, const QString& label2); + /** + * Returns the modified string map. + * + * @return The modified string map + */ + QMap<QString, QString> stringMap(); + +private slots: + void slotAdd(); + void slotDelete(); + void slotUpdate(QListViewItem* item); + +protected: + KListView* m_listView; + KLineEdit* m_edit1; + KLineEdit* m_edit2; +}; + +} // end namespace +#endif diff --git a/src/gui/tabcontrol.cpp b/src/gui/tabcontrol.cpp new file mode 100644 index 0000000..145f632 --- /dev/null +++ b/src/gui/tabcontrol.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + copyright : (C) 2002-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "tabcontrol.h" + +#include <qtabbar.h> +#include <qobjectlist.h> + +using Tellico::GUI::TabControl; + +TabControl::TabControl(QWidget* parent_, const char* name_/*=0*/) + : QTabWidget(parent_, name_) { +} + +QTabBar* TabControl::tabBar() const { + return QTabWidget::tabBar(); +} + +void TabControl::setFocusToFirstChild() { + QWidget* page = currentPage(); + Q_ASSERT(page); + QObjectList* list = page->queryList("QWidget"); + for(QObjectListIt it(*list); it.current(); ++it) { + QWidget* w = static_cast<QWidget*>(it.current()); + if(w->isFocusEnabled()) { + w->setFocus(); + break; + } + } + delete list; +} + +// have to loop backwards since count() gets decremented on delete +void TabControl::clear() { + for(int i = count(); i > 0; --i) { + QWidget* w = page(i-1); + if(w) { + removePage(w); + delete w; + } + } +} + +void TabControl::setTabBarHidden(bool hide_) { + QWidget* rightcorner = cornerWidget(TopRight); + QWidget* leftcorner = cornerWidget(TopLeft); + + if(hide_) { + if(leftcorner) { + leftcorner->hide(); + } + if(rightcorner) { + rightcorner->hide(); + } + tabBar()->hide(); + } else { + tabBar()->show(); + if(leftcorner) { + leftcorner->show(); + } + if(rightcorner) { + rightcorner->show(); + } + } +} + +#include "tabcontrol.moc" diff --git a/src/gui/tabcontrol.h b/src/gui/tabcontrol.h new file mode 100644 index 0000000..20d22f5 --- /dev/null +++ b/src/gui/tabcontrol.h @@ -0,0 +1,48 @@ +/*************************************************************************** + copyright : (C) 2002-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TABCONTROL_H +#define TABCONTROL_H + +#include <qtabwidget.h> + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class TabControl : public QTabWidget { +Q_OBJECT + +public: + /** + * Constructor + */ + TabControl(QWidget* parent, const char* name=0); + + QTabBar* tabBar() const; + void setTabBarHidden(bool hide); + + /** + * Sets the focus to the first focusable widget on the current page. + */ + void setFocusToFirstChild(); + +public slots: + void clear(); +}; + + } // end namespace +} // end namespace +#endif diff --git a/src/gui/tablefieldwidget.cpp b/src/gui/tablefieldwidget.cpp new file mode 100644 index 0000000..30f89b4 --- /dev/null +++ b/src/gui/tablefieldwidget.cpp @@ -0,0 +1,330 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "tablefieldwidget.h" +#include "../field.h" +#include "../tellico_utils.h" +#include "../tellico_kernel.h" + +#include <klocale.h> +#include <kpopupmenu.h> +#include <kiconloader.h> +#include <kinputdialog.h> + +#include <qtable.h> + +namespace { + static const int MIN_TABLE_ROWS = 5; + static const int MAX_TABLE_COLS = 10; +} + +using Tellico::GUI::TableFieldWidget; + +TableFieldWidget::TableFieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : FieldWidget(field_, parent_, name_), m_field(field_), m_row(-1), m_col(-1) { + + bool ok; + m_columns = Tellico::toUInt(field_->property(QString::fromLatin1("columns")), &ok); + if(!ok) { + m_columns = 1; + } else { + m_columns = QMIN(m_columns, MAX_TABLE_COLS); // max of 5 columns + } + + m_table = new QTable(MIN_TABLE_ROWS, m_columns, this); + labelColumns(m_field); + // allow renaming of column titles + m_table->horizontalHeader()->setClickEnabled(true); + m_table->horizontalHeader()->installEventFilter(this); + + m_table->verticalHeader()->setClickEnabled(true); + m_table->verticalHeader()->installEventFilter(this); + connect(m_table->verticalHeader(), SIGNAL(indexChange(int, int, int)), SIGNAL(modified())); + + m_table->setDragEnabled(false); + m_table->setFocusStyle(QTable::FollowStyle); + m_table->setRowMovingEnabled(true); // rows can be moved + m_table->setColumnMovingEnabled(false); // columns remain fixed + + m_table->setColumnStretchable(m_columns-1, true); + m_table->adjustColumn(m_columns-1); + m_table->setSelectionMode(QTable::NoSelection); + m_table->setHScrollBarMode(QScrollView::AlwaysOff); + + connect(m_table, SIGNAL(valueChanged(int, int)), SIGNAL(modified())); + connect(m_table, SIGNAL(currentChanged(int, int)), SLOT(slotCheckRows(int, int))); + connect(m_table, SIGNAL(valueChanged(int, int)), SLOT(slotResizeColumn(int, int))); + connect(m_table, SIGNAL(contextMenuRequested(int, int, const QPoint&)), SLOT(contextMenu(int, int, const QPoint&))); + + registerWidget(); +} + +QString TableFieldWidget::text() const { + QString text, str, rstack, cstack, rowStr; + for(int row = 0; row < m_table->numRows(); ++row) { + rowStr.truncate(0); + cstack.truncate(0); + for(int col = 0; col < m_table->numCols(); ++col) { + str = m_table->text(row, col).simplifyWhiteSpace(); + if(str.isEmpty()) { + cstack += QString::fromLatin1("::"); + } else { + rowStr += cstack + str + QString::fromLatin1("::"); + cstack.truncate(0); + } + } + if(rowStr.isEmpty()) { + rstack += QString::fromLatin1("; "); + } else { + rowStr.truncate(rowStr.length()-2); // remove last semi-colon and space + text += rstack + rowStr + QString::fromLatin1("; "); + rstack.truncate(0); + } + } + if(!text.isEmpty()) { + text.truncate(text.length()-2); // remove last semi-colon and space + } + + // now reduce number of rows if necessary + bool loop = true; + for(int row = m_table->numRows()-1; loop && row > MIN_TABLE_ROWS; --row) { + bool empty = true; + for(int col = 0; col < m_table->numCols(); ++col) { + if(!m_table->text(row, col).isEmpty()) { + empty = false; + break; + } + } + if(empty) { + m_table->removeRow(row); + } else { + loop = false; + } + } + return text; +} + +void TableFieldWidget::setText(const QString& text_) { + QStringList list = Data::Field::split(text_, true); + // add additional rows if needed + if(static_cast<int>(list.count()) > m_table->numRows()) { + m_table->insertRows(m_table->numRows(), list.count()-m_table->numRows()); + } + int row; + for(row = 0; row < static_cast<int>(list.count()); ++row) { + for(int col = 0; col < m_table->numCols(); ++col) { + m_table->setText(row, col, list[row].section(QString::fromLatin1("::"), col, col)); + } + m_table->showRow(row); + } + // remove any un-needed rows + int minRow = QMAX(row, MIN_TABLE_ROWS); + for(row = m_table->numRows()-1; row >= minRow; --row) { + m_table->removeRow(row); + } + // adjust all columns + for(int col = 0; col < m_table->numCols()-1; ++col) { + m_table->adjustColumn(col); + } +} + +void TableFieldWidget::clear() { + bool wasEmpty = true; + for(int row = 0; row < m_table->numRows(); ++row) { + if(!emptyRow(row)) { + wasEmpty = false; + } + for(int col = 0; col < m_table->numCols(); ++col) { + m_table->setText(row, col, QString::null); + } + if(row >= MIN_TABLE_ROWS) { + m_table->removeRow(row); + --row; + } + } + editMultiple(false); + if(!wasEmpty) { + emit modified(); + } +} + +QWidget* TableFieldWidget::widget() { + return m_table; +} + +void TableFieldWidget::slotCheckRows(int row_, int) { + if(row_ == m_table->numRows()-1 && !emptyRow(row_)) { // if is last row and row above is not empty + m_table->insertRows(m_table->numRows()); + } +} + +void TableFieldWidget::slotResizeColumn(int, int col_) { + m_table->adjustColumn(col_); +} + +void TableFieldWidget::slotRenameColumn() { + if(m_col < 0 || m_col >= m_columns) { + return; + } + QString name = m_table->horizontalHeader()->label(m_col); + bool ok; + QString newName = KInputDialog::getText(i18n("Rename Column"), i18n("New column name:"), + name, &ok, this); + if(ok && !newName.isEmpty()) { + Data::FieldPtr newField = new Data::Field(*m_field); + newField->setProperty(QString::fromLatin1("column%1").arg(m_col+1), newName); + if(Kernel::self()->modifyField(newField)) { + m_field = newField; + labelColumns(m_field); + } + } +} + +bool TableFieldWidget::emptyRow(int row_) const { + for(int col = 0; col < m_table->numCols(); ++col) { + if(!m_table->text(row_, col).isEmpty()) { + return false; + } + } + return true; +} + +void TableFieldWidget::labelColumns(Data::FieldPtr field_) { + for(int i = 0; i < m_columns; ++i) { + QString s = field_->property(QString::fromLatin1("column%1").arg(i+1)); + if(s.isEmpty()) { + s = i18n("Column %1").arg(i+1); + } + m_table->horizontalHeader()->setLabel(i, s); + } +} + +void TableFieldWidget::updateFieldHook(Data::FieldPtr, Data::FieldPtr newField_) { + bool ok; + m_columns = Tellico::toUInt(newField_->property(QString::fromLatin1("columns")), &ok); + if(!ok) { + m_columns = 1; + } else { + m_columns = QMIN(m_columns, MAX_TABLE_COLS); // max of 5 columns + } + if(m_columns != m_table->numCols()) { + m_table->setNumCols(m_columns); + } + m_table->horizontalHeader()->adjustHeaderSize(); + labelColumns(newField_); +} + +bool TableFieldWidget::eventFilter(QObject* obj_, QEvent* ev_) { + if(ev_->type() == QEvent::MouseButtonPress + && static_cast<QMouseEvent*>(ev_)->button() == Qt::RightButton) { + if(obj_ == m_table->horizontalHeader()) { + QMouseEvent* ev = static_cast<QMouseEvent*>(ev_); + // might be scrolled + int pos = ev->x() + m_table->horizontalHeader()->offset(); + int col = m_table->horizontalHeader()->sectionAt(pos); + if(col >= m_columns) { + return false; + } + m_row = -1; + m_col = col; + KPopupMenu menu(this); + menu.insertItem(SmallIconSet(QString::fromLatin1("edit")), i18n("Rename Column..."), + this, SLOT(slotRenameColumn())); + menu.exec(ev->globalPos()); + return true; + } else if(obj_ == m_table->verticalHeader()) { + QMouseEvent* ev = static_cast<QMouseEvent*>(ev_); + // might be scrolled + int pos = ev->y() + m_table->verticalHeader()->offset(); + int row = m_table->verticalHeader()->sectionAt(pos); + if(row < 0 || row > m_table->numRows()-1) { + return false; + } + m_row = row; + m_col = -1; + // show regular right-click menu + contextMenu(m_row, m_col, ev->globalPos()); + return true; + } + } + return FieldWidget::eventFilter(obj_, ev_); +} + +void TableFieldWidget::contextMenu(int row_, int col_, const QPoint& p_) { + // might get called with col == -1 for clicking on vertical header + // but a negative row means clicking outside bounds of table + if(row_ < 0) { + return; + } + m_row = row_; + m_col = col_; + + int id; + KPopupMenu menu(this); + menu.insertItem(SmallIconSet(QString::fromLatin1("insrow")), i18n("Insert Row"), + this, SLOT(slotInsertRow())); + menu.insertItem(SmallIconSet(QString::fromLatin1("remrow")), i18n("Remove Row"), + this, SLOT(slotRemoveRow())); + id = menu.insertItem(SmallIconSet(QString::fromLatin1("1uparrow")), i18n("Move Row Up"), + this, SLOT(slotMoveRowUp())); + if(m_row == 0) { + menu.setItemEnabled(id, false); + } + id = menu.insertItem(SmallIconSet(QString::fromLatin1("1downarrow")), i18n("Move Row Down"), + this, SLOT(slotMoveRowDown())); + if(m_row == m_table->numRows()-1) { + menu.setItemEnabled(id, false); + } + menu.insertSeparator(); + id = menu.insertItem(SmallIconSet(QString::fromLatin1("edit")), i18n("Rename Column..."), + this, SLOT(slotRenameColumn())); + if(m_col < 0 || m_col > m_columns-1) { + menu.setItemEnabled(id, false); + } + menu.insertSeparator(); + menu.insertItem(SmallIconSet(QString::fromLatin1("locationbar_erase")), i18n("Clear Table"), + this, SLOT(clear())); + menu.exec(p_); +} + +void TableFieldWidget::slotInsertRow() { + if(m_row > -1) { + m_table->insertRows(m_row); + emit modified(); + } +} + +void TableFieldWidget::slotRemoveRow() { + if(m_row > -1) { + m_table->removeRow(m_row); + emit modified(); + } +} + +void TableFieldWidget::slotMoveRowUp() { + if(m_row > 0) { + m_table->swapRows(m_row, m_row-1, true); + m_table->updateContents(); + emit modified(); + } +} + +void TableFieldWidget::slotMoveRowDown() { + if(m_row < m_table->numRows()-1) { + m_table->swapRows(m_row, m_row+1, true); + m_table->updateContents(); + emit modified(); + } +} + +#include "tablefieldwidget.moc" diff --git a/src/gui/tablefieldwidget.h b/src/gui/tablefieldwidget.h new file mode 100644 index 0000000..da9157d --- /dev/null +++ b/src/gui/tablefieldwidget.h @@ -0,0 +1,74 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TABLEFIELDWIDGET_H +#define TABLEFIELDWIDGET_H + +class QTable; +class QEvent; + +#include "fieldwidget.h" +#include "../datavectors.h" + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class TableFieldWidget : public FieldWidget { +Q_OBJECT + +public: + TableFieldWidget(Data::FieldPtr field, QWidget* parent, const char* name=0); + virtual ~TableFieldWidget() {} + + virtual QString text() const; + virtual void setText(const QString& text); + + /** + * Event filter used to popup the menu + */ + bool eventFilter(QObject* obj, QEvent* ev); + +public slots: + virtual void clear(); + +protected: + virtual QWidget* widget(); + virtual void updateFieldHook(Data::FieldPtr oldField, Data::FieldPtr newField); + +private slots: + void contextMenu(int row, int col, const QPoint& p); + void slotCheckRows(int row, int col); + void slotResizeColumn(int row, int col); + void slotRenameColumn(); + void slotInsertRow(); + void slotRemoveRow(); + void slotMoveRowUp(); + void slotMoveRowDown(); + +private: + bool emptyRow(int row) const; + void labelColumns(Data::FieldPtr field); + + QTable* m_table; + int m_columns; + Data::FieldPtr m_field; + int m_row; + int m_col; +}; + + } // end GUI namespace +} // end namespace +#endif diff --git a/src/gui/urlfieldwidget.cpp b/src/gui/urlfieldwidget.cpp new file mode 100644 index 0000000..f3c2afd --- /dev/null +++ b/src/gui/urlfieldwidget.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "urlfieldwidget.h" +#include "../field.h" +#include "../latin1literal.h" +#include "../tellico_kernel.h" + +#include <klineedit.h> +#include <kurlrequester.h> +#include <kurllabel.h> + +using Tellico::GUI::URLFieldWidget; + +// subclass of KURLCompletion is needed so the KURLLabel +// can open relative links. I don't want to have to have to update +// the base directory of the completion every time a new document is opened +QString URLFieldWidget::URLCompletion::makeCompletion(const QString& text_) { + // KURLCompletion::makeCompletion() uses an internal variable instead + // of calling KURLCompletion::dir() so need to set the base dir before completing + setDir(Kernel::self()->URL().directory()); + return KURLCompletion::makeCompletion(text_); +} + +URLFieldWidget::URLFieldWidget(Data::FieldPtr field_, QWidget* parent_, const char* name_/*=0*/) + : FieldWidget(field_, parent_, name_), m_run(0) { + + m_requester = new KURLRequester(this); + m_requester->lineEdit()->setCompletionObject(new URLCompletion()); + m_requester->lineEdit()->setAutoDeleteCompletionObject(true); + connect(m_requester, SIGNAL(textChanged(const QString&)), SIGNAL(modified())); + connect(m_requester, SIGNAL(textChanged(const QString&)), label(), SLOT(setURL(const QString&))); + connect(label(), SIGNAL(leftClickedURL(const QString&)), SLOT(slotOpenURL(const QString&))); + registerWidget(); + + // special case, remember if it's a relative url + m_isRelative = field_->property(QString::fromLatin1("relative")) == Latin1Literal("true"); +} + +URLFieldWidget::~URLFieldWidget() { + if(m_run) { + m_run->abort(); + } +} + +QString URLFieldWidget::text() const { + if(m_isRelative) { + return KURL::relativeURL(Kernel::self()->URL(), m_requester->url()); + } + // for comparison purposes and to be consistent with the file listing importer + // I want the full url here, including the protocol + // the requester only returns the path, so create a KURL + return KURL(m_requester->url()).url(); +} + +void URLFieldWidget::setText(const QString& text_) { + blockSignals(true); + + m_requester->blockSignals(true); + m_requester->setURL(text_); + m_requester->blockSignals(false); + static_cast<KURLLabel*>(label())->setURL(text_); + + blockSignals(false); +} + +void URLFieldWidget::clear() { + m_requester->clear(); + editMultiple(false); +} + +void URLFieldWidget::updateFieldHook(Data::FieldPtr, Data::FieldPtr newField_) { + m_isRelative = newField_->property(QString::fromLatin1("relative")) == Latin1Literal("true"); +} + +void URLFieldWidget::slotOpenURL(const QString& url_) { + if(url_.isEmpty()) { + return; + } + // just in case, interpret string relative to document url + m_run = new KRun(KURL(Kernel::self()->URL(), url_)); +} + +QWidget* URLFieldWidget::widget() { + return m_requester; +} + +#include "urlfieldwidget.moc" diff --git a/src/gui/urlfieldwidget.h b/src/gui/urlfieldwidget.h new file mode 100644 index 0000000..70e9505 --- /dev/null +++ b/src/gui/urlfieldwidget.h @@ -0,0 +1,66 @@ +/*************************************************************************** + copyright : (C) 2005-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef URLFIELDWIDGET_H +#define URLFIELDWIDGET_H + +class KURLRequester; + +#include "fieldwidget.h" + +#include <krun.h> +#include <kurlcompletion.h> + +#include <qguardedptr.h> + +namespace Tellico { + namespace GUI { + +/** + * @author Robby Stephenson + */ +class URLFieldWidget : public FieldWidget { +Q_OBJECT + +public: + URLFieldWidget(Data::FieldPtr field, QWidget* parent, const char* name=0); + virtual ~URLFieldWidget(); + + virtual QString text() const; + virtual void setText(const QString& text); + +public slots: + virtual void clear(); + +protected: + virtual QWidget* widget(); + virtual void updateFieldHook(Data::FieldPtr oldField, Data::FieldPtr newField); + +protected slots: + void slotOpenURL(const QString& url); + +private: + class URLCompletion : public KURLCompletion { + public: + URLCompletion() : KURLCompletion() {} + virtual QString makeCompletion(const QString& text); + }; + + KURLRequester* m_requester; + bool m_isRelative : 1; + QGuardedPtr<KRun> m_run; +}; + + } // end GUI namespace +} // end namespace +#endif |