root/trunk/qt4-gui/src/views/contactdelegate.cpp

Revision 6444, 17.4 kB (checked in by eugene, 5 months ago)

Fixed the phone icons suppression conditions.

Line 
1// -*- c-basic-offset: 2 -*-
2/*
3 * This file is part of Licq, an instant messaging client for UNIX.
4 * Copyright (C) 1999-2006 Licq developers
5 *
6 * Licq is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Licq is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Licq; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 */
20
21#include "config.h"
22
23#include "contactdelegate.h"
24
25#include <QApplication>
26#include <QKeyEvent>
27#include <QLineEdit>
28#include <QPainter>
29
30#include "config/contactlist.h"
31#include "config/iconmanager.h"
32#include "config/skin.h"
33
34#include "userviewbase.h"
35
36using namespace LicqQtGui;
37
38ContactDelegate::ContactDelegate(UserViewBase* userView, QObject* parent)
39  : QAbstractItemDelegate(parent),
40    myUserView(userView)
41{
42  // Empty
43}
44
45QSize ContactDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
46{
47  // Make a copy so we can make our own style options
48  QStyleOptionViewItem textStyle = option;
49
50  ContactListModel::ItemType itemType = static_cast<ContactListModel::ItemType>
51    (index.data(ContactListModel::ItemTypeRole).toInt());
52
53  if (itemType == ContactListModel::GroupItem ||
54      itemType == ContactListModel::BarItem)
55  {
56    // Smaller font for group and subgroup headings
57    if (textStyle.font.pointSize() > 2)
58      textStyle.font.setPointSize(textStyle.font.pointSize() - 2);
59  }
60
61  QSize size = QSize(0, 0);
62
63  QVariant var;
64  if ((var = index.data(Qt::DisplayRole)).isValid())
65  {
66    QString text = var.toString();
67    size = QSize(
68        textStyle.fontMetrics.width(text),
69        textStyle.fontMetrics.height());
70  }
71
72  // Make sure we have enough height for icons
73  // User icons are always 16x16 so set minimum size to 18
74  if (itemType != ContactListModel::BarItem && size.height() < 18)
75    size.setHeight(18);
76
77  // For first column, add width for icon
78  if (index.column() == 0)
79    size.setWidth(size.width() + 18);
80
81  return size;
82}
83
84void ContactDelegate::paint(QPainter* p, const QStyleOptionViewItem& option,
85    const QModelIndex& index) const
86{
87  // Painter uses coordinates of entire widget,
88  // move it so coordinates are relative to
89  // the top left corner of this cell
90  p->save();
91  p->translate(option.rect.topLeft());
92
93  QVariant var;
94
95  // Fill up the data structure
96  Parameters arg =
97  {
98    p,
99    option,
100    index,
101    option.rect.width(),
102    option.rect.height(),
103    (1 << Config::ContactList::instance()->columnAlignment(index.column())) |
104      Qt::AlignVCenter,
105    static_cast<ContactListModel::ItemType>
106      (index.data(ContactListModel::ItemTypeRole).toInt()),
107    Config::Skin::active(),
108    option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled,
109    static_cast<ContactListModel::StatusType>
110      (index.data(ContactListModel::StatusRole).toUInt()),
111    index.data(ContactListModel::ExtendedStatusRole).toUInt(),
112    QString::null
113  };
114
115  // Some corrections to the data above
116  if (arg.cg == QPalette::Normal && !(option.state & QStyle::State_Active))
117    arg.cg = QPalette::Inactive;
118  if ((var = index.data(Qt::DisplayRole)).isValid())
119    arg.text = var.toString();
120
121  fillBackground(arg);
122  drawGrid(arg, !(index.model()->columnCount() - index.column() - 1));
123  prepareForeground(arg, index.data(ContactListModel::OnlineAnimationRole));
124
125  switch (arg.itemType)
126  {
127    case ContactListModel::BarItem:
128      drawBar(arg);
129      break;
130    default:
131      if ((var = index.data(ContactListModel::CarAnimationRole)).isValid())
132        drawCarAnimation(arg, var.toInt());
133      if (index.column() == 0)
134        drawStatusIcon(arg);
135      if (arg.itemType == ContactListModel::GroupItem)
136        arg.align = Qt::AlignLeft | Qt::AlignVCenter;
137      drawText(arg);
138      drawExtIcons(arg);
139  }
140
141  p->restore();
142}
143
144void ContactDelegate::drawExtIcon(Parameters& arg, IconManager::IconType iconType) const
145{
146  drawExtIcon(arg,
147      const_cast<QPixmap*>(&IconManager::instance()->getIcon(iconType)));
148}
149
150void ContactDelegate::drawExtIcon(Parameters& arg, const QPixmap* icon) const
151{
152  if (icon->isNull() || arg.width < icon->width())
153    return;
154
155  int x = 0, y = 0;
156  int align = arg.align;
157
158  switch (align & Qt::AlignVertical_Mask)
159  {
160    case Qt::AlignVCenter:
161      y = (arg.height - icon->height()) / 2;
162      break;
163    case Qt::AlignBottom:
164      y = arg.height - icon->height();
165  }
166
167  if (y < 0)
168    y = 0;
169
170  align &= Qt::AlignHorizontal_Mask;
171
172  if (align == Qt::AlignRight)
173    x = arg.width - icon->width();
174
175  arg.p->drawPixmap(x, y, *icon);
176
177  arg.width -= icon->width() + 3;
178  if (align != Qt::AlignRight)
179    arg.p->translate(icon->width() + 3, 0);
180}
181
182void ContactDelegate::fillBackground(Parameters& arg) const
183{
184#define FILL(color) \
185  arg.p->fillRect(0, 0, arg.width, arg.height, (color))
186
187  // we assume the same coloring for groups and bars, since they are
188  // mutually exclusive.
189  if (arg.itemType == ContactListModel::GroupItem ||
190      arg.itemType == ContactListModel::BarItem)
191  {
192    if (!arg.skin->groupBackImage.isNull())
193      if (arg.skin->tileGroupBackImage)
194        arg.p->drawTiledPixmap(0, 0, arg.width, arg.height,
195            QPixmap::fromImage(arg.skin->groupBackImage));
196      else
197        arg.p->drawImage(0, 0,
198            arg.skin->groupBackImage.scaled(arg.width, arg.height));
199    else
200      if (arg.option.state & QStyle::State_Selected)
201        if (arg.skin->groupHighBackColor.isValid())
202          FILL(arg.skin->groupHighBackColor);
203        else
204          FILL(arg.option.palette.brush(arg.cg, QPalette::Highlight));
205      else
206        if (arg.skin->groupBackColor.isValid())
207          FILL(arg.skin->groupBackColor);
208  }
209  else
210  {
211    if ((arg.option.state & QStyle::State_Selected) != 0)
212    {
213      if (arg.skin->highBackColor.isValid())
214        FILL(arg.skin->highBackColor);
215      else
216        FILL(arg.option.palette.brush(arg.cg, QPalette::Highlight));
217    }
218  }
219#undef FILL
220}
221
222void ContactDelegate::drawGrid(Parameters& arg, bool last) const
223{
224  if (Config::ContactList::instance()->showGridLines() &&
225      arg.itemType == ContactListModel::UserItem)
226  {
227    arg.p->setPen(arg.skin->gridlineColor);
228    arg.p->drawRect(0, 0, arg.width, arg.height);
229    // FIXME: right border is not shown for the last column...
230    // And giving width - 1 above would result in thick
231    // vertical border between the columns.
232    if (last)
233      arg.p->drawLine(arg.width - 1, 0, arg.width - 1, arg.height - 1);
234  }
235}
236
237void ContactDelegate::prepareForeground(Parameters& arg, QVariant animate) const
238{
239  arg.option.displayAlignment = (Qt::AlignmentFlag)arg.align;
240
241  // Setup font and color to use
242  arg.option.font.setItalic(false);
243  arg.option.font.setStrikeOut(false);
244  arg.option.font.setWeight(QFont::Normal);
245
246  QColor textColor;
247
248  switch (arg.itemType)
249  {
250    case ContactListModel::UserItem:
251      {
252        if (Config::ContactList::instance()->useFontStyles())
253        {
254          if (arg.extStatus & ContactListModel::OnlineNotifyStatus)
255            arg.option.font.setWeight(QFont::DemiBold);
256          if (arg.extStatus & ContactListModel::InvisibleListStatus)
257            arg.option.font.setStrikeOut(true);
258          if (arg.extStatus & ContactListModel::VisibleListStatus)
259            arg.option.font.setItalic(true);
260        }
261
262        if (animate.isValid() && (animate.toInt() & 1))
263          textColor = arg.skin->offlineColor;
264        else if ((arg.option.state & QStyle::State_Selected) != 0)
265          textColor = arg.skin->highTextColor;
266        else if (arg.extStatus & ContactListModel::NewUserStatus)
267          textColor = arg.skin->newUserColor;
268        else if (arg.extStatus & ContactListModel::AwaitingAuthStatus)
269          textColor = arg.skin->awaitingAuthColor;
270        else
271          switch (arg.status)
272          {
273            case ContactListModel::AwayStatus:
274            case ContactListModel::OccupiedStatus:
275            case ContactListModel::DoNotDisturbStatus:
276            case ContactListModel::NotAvailableStatus:
277              textColor = arg.skin->awayColor;
278              break;
279            case ContactListModel::OfflineStatus:
280              textColor = arg.skin->offlineColor;
281              break;
282            case ContactListModel::OnlineStatus:
283            case ContactListModel::FreeForChatStatus:
284            default:
285              textColor = arg.skin->onlineColor;
286          }
287        break;
288      }
289
290      case ContactListModel::GroupItem: // we assume the same coloring for
291      case ContactListModel::BarItem:   // groups and bars, since they are
292      default:                          // mutually exclusive.
293        if (arg.itemType == ContactListModel::GroupItem)
294          arg.option.font.setWeight(QFont::Bold);
295        if (arg.option.font.pointSize() > 2)
296          arg.option.font.setPointSize(arg.option.font.pointSize() - 2);
297        if ((arg.option.state & QStyle::State_Selected) != 0)
298          textColor = arg.skin->groupHighTextColor;
299        else
300          textColor = arg.skin->groupTextColor;
301        break;
302  }
303
304  if (textColor.isValid())
305  {
306    arg.option.palette.setColor(QPalette::Text, textColor);
307    arg.p->setPen(textColor);
308  }
309  else
310  {
311    // compatibility fallbacks
312    if ((arg.option.state & QStyle::State_Selected) != 0)
313      arg.p->setPen(arg.option.palette.color(arg.cg, QPalette::HighlightedText));
314    else
315      if ((arg.itemType == ContactListModel::GroupItem ||
316           arg.itemType == ContactListModel::BarItem) &&
317          arg.skin->gridlineColor.isValid())
318        arg.p->setPen(arg.skin->gridlineColor);
319      else
320        arg.p->setPen(arg.option.palette.color(arg.cg, QPalette::Text));
321  }
322
323  arg.p->setFont(arg.option.font);
324}
325
326void ContactDelegate::drawBar(Parameters& arg) const
327{
328  int textWidth = arg.p->fontMetrics().width(arg.text);
329  int barWidth = (arg.width >> 1) - 20 - (textWidth >> 1) - 5;
330
331  if (barWidth > 0)
332  {
333    int barHeight = arg.height >> 1;
334
335    qDrawShadeLine(arg.p,
336        20, barHeight,
337        20 + barWidth, barHeight,
338        arg.option.palette);
339    qDrawShadeLine(arg.p,
340        arg.width - 20 - barWidth, barHeight,
341        arg.width - 20, barHeight,
342        arg.option.palette);
343  }
344
345  if (!arg.text.isEmpty())
346    arg.p->drawText(0, 0, arg.width, arg.height, Qt::AlignCenter, arg.text);
347}
348
349void ContactDelegate::drawStatusIcon(Parameters& arg) const
350{
351  const QPixmap* icon = NULL;
352  IconManager* iconman = IconManager::instance();
353
354  if (arg.itemType == ContactListModel::UserItem)
355  {
356    QVariant var = arg.index.data(ContactListModel::EventAnimationRole);
357    if (var.isValid() && (var.toInt() & 1))
358      icon = &iconman->iconForEvent(
359          arg.index.data(ContactListModel::EventSubCommandRole).toUInt());
360    else
361      icon = &iconman->iconForStatus(
362          arg.index.data(ContactListModel::StatusRole).toUInt(),
363          arg.index.data(ContactListModel::UserIdRole).toString(),
364          arg.index.data(ContactListModel::PpidRole).toUInt());
365  }
366  else if (arg.itemType == ContactListModel::GroupItem)
367  {
368    if (myUserView->isExpanded(arg.index))
369      icon = &iconman->getIcon(IconManager::ExpandedIcon);
370    else
371      icon = &iconman->getIcon(IconManager::CollapsedIcon);
372  }
373
374  if (icon != NULL)
375  {
376    // Draw the icon
377    unsigned short iconWidth = qMax(icon->width(), 18);
378    arg.p->drawPixmap(
379        (iconWidth - icon->width()) / 2,
380        (arg.height - icon->height()) / 2,
381        *icon);
382
383    // Adjust drawing coordinates to exclude status icon
384    // and 2-pixel padding
385    arg.width -= iconWidth + 2;
386    arg.p->translate(iconWidth + 2, 0);
387  }
388}
389
390void ContactDelegate::drawCarAnimation(Parameters& arg, int counter) const
391{
392  QPen tmp = arg.p->pen();
393  arg.p->setPen((counter & 1) ? Qt::white : Qt::black);
394
395  // Draw a rectangle around the contact which will span over multiple cells
396  // if the view has more than one column
397  arg.p->drawLine(0, 0, arg.width - 1, 0);
398  arg.p->drawLine(0, arg.height - 1, arg.width - 1, arg.height - 1);
399  if (arg.index.column() == 0)
400    arg.p->drawLine(0, 0, 0, arg.height - 1);
401  if (arg.index.column() == arg.index.model()->columnCount() - 1)
402    arg.p->drawLine(arg.width - 1, 0, arg.width - 1, arg.height - 1);
403  arg.p->setPen(tmp);
404}
405
406void ContactDelegate::drawText(Parameters& arg) const
407{
408  if (arg.text.isEmpty())
409    return;
410
411  QString elidedText = arg.p->fontMetrics().elidedText(
412      arg.text, arg.option.textElideMode, arg.width - 6);
413  arg.p->drawText(2, 0, arg.width - 4, arg.height, arg.align, elidedText);
414
415  int textWidth = arg.p->fontMetrics().width(elidedText);
416
417  switch (arg.align & Qt::AlignHorizontal_Mask)
418  {
419    case Qt::AlignHCenter: // Fall through
420      textWidth += arg.width - 2;
421      textWidth /= 2;
422    case Qt::AlignLeft: // Fall through
423      arg.p->translate(textWidth + 6, 0);
424    case Qt::AlignRight:
425      arg.width -= textWidth + 6;
426  }
427}
428
429void ContactDelegate::drawExtIcons(Parameters& arg) const
430{
431  if (arg.itemType == ContactListModel::GroupItem)
432  {
433    if (arg.index.data(ContactListModel::UnreadEventsRole).toInt() > 0 &&
434        !myUserView->isExpanded(arg.index))
435      drawExtIcon(arg, IconManager::StandardMessageIcon);
436    return;
437  }
438
439  if (Config::ContactList::instance()->columnFormat(arg.index.column())
440      .contains("%a"))
441  {
442    if (Config::ContactList::instance()->showUserIcons())
443    {
444      QVariant var = arg.index.data(ContactListModel::UserIconRole);
445      if (var.isValid() && var.canConvert(QVariant::Image))
446      {
447        QImage tmp = var.value<QImage>();
448        if (tmp.height() > arg.height - 2)
449          tmp = tmp.scaledToHeight(arg.height - 2, Qt::SmoothTransformation);
450        QPixmap* pic = new QPixmap(QPixmap::fromImage(tmp));
451        drawExtIcon(arg, pic);
452        delete pic;
453      }
454    }
455
456#define EXTICON(status, icon) \
457  if (arg.extStatus & (status)) \
458    drawExtIcon(arg, (icon))
459
460    if (Config::ContactList::instance()->showExtendedIcons())
461    {
462      if (Config::ContactList::instance()->showPhoneIcons())
463      {
464        EXTICON(ContactListModel::PhoneStatus, IconManager::PhoneIcon);
465        EXTICON(ContactListModel::CellularStatus, IconManager::CellularIcon);
466      }
467      EXTICON(ContactListModel::BirthdayStatus, IconManager::BirthdayIcon);
468      EXTICON(ContactListModel::InvisibleStatus, IconManager::InvisibleIcon);
469
470#ifdef HAVE_LIBGPGME
471      // pmGPGKey
472      if (arg.extStatus & ContactListModel::GpgKeyStatus)
473      {
474        EXTICON(ContactListModel::GpgKeyEnabledStatus,
475            IconManager::GpgKeyEnabledIcon);
476        else
477          drawExtIcon(arg, IconManager::GpgKeyDisabledIcon);
478      }
479#endif
480
481      if (arg.status != ContactListModel::OfflineStatus)
482      {
483        if (Config::ContactList::instance()->showPhoneIcons())
484        {
485          EXTICON(ContactListModel::PhoneFollowMeActiveStatus,
486              IconManager::PfmActiveIcon);
487          else
488          EXTICON(ContactListModel::PhoneFollowMeBusyStatus,
489              IconManager::PfmBusyIcon);
490
491          EXTICON(ContactListModel::IcqPhoneActiveStatus,
492              IconManager::IcqPhoneActiveIcon);
493          EXTICON(ContactListModel::IcqPhoneBusyStatus,
494              IconManager::IcqPhoneBusyIcon);
495        }
496
497        EXTICON(ContactListModel::SharedFilesStatus, IconManager::SharedFilesIcon);
498        EXTICON(ContactListModel::TypingStatus, IconManager::TypingIcon);
499      }
500
501      EXTICON(ContactListModel::SecureStatus, IconManager::SecureOnIcon);
502      EXTICON(ContactListModel::CustomArStatus, IconManager::CustomArIcon);
503    }
504  }
505#undef EXTICON
506}
507
508QWidget* ContactDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& /* option */, const QModelIndex& /* index */) const
509{
510  QLineEdit* editor = new QLineEdit(parent);
511
512  // Don't use the view's skinned palette
513  editor->setPalette(QApplication::palette());
514
515  return editor;
516}
517
518void ContactDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
519{
520  QString value = index.model()->data(index, ContactListModel::NameRole).toString();
521
522  QLineEdit* lineedit = dynamic_cast<QLineEdit*>(editor);
523  lineedit->setText(value);
524}
525
526void ContactDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
527{
528  QLineEdit* lineedit = dynamic_cast<QLineEdit*>(editor);
529  model->setData(index, lineedit->text(), ContactListModel::NameRole);
530}
531
532void ContactDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
533{
534  QRect r = option.rect;
535
536  // Leave space for icon in first column
537  if (index.column() == 0)
538    r.setLeft(r.left() + 18);
539
540  editor->setGeometry(r);
541}
542
543bool ContactDelegate::eventFilter(QObject* object, QEvent* event)
544{
545  QWidget* editor = qobject_cast<QWidget*>(object);
546  if (editor == NULL)
547    return false;
548
549  if (event->type() == QEvent::KeyPress)
550  {
551    switch (static_cast<QKeyEvent*>(event)->key())
552    {
553      case Qt::Key_Enter:
554      case Qt::Key_Return:
555        emit commitData(editor);
556        emit closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
557        return true;
558      case Qt::Key_Escape:
559        // don't commit data
560        emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
561        return true;
562    }
563  }
564  else if (event->type() == QEvent::FocusOut)
565  {
566    emit commitData(editor);
567    emit closeEditor(editor, NoHint);
568  }
569
570  return QAbstractItemDelegate::eventFilter(object, event);
571}
Note: See TracBrowser for help on using the browser.