[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    Icon choosing in kedittoolbar
From:       David Faure <faure () kde ! org>
Date:       2004-04-28 10:11:55
Message-ID: 200404281211.55728.faure () kde ! org
[Download RAW message or body]

I implemented today something that I wanted to do for a long time:
being able to set a custom icon to an action in kedittoolbar.
(I was reminded by wish #80450, but I'm sure there are older wishes for it)

It works perfectly, see attached patch (which also factorizes some code)
     - except that one cannot choose the icon :-}
KIconChooser is in libkio (kio/kfile/) since it needs KFileDialog, and
KEditToolbar is in kdeui so it can't use it.

The only solutions I can think of are:
1) using KProcess to launch "kdialog --chooseicon" (which doesn't exist yet)
2) moving KEditToolbar to kio/kfile - but this isn't BC, for the (rare?) applications
    that would use kdeui and not kio.
3) waiting for KDE 4 before moving kedittoolbar to kio/kfile :(
4) implementing a custom iconchooser with no filedialog. Code duplication and limited functionality...

Any other solution I'm missing?
I guess 1) is the best bet, assuming that the window manager behaves properly
(--embed <winid> will make it modal to the kedittoolbar dialog, right?).

-- 
David Faure, faure@kde.org, sponsored by Trolltech to work on KDE,
Konqueror (http://www.konqueror.org), and KOffice (http://www.koffice.org).

["changeicon.diff" (text/x-diff)]

Index: kactioncollection.cpp
===================================================================
RCS file: /home/kde/kdelibs/kdeui/kactioncollection.cpp,v
retrieving revision 1.315
diff -u -p -r1.315 kactioncollection.cpp
--- kactioncollection.cpp	1 Apr 2004 12:27:01 -0000	1.315
+++ kactioncollection.cpp	28 Apr 2004 10:01:44 -0000
@@ -710,9 +710,6 @@ bool KActionShortcutList::save() const
 	if( m_actions.xmlFile().isEmpty() )
 		return writeSettings();
 
-	QString tagActionProp = QString::fromLatin1("ActionProperties");
-	QString tagAction     = QString::fromLatin1("Action");
-	QString attrName      = QString::fromLatin1("name");
 	QString attrShortcut  = QString::fromLatin1("shortcut");
 	QString attrAccel     = QString::fromLatin1("accel"); // Depricated attribute
 
@@ -723,22 +720,8 @@ bool KActionShortcutList::save() const
 
 	// Process XML data
 
-	// first, lets see if we have existing properties
-	QDomElement elem;
-	QDomNode it = doc.documentElement().firstChild();
-	for( ; !it.isNull(); it = it.nextSibling() ) {
-	        QDomElement e = it.toElement();
-		if( e.tagName() == tagActionProp ) {
-			elem = e;
-			break;
-		}
-	}
-
-	// if there was none, create one
-	if( elem.isNull() ) {
-		elem = doc.createElement( tagActionProp );
-		doc.documentElement().appendChild( elem );
-	}
+        // Get hold of ActionProperties tag
+        QDomElement elem = KXMLGUIFactory::actionPropertiesElement( doc );
 
 	// now, iterate through our actions
 	uint nSize = count();
@@ -749,23 +732,10 @@ bool KActionShortcutList::save() const
 		//kdDebug(129) << "name = " << sName << " shortcut = " << shortcut(i).toStringInternal() << \
" def = " << shortcutDefault(i).toStringInternal() << endl;  
 		// now see if this element already exists
-		QDomElement act_elem;
-		for( it = elem.firstChild(); !it.isNull(); it = it.nextSibling() ) {
-			QDomElement e = it.toElement();		
-			if( e.attribute( attrName ) == sName ) {
-				act_elem = e;
-				break;
-			}
-		}
-
-		// nope, create a new one
-		if( act_elem.isNull() ) {
-			if( bSameAsDefault )
-				continue;
-			//kdDebug(129) << "\tnode doesn't exist." << endl;
-			act_elem = doc.createElement( tagAction );
-			act_elem.setAttribute( attrName, sName );
-		}
+                // and create it if necessary (unless bSameAsDefault)
+		QDomElement act_elem = KXMLGUIFactory::findActionByName( elem, sName, !bSameAsDefault );
+                if ( act_elem.isNull() )
+                    continue;
 
 		act_elem.removeAttribute( attrAccel );
 		if( bSameAsDefault ) {
@@ -775,7 +745,6 @@ bool KActionShortcutList::save() const
 				elem.removeChild( act_elem );
 		} else {
 			act_elem.setAttribute( attrShortcut, shortcut(i).toStringInternal() );
-			elem.appendChild( act_elem );
 		}
 	}
 
