You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tellico/src/entryiconview.cpp

445 lines
13 KiB

/***************************************************************************
copyright : (C) 2002-2006 by Robby Stephenson
email : robby@periapsis.org
***************************************************************************/
/***************************************************************************
* *
* 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 "entryiconview.h"
#include "collection.h"
#include "collectionfactory.h"
#include "imagefactory.h"
#include "controller.h"
#include "entry.h"
#include "field.h"
#include "tellico_utils.h"
#include "tellico_debug.h"
#include "listviewcomparison.h"
#include <tdepopupmenu.h>
#include <kstringhandler.h>
#include <kiconloader.h>
#include <kwordwrap.h>
#include <kimageeffect.h>
#include <tdelocale.h>
#include <tqpainter.h>
namespace {
static const int MIN_ENTRY_ICON_SIZE = 32;
static const int MAX_ENTRY_ICON_SIZE = 128;
static const int ENTRY_ICON_SIZE_PAD = 6;
static const int ENTRY_ICON_SHADOW_RIGHT = 1;
static const int ENTRY_ICON_SHADOW_BOTTOM = 1;
}
using Tellico::EntryIconView;
using Tellico::EntryIconViewItem;
EntryIconView::EntryIconView(TQWidget* parent_, const char* name_/*=0*/)
: TDEIconView(parent_, name_), m_coll(0), m_maxAllowedIconWidth(MAX_ENTRY_ICON_SIZE),
m_maxIconWidth(MIN_ENTRY_ICON_SIZE), m_maxIconHeight(MIN_ENTRY_ICON_SIZE),
m_comparison(0) {
setAutoArrange(true);
setSorting(true);
setItemsMovable(false);
setSelectionMode(TQIconView::Extended);
setResizeMode(TQIconView::Adjust);
setMode(TDEIconView::Select);
setSpacing(4);
// setWordWrapIconText(false);
m_defaultPixmaps.setAutoDelete(true);
connect(this, TQT_SIGNAL(selectionChanged()), TQT_SLOT(slotSelectionChanged()));
connect(this, TQT_SIGNAL(doubleClicked(TQIconViewItem*)), TQT_SLOT(slotDoubleClicked(TQIconViewItem*)));
connect(this, TQT_SIGNAL(contextMenuRequested(TQIconViewItem*, const TQPoint&)),
TQT_SLOT(slotShowContextMenu(TQIconViewItem*, const TQPoint&)));
}
EntryIconView::~EntryIconView() {
delete m_comparison;
m_comparison = 0;
}
EntryIconViewItem* EntryIconView::firstItem() const {
return static_cast<EntryIconViewItem*>(TDEIconView::firstItem());
}
void EntryIconView::findImageField() {
m_imageField.truncate(0);
if(!m_coll) {
return;
}
const Data::FieldVec& fields = m_coll->imageFields();
if(!fields.isEmpty()) {
m_imageField = fields[0]->name();
}
// myDebug() << "EntryIconView::findImageField() - image field = " << m_imageField << endl;
}
const TQString& EntryIconView::imageField() {
return m_imageField;
}
const TQString& EntryIconView::sortField() {
return m_comparison ? m_comparison->fieldName() : TQString();
}
const TQPixmap& EntryIconView::defaultPixmap() {
TQPixmap* pix = m_defaultPixmaps[m_coll->type()];
if(pix) {
return *pix;
}
TDEIconLoader* loader = TDEGlobal::instance()->iconLoader();
TQPixmap tmp = loader->loadIcon(TQString::fromLatin1("nocover_") + CollectionFactory::typeName(m_coll->type()),
TDEIcon::User, 0, TDEIcon::DefaultState, 0, true /*canReturnNull */);
if(tmp.isNull()) {
myLog() << "EntryIconView::defaultPixmap() - null nocover image, loading tellico.png" << endl;
tmp = UserIcon(TQString::fromLatin1("tellico"));
}
if(TQMAX(tmp.width(), tmp.height()) > static_cast<int>(MIN_ENTRY_ICON_SIZE)) {
tmp.convertFromImage(tmp.convertToImage().smoothScale(m_maxIconWidth, m_maxIconHeight, TQImage::ScaleMin));
}
pix = new TQPixmap(tmp);
m_defaultPixmaps.insert(m_coll->type(), pix);
return *pix;
}
void EntryIconView::setMaxAllowedIconWidth(int width_) {
m_maxAllowedIconWidth = TQMAX(MIN_ENTRY_ICON_SIZE, TQMIN(MAX_ENTRY_ICON_SIZE, width_));
setMaxItemWidth(m_maxAllowedIconWidth + 2*ENTRY_ICON_SIZE_PAD);
m_defaultPixmaps.clear();
refresh();
}
void EntryIconView::fillView() {
setSorting(false);
setGridX(m_maxAllowedIconWidth + 2*ENTRY_ICON_SIZE_PAD); // set default spacing initially
GUI::CursorSaver cs(TQt::waitCursor);
bool allDefaultImages = true;
m_maxIconWidth = TQMAX(MIN_ENTRY_ICON_SIZE, m_maxIconWidth);
m_maxIconHeight = TQMAX(MIN_ENTRY_ICON_SIZE, m_maxIconHeight);
EntryIconViewItem* item;
for(Data::EntryVecIt it = m_entries.begin(); it != m_entries.end(); ++it) {
item = new EntryIconViewItem(this, it);
m_maxIconWidth = TQMAX(m_maxIconWidth, item->width());
m_maxIconHeight = TQMAX(m_maxIconHeight, item->pixmapRect().height());
if(item->usesImage()) {
allDefaultImages = false;
}
}
if(allDefaultImages) {
m_maxIconWidth = m_maxAllowedIconWidth;
m_maxIconHeight = m_maxAllowedIconWidth;
}
// if both width and height are min, then that means there are no images
m_defaultPixmaps.clear();
// now reset size of all default pixmaps
for(item = firstItem(); item; item = item->nextItem()) {
if(!item->usesImage()) {
item->updatePixmap();
}
}
setGridX(m_maxIconWidth + 2*ENTRY_ICON_SIZE_PAD); // the pad is so the text can be wider than the icon
setGridY(m_maxIconHeight + fontMetrics().height());
sort();
setSorting(true);
}
void EntryIconView::refresh() {
if(!m_coll) {
return;
}
showEntries(m_entries);
}
void EntryIconView::clear() {
TDEIconView::clear();
m_coll = 0;
m_entries.clear();
m_imageField.truncate(0);
}
void EntryIconView::showEntries(const Data::EntryVec& entries_) {
setUpdatesEnabled(false);
TDEIconView::clear(); // don't call EntryIconView::clear() since that clears the entries_ ref
if(entries_.isEmpty()) {
return;
}
m_coll = entries_[0]->collection();
m_entries = entries_;
findImageField();
fillView();
setUpdatesEnabled(true);
}
void EntryIconView::addEntries(Data::EntryVec entries_) {
if(entries_.isEmpty()) {
return;
}
if(!m_coll) {
m_coll = entries_[0]->collection();
}
// since the view probably doesn't show all the current entries
// only add the new ones if the count is the total
if(m_entries.count() + entries_.count() < m_coll->entryCount()) {
return;
}
int w = MIN_ENTRY_ICON_SIZE;
int h = MIN_ENTRY_ICON_SIZE;
for(Data::EntryVecIt entry = entries_.begin(); entry != entries_.end(); ++entry) {
m_entries.append(entry);
EntryIconViewItem* item = new EntryIconViewItem(this, entry);
w = TQMAX(w, item->width());
h = TQMAX(h, item->pixmapRect().height());
}
if(w > m_maxIconWidth || h > m_maxIconHeight) {
refresh();
}
}
void EntryIconView::modifyEntries(Data::EntryVec entries_) {
for(Data::EntryVecIt entry = entries_.begin(); entry != entries_.end(); ++entry) {
EntryIconViewItem* item = 0;
for(EntryIconViewItem* it = firstItem(); it; it = it->nextItem()) {
if(it->entry() == entry) {
item = it;
break;
}
}
if(!item) {
continue;
}
item->update();
}
}
void EntryIconView::removeEntries(Data::EntryVec entries_) {
for(Data::EntryVecIt entry = entries_.begin(); entry != entries_.end(); ++entry) {
m_entries.remove(entry);
}
bool found = false;
EntryIconViewItem* item = firstItem();
while(item) {
if(entries_.contains(item->entry())) {
EntryIconViewItem* prev = item;
item = item->nextItem();
delete prev;
found = true;
} else {
item = item->nextItem();
}
}
if(found) {
arrangeItemsInGrid();
}
}
void EntryIconView::slotSelectionChanged() {
Data::EntryVec entries;
const TQPtrList<EntryIconViewItem>& items = selectedItems();
for(TQPtrListIterator<EntryIconViewItem> it(items); it.current(); ++it) {
entries.append(it.current()->entry());
}
Controller::self()->slotUpdateSelection(this, entries);
}
void EntryIconView::slotDoubleClicked(TQIconViewItem* item_) {
EntryIconViewItem* i = static_cast<EntryIconViewItem*>(item_);
if(i) {
Controller::self()->editEntry(i->entry());
}
}
void EntryIconView::updateSelected(EntryIconViewItem* item_, bool s_) const {
if(s_) {
m_selectedItems.append(item_);
} else {
m_selectedItems.removeRef(item_);
}
}
void EntryIconView::slotShowContextMenu(TQIconViewItem* item_, const TQPoint& point_) {
TDEPopupMenu menu(this);
// only insert entry items if one is selected
if(item_) {
Controller::self()->plugEntryActions(&menu);
menu.insertSeparator();
}
TDEPopupMenu sortMenu(&menu);
const TQStringList titles = m_coll->fieldTitles();
for(TQStringList::ConstIterator it = titles.begin(); it != titles.end(); ++it) {
sortMenu.insertItem(*it);
}
connect(&sortMenu, TQT_SIGNAL(activated(int)), TQT_SLOT(slotSortMenuActivated(int)));
menu.insertItem(i18n("&Sort By"), &sortMenu);
menu.exec(point_);
}
void EntryIconView::slotSortMenuActivated(int id_) {
const TDEPopupMenu* menu = ::tqqt_cast<TDEPopupMenu*>(sender());
if(menu) {
TQString title = menu->text(id_);
Data::FieldPtr f = m_coll->fieldByTitle(title);
if(f) {
delete m_comparison;
m_comparison = ListViewComparison::create(f);
sort();
}
}
}
int EntryIconView::compare(const EntryIconViewItem* item1, EntryIconViewItem* item2) {
if(m_comparison) {
return m_comparison->compare(item1, item2);
}
return 0;
}
/* *********************************************************** */
EntryIconViewItem::EntryIconViewItem(EntryIconView* parent_, Data::EntryPtr entry_)
: TDEIconViewItem(parent_, entry_->title()), m_entry(entry_), m_usesImage(false) {
setDragEnabled(false);
const TQString& imageField = parent_->imageField();
if(!imageField.isEmpty()) {
TQPixmap p = ImageFactory::pixmap(m_entry->field(imageField),
parent_->maxAllowedIconWidth(),
parent_->maxAllowedIconWidth());
if(!p.isNull()) {
setPixmap(p);
m_usesImage = true;
}
}
}
EntryIconViewItem::~EntryIconViewItem() {
// be sure to remove from selected list when it's deleted
EntryIconView* view = iconView();
if(view) {
view->updateSelected(this, false);
}
}
void EntryIconViewItem::setSelected(bool s_) {
setSelected(s_, false);
}
void EntryIconViewItem::setSelected(bool s_, bool cb_) {
EntryIconView* view = iconView();
if(!view) {
return;
}
if(s_ != isSelected()) {
view->updateSelected(this, s_);
TDEIconViewItem::setSelected(s_, cb_);
}
}
void EntryIconViewItem::updatePixmap() {
EntryIconView* view = iconView();
const TQString& imageField = view->imageField();
m_usesImage = false;
if(imageField.isEmpty()) {
setPixmap(view->defaultPixmap());
} else {
TQPixmap p = ImageFactory::pixmap(m_entry->field(imageField),
view->maxAllowedIconWidth(),
view->maxAllowedIconWidth());
if(p.isNull()) {
setPixmap(view->defaultPixmap());
} else {
setPixmap(p);
m_usesImage = true;
calcRect();
}
}
}
void EntryIconViewItem::update() {
setText(m_entry->title());
updatePixmap();
iconView()->arrangeItemsInGrid();
}
void EntryIconViewItem::calcRect(const TQString& text_) {
TDEIconViewItem::calcRect(text_);
TQRect r = pixmapRect();
r.rRight() += ENTRY_ICON_SHADOW_RIGHT;
r.rBottom() += ENTRY_ICON_SHADOW_BOTTOM;
setPixmapRect(r);
}
void EntryIconViewItem::paintItem(TQPainter* p_, const TQColorGroup &cg_) {
p_->save();
paintPixmap(p_, cg_);
paintText(p_, cg_);
p_->restore();
}
void EntryIconViewItem::paintFocus(TQPainter*, const TQColorGroup&) {
// don't draw anything
}
void EntryIconViewItem::paintPixmap(TQPainter* p_, const TQColorGroup& cg_) {
// only draw the shadow if there's an image
// oh, and don't draw it if it's a file catalog, it doesn't look right
if(m_usesImage && !isSelected() && m_entry->collection()->type() != Data::Collection::File) {
// pixmapRect() already includes shadow size, so shift the rect by that amount
TQRect r = pixmapRect(false);
r.setLeft(r.left() + ENTRY_ICON_SHADOW_RIGHT);
r.setTop(r.top() + ENTRY_ICON_SHADOW_BOTTOM);
TQColor c = Tellico::blendColors(iconView()->backgroundColor(), TQt::black, 10);
p_->fillRect(r, c);
}
TDEIconViewItem::paintPixmap(p_, cg_);
}
void EntryIconViewItem::paintText(TQPainter* p_, const TQColorGroup &cg_) {
TQRect tr = textRect(false);
int textX = tr.x() + 2;
int textY = tr.y();
if(isSelected()) {
p_->setBrush(TQBrush(cg_.highlight()));
p_->setPen(TQPen(cg_.highlight()));
p_->drawRoundRect(tr, 1000/tr.width(), 1000/tr.height());
p_->setPen(TQPen(cg_.highlightedText()));
} else {
if(iconView()->itemTextBackground() != TQt::NoBrush) {
p_->fillRect(tr, iconView()->itemTextBackground());
}
p_->setPen(cg_.text());
}
int align = iconView()->itemTextPos() == TQIconView::Bottom ? AlignHCenter : AlignAuto;
wordWrap()->drawText(p_, textX, textY, align | KWordWrap::Truncate);
}
TQString EntryIconViewItem::key() const {
const TQString& sortField = iconView()->sortField();
if(sortField.isEmpty()) {
return TDEIconViewItem::key();
}
return m_entry->field(sortField);
}
int EntryIconViewItem::compare(TQIconViewItem* item_) const {
int res = iconView()->compare(this, static_cast<EntryIconViewItem*>(item_));
return res == 0 ? TDEIconViewItem::compare(item_) : res;
}
#include "entryiconview.moc"