Index: kedittoolbar.cpp
===================================================================
RCS file: /home/kde/kdelibs/kdeui/kedittoolbar.cpp,v
retrieving revision 1.83
diff -u -p -r1.83 kedittoolbar.cpp
--- kedittoolbar.cpp	7 Apr 2004 10:47:23 -0000	1.83
+++ kedittoolbar.cpp	28 Apr 2004 10:01:45 -0000
@@ -42,6 +42,7 @@
 #include <qtextstream.h>
 #include <qfile.h>
 #include <kdebug.h>
+#include "kpushbutton.h"
 
 #define LINESEPARATORSTRING i18n("--- line separator ---")
 #define SEPARATORSTRING i18n("--- separator ---")
@@ -171,6 +172,22 @@ public:
     return list;
   }
 
+  /**
+   * Look for a given item in the current toolbar
+   */
+  QDomElement findElementForToolbarItem( const ToolbarItem* item ) const
+  {
+    static const QString &attrName    = KGlobal::staticQString( "name" );
+    QDomElement elem = m_currentToolbarElem.firstChild().toElement();
+    for( ; !elem.isNull(); elem = elem.nextSibling().toElement())
+    {
+      if ((elem.attribute(attrName) == item->internalName()) &&
+          (elem.tagName() == item->internalTag()))
+        return elem;
+    }
+    return QDomElement();
+  }
+
   QValueList<KAction*> m_actionList;
   KActionCollection* m_collection;
   KInstance         *m_instance;
@@ -189,7 +206,7 @@ public:
   XmlDataList m_xmlFiles;
 
   QLabel * m_helpArea;
-
+  KPushButton* m_changeIcon;
 };
 
 class KEditToolbarPrivate {
@@ -499,8 +516,8 @@ void KEditToolbarWidget::setupLayout()
   m_inactiveList->setAllColumnsShowFocus(true);
   m_inactiveList->setMinimumSize(180, 250);
   m_inactiveList->header()->hide();
-  m_inactiveList->addColumn("");
-  int column2 = m_inactiveList->addColumn("");
+  m_inactiveList->addColumn(""); // icon
+  int column2 = m_inactiveList->addColumn(""); // text
   m_inactiveList->setSorting( column2 );
   inactive_label->setBuddy(m_inactiveList);
   connect(m_inactiveList, SIGNAL(selectionChanged(QListViewItem *)),
@@ -512,14 +529,21 @@ void KEditToolbarWidget::setupLayout()
   m_activeList->setAllColumnsShowFocus(true);
   m_activeList->setMinimumWidth(m_inactiveList->minimumWidth());
   m_activeList->header()->hide();
-  m_activeList->addColumn("");
-  m_activeList->addColumn("");
-  m_activeList->setSorting (-1);
+  m_activeList->addColumn(""); // icon
+  m_activeList->addColumn(""); // text
+  m_activeList->setSorting(-1);
   active_label->setBuddy(m_activeList);
 
   connect(m_activeList, SIGNAL(selectionChanged(QListViewItem *)),
           this,         SLOT(slotActiveSelected(QListViewItem *)));
 
+  // "change icon" button
+  d->m_changeIcon = new KPushButton( i18n( "Change Icon" ), this );
+
+  connect( d->m_changeIcon, SIGNAL( clicked() ),
+           this, SLOT( slotChangeIcon() ) );
+
+  // The buttons in the middle
   QIconSet iconSet;
 
   m_upAction     = new QToolButton(this);
@@ -559,6 +583,7 @@ void KEditToolbarWidget::setupLayout()
 
   QVBoxLayout *inactive_layout = new QVBoxLayout(KDialog::spacingHint());
   QVBoxLayout *active_layout = new QVBoxLayout(KDialog::spacingHint());
+  QHBoxLayout *changeIcon_layout = new QHBoxLayout(KDialog::spacingHint());
 
   QGridLayout *button_layout = new QGridLayout(5, 3, 0);
 
@@ -579,6 +604,11 @@ void KEditToolbarWidget::setupLayout()
 
   active_layout->addWidget(active_label);
   active_layout->addWidget(m_activeList, 1);
+  active_layout->addLayout(changeIcon_layout);
+
+  changeIcon_layout->addStretch( 1 );
+  changeIcon_layout->addWidget( d->m_changeIcon );
+  changeIcon_layout->addStretch( 1 );
 
   list_layout->addLayout(inactive_layout);
   list_layout->addLayout(button_layout);
@@ -822,10 +852,11 @@ void KEditToolbarWidget::slotInactiveSel
 
 void KEditToolbarWidget::slotActiveSelected(QListViewItem *item)
 {
+  m_removeAction->setEnabled( item != 0 );
+  d->m_changeIcon->setEnabled( item != 0 );
+
   if (item)
   {
-    m_removeAction->setEnabled(true);
-
     if (item->itemAbove())
       m_upAction->setEnabled(true);
     else
@@ -840,7 +871,6 @@ void KEditToolbarWidget::slotActiveSelec
   }
   else
   {
-    m_removeAction->setEnabled(false);
     m_upAction->setEnabled(false);
     m_downAction->setEnabled(false);
     d->m_helpArea->setText( QString::null );
@@ -875,16 +905,9 @@ void KEditToolbarWidget::slotInsertButto
     // we have a selected item in the active list.. so let's try
     // our best to add our new item right after the selected one
     ToolbarItem *act_item = (ToolbarItem*)m_activeList->currentItem();
-    QDomElement elem = d->m_currentToolbarElem.firstChild().toElement();
-    for( ; !elem.isNull(); elem = elem.nextSibling().toElement())
-    {
-      if ((elem.attribute(attrName) == act_item->internalName()) &&
-          (elem.tagName() == act_item->internalTag()))
-      {
-        d->m_currentToolbarElem.insertAfter(new_item, elem);
-        break;
-      }
-    }
+    QDomElement elem = d->findElementForToolbarItem( act_item );
+    Q_ASSERT( !elem.isNull() );
+    d->m_currentToolbarElem.insertAfter(new_item, elem);
   }
   else
   {
@@ -903,7 +926,6 @@ void KEditToolbarWidget::slotInsertButto
 
 void KEditToolbarWidget::slotRemoveButton()
 {
-  static const QString &attrName    = KGlobal::staticQString( "name" );
   static const QString &attrNoMerge = KGlobal::staticQString( "noMerge" );
 
   // we're modified, so let this change
@@ -911,22 +933,17 @@ void KEditToolbarWidget::slotRemoveButto
 
   ToolbarItem *item = (ToolbarItem*)m_activeList->currentItem();
   // now iterate through to find the child to nuke
-  QDomElement elem = d->m_currentToolbarElem.firstChild().toElement();
-  for( ; !elem.isNull(); elem = elem.nextSibling().toElement())
+  QDomElement elem = d->findElementForToolbarItem( item );
+  if ( !elem.isNull() )
   {
-    if ((elem.attribute(attrName) == item->internalName()) &&
-        (elem.tagName() == item->internalTag()))
-    {
-      // nuke myself!
-      d->m_currentToolbarElem.removeChild(elem);
+    // nuke myself!
+    d->m_currentToolbarElem.removeChild(elem);
 
-      // and set this container as a noMerge
-      d->m_currentToolbarElem.setAttribute( attrNoMerge, "1");
+    // and set this container as a noMerge
+    d->m_currentToolbarElem.setAttribute( attrNoMerge, "1");
 
-      // update the local doc
-      updateLocal(d->m_currentToolbarElem);
-      break;
-    }
+    // update the local doc
+    updateLocal(d->m_currentToolbarElem);
   }
   slotToolbarSelected( m_toolbarCombo->currentText() );
 }
@@ -939,55 +956,48 @@ void KEditToolbarWidget::slotUpButton()
   if (!item->itemAbove())
     return;
 
-  static const QString &attrName    = KGlobal::staticQString( "name" );
   static const QString &attrNoMerge = KGlobal::staticQString( "noMerge" );
 
   // we're modified, so let this change
   emit enableOk(true);
 
   // now iterate through to find where we are
-  QDomElement elem = d->m_currentToolbarElem.firstChild().toElement();
-  for( ; !elem.isNull(); elem = elem.nextSibling().toElement())
+  QDomElement elem = d->findElementForToolbarItem( item );
+  if ( !elem.isNull() )
   {
-    if ((elem.attribute(attrName) == item->internalName()) &&
-        (elem.tagName() == item->internalTag()))
-    {
-      // cool, i found me.  now clone myself
-      ToolbarItem *clone = new ToolbarItem(m_activeList,
-                                           item->itemAbove()->itemAbove(),
-                                           item->internalTag(),
-                                           item->internalName(),
-                                           item->statusText());
-      clone->setText(1, item->text(1));
-
-      // only set new pixmap if exists
-      if( item->pixmap(0) )
-        clone->setPixmap(0, *item->pixmap(0));
-
-      // remove the old me
-      m_activeList->takeItem(item);
-      delete item;
-
-      // select my clone
-      m_activeList->setSelected(clone, true);
-
-      // make clone visible
-      m_activeList->ensureItemVisible(clone);
-
-      // and do the real move in the DOM
-      QDomNode prev = elem.previousSibling();
-      while ( prev.toElement().tagName() == QString( "WeakSeparator" ) )
+    // cool, i found me.  now clone myself
+    ToolbarItem *clone = new ToolbarItem(m_activeList,
+                                         item->itemAbove()->itemAbove(),
+                                         item->internalTag(),
+                                         item->internalName(),
+                                         item->statusText());
+    clone->setText(1, item->text(1));
+
+    // only set new pixmap if exists
+    if( item->pixmap(0) )
+      clone->setPixmap(0, *item->pixmap(0));
+
+    // remove the old me
+    m_activeList->takeItem(item);
+    delete item;
+
+    // select my clone
+    m_activeList->setSelected(clone, true);
+
+    // make clone visible
+    m_activeList->ensureItemVisible(clone);
+
+    // and do the real move in the DOM
+    QDomNode prev = elem.previousSibling();
+    while ( prev.toElement().tagName() == QString( "WeakSeparator" ) )
         prev = prev.previousSibling();
-      d->m_currentToolbarElem.insertBefore(elem, prev);
+    d->m_currentToolbarElem.insertBefore(elem, prev);
 
-      // and set this container as a noMerge
-      d->m_currentToolbarElem.setAttribute( attrNoMerge, "1");
+    // and set this container as a noMerge
+    d->m_currentToolbarElem.setAttribute( attrNoMerge, "1");
 
-      // update the local doc
-      updateLocal(d->m_currentToolbarElem);
-
-      break;
-    }
+    // update the local doc
+    updateLocal(d->m_currentToolbarElem);
   }
 }
 
@@ -999,55 +1009,48 @@ void KEditToolbarWidget::slotDownButton(
   if (!item->itemBelow())
     return;
 
-  static const QString &attrName    = KGlobal::staticQString( "name" );
   static const QString &attrNoMerge = KGlobal::staticQString( "noMerge" );
 
   // we're modified, so let this change
   emit enableOk(true);
 
   // now iterate through to find where we are
-  QDomElement elem = d->m_currentToolbarElem.firstChild().toElement();
-  for( ; !elem.isNull(); elem = elem.nextSibling().toElement())
+  QDomElement elem = d->findElementForToolbarItem( item );
+  if ( !elem.isNull() )
   {
-    if ((elem.attribute(attrName) == item->internalName()) &&
-        (elem.tagName() == item->internalTag()))
-    {
-      // cool, i found me.  now clone myself
-      ToolbarItem *clone = new ToolbarItem(m_activeList,
-                                           item->itemBelow(),
-                                           item->internalTag(),
-                                           item->internalName(),
-                                           item->statusText());
-      clone->setText(1, item->text(1));
-
-      // only set new pixmap if exists
-      if( item->pixmap(0) )
-        clone->setPixmap(0, *item->pixmap(0));
-
-      // remove the old me
-      m_activeList->takeItem(item);
-      delete item;
-
-      // select my clone
-      m_activeList->setSelected(clone, true);
-
-      // make clone visible
-      m_activeList->ensureItemVisible(clone);
-
-      // and do the real move in the DOM
-      QDomNode next = elem.nextSibling();
-      while ( next.toElement().tagName() == QString( "WeakSeparator" ) )
-        next = next.nextSibling();
-      d->m_currentToolbarElem.insertAfter(elem, next);
-
-      // and set this container as a noMerge
-      d->m_currentToolbarElem.setAttribute( attrNoMerge, "1");
+    // cool, i found me.  now clone myself
+    ToolbarItem *clone = new ToolbarItem(m_activeList,
+                                         item->itemBelow(),
+                                         item->internalTag(),
+                                         item->internalName(),
+                                         item->statusText());
+    clone->setText(1, item->text(1));
+
+    // only set new pixmap if exists
+    if( item->pixmap(0) )
+      clone->setPixmap(0, *item->pixmap(0));
+
+    // remove the old me
+    m_activeList->takeItem(item);
+    delete item;
+
+    // select my clone
+    m_activeList->setSelected(clone, true);
+
+    // make clone visible
+    m_activeList->ensureItemVisible(clone);
+
+    // and do the real move in the DOM
+    QDomNode next = elem.nextSibling();
+    while ( next.toElement().tagName() == QString( "WeakSeparator" ) )
+      next = next.nextSibling();
+    d->m_currentToolbarElem.insertAfter(elem, next);
 
-      // update the local doc
-      updateLocal(d->m_currentToolbarElem);
+    // and set this container as a noMerge
+    d->m_currentToolbarElem.setAttribute( attrNoMerge, "1");
 
-      break;
-    }
+    // update the local doc
+    updateLocal(d->m_currentToolbarElem);
   }
 }
 
@@ -1094,6 +1097,45 @@ void KEditToolbarWidget::updateLocal(QDo
   }
 }
 
+void KEditToolbarWidget::slotChangeIcon()
+{
+  // we're modified, so let this change
+  emit enableOk(true);
+
+  ToolbarItem *item = (ToolbarItem*)m_activeList->currentItem();
+  /// ############ TODO icon chooser. But how? It's in libkio (kfile/)....
+  QString icon = "www";
+  item->setPixmap(0, BarIcon(icon, 16));
+
+  // Very much like the beginning of updateLocal
+  XmlDataList::Iterator xit = d->m_xmlFiles.begin();
+  for ( ; xit != d->m_xmlFiles.end(); ++xit)
+  {
+    if ( (*xit).m_type == XmlData::Merged )
+      continue;
+
+    if ( (*xit).m_type == XmlData::Shell ||
+         (*xit).m_type == XmlData::Part )
+    {
+      if ( d->m_currentXmlData.m_xmlFile == (*xit).m_xmlFile )
+      {
+        (*xit).m_isModified = true;
+        return;
+      }
+      continue;
+    }
+
+    (*xit).m_isModified = true;
+
+    // Get hold of ActionProperties tag
+    QDomElement elem = KXMLGUIFactory::actionPropertiesElement( (*xit).m_document );
+    // Find or create an element for this action
+    QDomElement act_elem = KXMLGUIFactory::findActionByName( elem, item->internalName(), true \
/*create*/ ); +    Q_ASSERT( !act_elem.isNull() );
+    act_elem.setAttribute( "icon", icon );
+  }
+}
+
 void KEditToolbar::virtual_hook( int id, void* data )
 { KDialogBase::virtual_hook( id, data ); }
 
Index: kedittoolbar.h
===================================================================
RCS file: /home/kde/kdelibs/kdeui/kedittoolbar.h,v
retrieving revision 1.36
diff -u -p -r1.36 kedittoolbar.h
--- kedittoolbar.h	17 Aug 2003 19:24:45 -0000	1.36
+++ kedittoolbar.h	28 Apr 2004 10:01:45 -0000
@@ -1,3 +1,4 @@
+// -*- mode: c++; c-basic-offset: 2 -*-
 /* This file is part of the KDE libraries
    Copyright (C) 2000 Kurt Granroth <granroth@kde.org>
 
@@ -376,6 +377,8 @@ protected slots:
   void slotUpButton();
   void slotDownButton();
 
+  void slotChangeIcon();
+
 protected:
   void setupLayout();
 
Index: kxmlguifactory.cpp
===================================================================
RCS file: /home/kde/kdelibs/kdeui/kxmlguifactory.cpp,v
retrieving revision 1.143
diff -u -p -r1.143 kxmlguifactory.cpp
--- kxmlguifactory.cpp	29 Mar 2004 21:30:53 -0000	1.143
+++ kxmlguifactory.cpp	28 Apr 2004 10:01:46 -0000
@@ -534,7 +534,7 @@ void KXMLGUIFactory::configureAction( KA
     else
         propertyValue = QVariant( attribute.value() );
 
-    action->setProperty( attrName.latin1() /* ???????? */, propertyValue );
+    action->setProperty( attrName.latin1(), propertyValue );
 }
 
 
@@ -552,6 +552,46 @@ int KXMLGUIFactory::configureShortcuts(b
 	return dlg.configure(bSaveSettings);
 }
 
+QDomElement KXMLGUIFactory::actionPropertiesElement( QDomDocument& doc )
+{
+	const QString tagActionProp = QString::fromLatin1("ActionProperties");
+	// first, lets see if we have existing properties
+	QDomElement elem;
+	QDomNode it = doc.documentElement().firstChild();
+	for( ; !it.isNull(); it = it.nextSibling() ) {
+		QDomElement e = it.toElement();
+		if( e.tagName() == tagActionProp ) {
+			elem = e;
+			break;
+		}
+	}
+
+	// if there was none, create one
+	if( elem.isNull() ) {
+		elem = doc.createElement( tagActionProp );
+		doc.documentElement().appendChild( elem );
+	}
+	return elem;
+}
+
+QDomElement KXMLGUIFactory::findActionByName( QDomElement& elem, const QString& sName, bool \
create ) +{
+        static const QString& attrName = KGlobal::staticQString( "name" );
+	static const QString& tagAction = KGlobal::staticQString( "Action" );
+	for( QDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling() ) {
+		QDomElement e = it.toElement();
+		if( e.attribute( attrName ) == sName )
+			return e;
+	}
+
+	if( create ) {
+		QDomElement act_elem = elem.ownerDocument().createElement( tagAction );
+		act_elem.setAttribute( attrName, sName );
+                elem.appendChild( act_elem );
+                return act_elem;
+	}
+        return QDomElement();
+}
 
 void KXMLGUIFactory::virtual_hook( int, void* )
 { /*BASE::virtual_hook( id, data );*/ }
Index: kxmlguifactory.h
===================================================================
RCS file: /home/kde/kdelibs/kdeui/kxmlguifactory.h,v
retrieving revision 1.78
diff -u -p -r1.78 kxmlguifactory.h
--- kxmlguifactory.h	23 Feb 2004 18:24:16 -0000	1.78
+++ kxmlguifactory.h	28 Apr 2004 10:01:46 -0000
@@ -1,3 +1,4 @@
+// -*- mode: c++; c-basic-offset: 2 -*-
 /* This file is part of the KDE libraries
    Copyright (C) 1999 Simon Hausmann <hausmann@kde.org>
    Copyright (C) 2000 Kurt Granroth <granroth@kde.org>
@@ -90,6 +91,19 @@ class KXMLGUIFactory : public QObject
   static void removeDOMComments( QDomNode &node );
 
   /**
+   * @internal
+   * Find or create the ActionProperties element, used when saving custom action properties
+   */
+  static QDomElement actionPropertiesElement( QDomDocument& doc );
+
+  /**
+   * @internal
+   * Find or create the element for a given action, by name.
+   * Used when saving custom action properties
+   */
+  static QDomElement findActionByName( QDomElement& elem, const QString& sName, bool create );
+
+  /**
    * Creates the GUI described by the QDomDocument of the client,
    * using the client's actions, and merges it with the previously
    * created GUI.



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic