diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 4aed2c8219774f5d797760606b8489a92ddc5163 (patch) | |
tree | 3f8c130f7d269626bf6a9447407ef6c35954426a /kicker/applets | |
download | tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kicker/applets')
98 files changed, 17636 insertions, 0 deletions
diff --git a/kicker/applets/Makefile.am b/kicker/applets/Makefile.am new file mode 100644 index 000000000..6079814ce --- /dev/null +++ b/kicker/applets/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = clock systemtray minipager taskbar run launcher naughty lockout menu media trash diff --git a/kicker/applets/clock/Makefile.am b/kicker/applets/clock/Makefile.am new file mode 100644 index 000000000..b87e0d8e7 --- /dev/null +++ b/kicker/applets/clock/Makefile.am @@ -0,0 +1,29 @@ +pic_DATA = lcd.png +picdir = $(kde_datadir)/clockapplet/pics + +INCLUDES = -I$(top_srcdir)/kicker/libkicker -I../../libkicker $(all_includes) + +kde_module_LTLIBRARIES = clock_panelapplet.la + +clock_panelapplet_la_SOURCES = clock.skel clock.cpp datepicker.cpp zone.cpp analog.ui digital.ui fuzzy.ui settings.ui prefs.kcfgc + +METASOURCES = AUTO +noinst_HEADERS = clock.h datepicker.h zone.h + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = clockapplet.desktop + +EXTRA_DIST = $(lnk_DATA) $(pic_DATA) + +clock_panelapplet_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +clock_panelapplet_la_LIBADD = ../../libkicker/libkickermain.la $(LIB_KDEUI) + +srcdoc: + kdoc -a -p -H -d $$HOME/web/src/clockapplet clockapplet *.h -lqt -lkdecore -lkdeui -lkfile + +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/clockapplet.pot + +KDE_OPTIONS=nofinal + +clock_skel.lo: settings.h diff --git a/kicker/applets/clock/analog.ui b/kicker/applets/clock/analog.ui new file mode 100644 index 000000000..4a20312ec --- /dev/null +++ b/kicker/applets/clock/analog.ui @@ -0,0 +1,344 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>AnalogWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>AnalogWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>524</width> + <height>307</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>ButtonGroup2_3_2</cstring> + </property> + <property name="title"> + <string>Display</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_AnalogShowDate</cstring> + </property> + <property name="text"> + <string>Dat&e</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_AnalogShowSeconds</cstring> + </property> + <property name="text"> + <string>Seco&nds</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_AnalogShowDayOfWeek</cstring> + </property> + <property name="text"> + <string>Da&y of week</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_AnalogShowFrame</cstring> + </property> + <property name="text"> + <string>&Frame</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer20</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Time</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KColorButton" row="2" column="1"> + <property name="name"> + <cstring>kcfg_AnalogBackgroundColor</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="1" column="1"> + <property name="name"> + <cstring>kcfg_AnalogShadowColor</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>foregroundAnalogLabel</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Foreground color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_AnalogForegroundColor</cstring> + </property> + </widget> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer13</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>backgroundAnalogLabel</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Background color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_AnalogBackgroundColor</cstring> + </property> + </widget> + <widget class="KColorButton" row="0" column="1"> + <property name="name"> + <cstring>kcfg_AnalogForegroundColor</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>shadowAnalogLabel</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Shadow color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_AnalogShadowColor</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel1_3</cstring> + </property> + <property name="text"> + <string>Antialias:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_AnalogAntialias</cstring> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>None</string> + </property> + </item> + <item> + <property name="text"> + <string>Low Quality</string> + </property> + </item> + <item> + <property name="text"> + <string>High Quality</string> + </property> + </item> + <property name="name"> + <cstring>kcfg_AnalogAntialias</cstring> + </property> + <property name="currentItem"> + <number>0</number> + </property> + </widget> + <spacer row="0" column="2"> + <property name="name"> + <cstring>Spacer18_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>310</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_AnalogLCDStyle</cstring> + </property> + <property name="text"> + <string>&LCD look</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <spacer row="3" column="2"> + <property name="name"> + <cstring>spacer54</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>50</height> + </size> + </property> + </spacer> + </grid> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>kcfg_AnalogLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>foregroundAnalogLabel</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_AnalogLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>backgroundAnalogLabel</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_AnalogLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>shadowAnalogLabel</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_AnalogLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_AnalogForegroundColor</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_AnalogLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_AnalogShadowColor</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_AnalogLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_AnalogBackgroundColor</receiver> + <slot>setDisabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>kcfg_AnalogShowDate</tabstop> + <tabstop>kcfg_AnalogShowSeconds</tabstop> + <tabstop>kcfg_AnalogShowFrame</tabstop> + <tabstop>kcfg_AnalogAntialias</tabstop> + <tabstop>kcfg_AnalogLCDStyle</tabstop> + <tabstop>kcfg_AnalogForegroundColor</tabstop> + <tabstop>kcfg_AnalogShadowColor</tabstop> + <tabstop>kcfg_AnalogBackgroundColor</tabstop> +</tabstops> +<includes> + <include location="local" impldecl="in implementation">kdialog.h</include> + <include location="local" impldecl="in implementation">kfontrequester.h</include> +</includes> +<layoutdefaults spacing="3" margin="6"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +<includehints> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> +</includehints> +</UI> diff --git a/kicker/applets/clock/clock.cpp b/kicker/applets/clock/clock.cpp new file mode 100644 index 000000000..19e91be5c --- /dev/null +++ b/kicker/applets/clock/clock.cpp @@ -0,0 +1,1871 @@ +/************************************************************ + +Copyright (c) 1996-2002 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <cstdlib> +#include <ctime> +#include <time.h> + +#include <qcheckbox.h> +#include <qcursor.h> +#include <qgroupbox.h> +#include <qimage.h> +#include <qpainter.h> +#include <qtimer.h> +#include <qtooltip.h> +#include <qclipboard.h> +#include <qtabwidget.h> +#include <qwidgetstack.h> +#include <qcombobox.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <kcolorbutton.h> +#include <kiconloader.h> +#include <kstandarddirs.h> +#include <kapplication.h> +#include <kprocess.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kstringhandler.h> +#include <kfiledialog.h> +#include <kfontrequester.h> +#include <kglobalsettings.h> +#include <kconfigdialogmanager.h> +#include <kcalendarsystem.h> +#include <kicontheme.h> +#include <kiconloader.h> + +#include <global.h> // libkickermain + +#include "kickerSettings.h" +#include "clock.h" +#include "datepicker.h" +#include "zone.h" +#include "analog.h" +#include "digital.h" +#include "fuzzy.h" +#include "prefs.h" + +extern "C" +{ + KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile) + { + KGlobal::locale()->insertCatalogue("clockapplet"); + KGlobal::locale()->insertCatalogue("timezones"); // For time zone translations + return new ClockApplet(configFile, KPanelApplet::Normal, + KPanelApplet::Preferences, parent, "clockapplet"); + } +} + +// Settings + +KConfigDialogSingle::KConfigDialogSingle(Zone *zone, QWidget *parent, + const char *name, Prefs * prefs, + KDialogBase::DialogType dialogType, + bool modal) : + KConfigDialog(parent, name, prefs, dialogType, + KDialogBase::Default | KDialogBase::Ok | + KDialogBase::Apply | KDialogBase::Cancel, + KDialogBase::Ok, + modal), _prefs(prefs) +{ + // As a temporary mesure until the kicker applet's app name is set to the + // applets name so KDialogBase gets the right info. + setPlainCaption(i18n("Configure - Clock")); + setIcon(SmallIcon("date")); + + settings = new SettingsWidgetImp(prefs, zone, 0, "General"); + connect(settings->kcfg_Type, SIGNAL(activated(int)), SLOT(selectPage(int))); + + settings->kcfg_PlainBackgroundColor->setDefaultColor(KApplication::palette().active().background()); + settings->kcfg_DateBackgroundColor->setDefaultColor(KApplication::palette().active().background()); + + // Digital + digitalPage = new DigitalWidget(0, "DigitalClock"); + settings->widgetStack->addWidget(digitalPage, 1); + digitalPage->kcfg_DigitalBackgroundColor->setDefaultColor(KApplication::palette().active().background()); + + // Analog + analogPage = new AnalogWidget(0, "AnalogClock"); + settings->widgetStack->addWidget(analogPage, 2); + analogPage->kcfg_AnalogBackgroundColor->setDefaultColor(KApplication::palette().active().background()); + + // Fuzzy + fuzzyPage = new FuzzyWidget(0, "FuzzyClock"); + settings->widgetStack->addWidget(fuzzyPage, 3); + fuzzyPage->kcfg_FuzzyBackgroundColor->setDefaultColor(KApplication::palette().active().background()); + + connect(settings->kcfg_PlainShowDate, SIGNAL(toggled(bool)), + SLOT(dateToggled())); + connect(settings->kcfg_PlainShowDayOfWeek, SIGNAL(toggled(bool)), + SLOT(dateToggled())); + connect(digitalPage->kcfg_DigitalShowDate, SIGNAL(toggled(bool)), + SLOT(dateToggled())); + connect(digitalPage->kcfg_DigitalShowDayOfWeek, SIGNAL(toggled(bool)), + SLOT(dateToggled())); + connect(digitalPage->kcfg_DigitalShowDate, SIGNAL(toggled(bool)), + SLOT(dateToggled())); + connect(analogPage->kcfg_AnalogShowDate, SIGNAL(toggled(bool)), + SLOT(dateToggled())); + connect(analogPage->kcfg_AnalogShowDayOfWeek, SIGNAL(toggled(bool)), + SLOT(dateToggled())); + connect(fuzzyPage->kcfg_FuzzyShowDate, SIGNAL(toggled(bool)), + SLOT(dateToggled())); + connect(fuzzyPage->kcfg_FuzzyShowDayOfWeek, SIGNAL(toggled(bool)), + SLOT(dateToggled())); + + addPage(settings, i18n("General"), QString::fromLatin1("package_settings")); +} + +void KConfigDialogSingle::updateSettings() +{ + settings->OkApply(); +} + +void KConfigDialogSingle::updateWidgets() +{ + selectPage( _prefs->type() ); +} + +void KConfigDialogSingle::updateWidgetsDefault() +{ + KConfigSkeletonItem *item = _prefs->findItem("Type"); + item->swapDefault(); + selectPage( _prefs->type() ); + item->swapDefault(); + // This is ugly, but kcfg_Type does not have its correct setting + // at this point in time. + QTimer::singleShot(0, this, SLOT(dateToggled())); +} + +void KConfigDialogSingle::selectPage(int p) +{ + settings->widgetStack->raiseWidget( p ); + dateToggled(); +} + +void KConfigDialogSingle::dateToggled() +{ + bool showDate; + switch( settings->kcfg_Type->currentItem() ) + { + case Prefs::EnumType::Plain: + showDate = settings->kcfg_PlainShowDate->isChecked() || + settings->kcfg_PlainShowDayOfWeek->isChecked(); + break; + case Prefs::EnumType::Digital: + showDate = digitalPage->kcfg_DigitalShowDate->isChecked() || + digitalPage->kcfg_DigitalShowDayOfWeek->isChecked(); + break; + case Prefs::EnumType::Analog: + showDate = analogPage->kcfg_AnalogShowDate->isChecked() || + analogPage->kcfg_AnalogShowDayOfWeek->isChecked(); + break; + case Prefs::EnumType::Fuzzy: + default: + showDate = fuzzyPage->kcfg_FuzzyShowDate->isChecked() || + fuzzyPage->kcfg_FuzzyShowDayOfWeek->isChecked(); + break; + } + settings->dateBox->setEnabled(showDate); +} + +SettingsWidgetImp::SettingsWidgetImp(Prefs *p, Zone *z, QWidget* parent, const char* name, WFlags fl) : + SettingsWidget(parent, name, fl), prefs(p), zone(z) +{ + zone->readZoneList(tzListView); +} + +void SettingsWidgetImp::OkApply() +{ + zone->getSelectedZonelist(tzListView); + zone->writeSettings(); +} + +//************************************************************ + + +ClockWidget::ClockWidget(ClockApplet *applet, Prefs* prefs) + : _applet(applet), _prefs(prefs), _force(false) +{} + + +ClockWidget::~ClockWidget() +{} + + +//************************************************************ + + +PlainClock::PlainClock(ClockApplet *applet, Prefs *prefs, QWidget *parent, const char *name) + : QLabel(parent, name), ClockWidget(applet, prefs) +{ + setWFlags(WNoAutoErase); + setBackgroundOrigin(AncestorOrigin); + loadSettings(); + updateClock(); +} + + +int PlainClock::preferedWidthForHeight(int ) const +{ + QString maxLengthTime = KGlobal::locale()->formatTime( QTime( 23, 59 ), _prefs->plainShowSeconds()); + return fontMetrics().width( maxLengthTime ) + 8; +} + + +int PlainClock::preferedHeightForWidth(int /*w*/) const +{ + return fontMetrics().lineSpacing(); +} + + +void PlainClock::updateClock() +{ + QString newStr = KGlobal::locale()->formatTime(_applet->clockGetTime(), _prefs->plainShowSeconds()); + + if (_force || newStr != _timeStr) { + _timeStr = newStr; + update(); + } +} + +void PlainClock::loadSettings() +{ + setFrameStyle(_prefs->plainShowFrame() ? Panel | Sunken : NoFrame); + setAlignment(AlignVCenter | AlignHCenter | SingleLine); + + setFont(_prefs->plainFont()); +} + +bool PlainClock::showDate() +{ + return _prefs->plainShowDate(); +} + +bool PlainClock::showDayOfWeek() +{ + return _prefs->plainShowDayOfWeek(); +} + +void PlainClock::paintEvent(QPaintEvent *) +{ + QPainter p; + QPixmap buf(size()); + buf.fill(this, 0, 0); + p.begin(&buf); + p.setFont(font()); + p.setPen(paletteForegroundColor()); + drawContents(&p); + drawFrame(&p); + p.end(); + p.begin(this); + p.drawPixmap(0, 0, buf); + p.end(); +} + +void PlainClock::drawContents(QPainter *p) +{ + QRect tr(0, 0, width(), height()); + + if (!KickerSettings::transparent()) + p->drawText(tr, AlignCenter, _timeStr); + else + _applet->shadowEngine()->drawText(*p, tr, AlignCenter, _timeStr, size()); +} + +//************************************************************ + + +DigitalClock::DigitalClock(ClockApplet *applet, Prefs *prefs, QWidget *parent, const char *name) + : QLCDNumber(parent, name), ClockWidget(applet, prefs) +{ + setWFlags(WNoAutoErase); + setBackgroundOrigin(AncestorOrigin); + loadSettings(); + updateClock(); +} + + +DigitalClock::~DigitalClock() +{ + delete _buffer; +} + + +int DigitalClock::preferedWidthForHeight(int h) const +{ + if (h > 29) h = 29; + if (h < 0) h = 0; + return (numDigits()*h*5/11)+2; +} + + +int DigitalClock::preferedHeightForWidth(int w) const +{ + if (w < 0) w = 0; + return((w / numDigits() * 2) + 6); +} + + +void DigitalClock::updateClock() +{ + static bool colon = true; + QString newStr; + QTime t(_applet->clockGetTime()); + + int h = t.hour(); + int m = t.minute(); + int s = t.second(); + + QString format("%02d"); + + QString sep(!colon && _prefs->digitalBlink() ? " " : ":"); + + if (_prefs->digitalShowSeconds()) + format += sep + "%02d"; + + if (KGlobal::locale()->use12Clock()) { + if (h > 12) + h -= 12; + else if( h == 0) + h = 12; + + format.prepend("%2d" + sep); + } else + format.prepend("%02d" + sep); + + + if (_prefs->digitalShowSeconds()) + newStr.sprintf(format.latin1(), h, m, s); + else + newStr.sprintf(format.latin1(), h, m); + + if (_force || newStr != _timeStr) + { + _timeStr = newStr; + setUpdatesEnabled( FALSE ); + display(_timeStr); + setUpdatesEnabled( TRUE ); + update(); + } + + if (_prefs->digitalBlink()) + colon = !colon; +} + +void DigitalClock::loadSettings() +{ + setFrameStyle(_prefs->digitalShowFrame() ? Panel | Sunken : NoFrame); + setMargin( 4 ); + setSegmentStyle(QLCDNumber::Flat); + + if (_prefs->digitalLCDStyle()) + lcdPattern = KIconLoader("clockapplet").loadIcon("lcd", KIcon::User); + + setNumDigits(_prefs->digitalShowSeconds() ? 8:5); + + _buffer = new QPixmap(width(), height()); +} + +void DigitalClock::paintEvent(QPaintEvent*) +{ + QPainter p(_buffer); + + if (_prefs->digitalLCDStyle()) + { + p.drawTiledPixmap(0, 0, width(), height(), lcdPattern); + } + else if (_prefs->digitalBackgroundColor() != + KApplication::palette().active().background()) + { + p.fillRect(0, 0, width(), height(), _prefs->digitalBackgroundColor()); + } + else if (paletteBackgroundPixmap()) + { + QPoint offset = backgroundOffset(); + p.drawTiledPixmap(0, 0, width(), height(), *paletteBackgroundPixmap(), offset.x(), offset.y()); + } + else + { + p.fillRect(0, 0, width(), height(), _prefs->digitalBackgroundColor()); + } + + drawContents(&p); + if (_prefs->digitalShowFrame()) + { + drawFrame(&p); + } + + p.end(); + bitBlt(this, 0, 0, _buffer, 0, 0); +} + + +// yes, the colors for the lcd-lock are hardcoded, +// but other colors would break the lcd-lock anyway +void DigitalClock::drawContents( QPainter * p) +{ + setUpdatesEnabled( FALSE ); + QPalette pal = palette(); + if (_prefs->digitalLCDStyle()) + pal.setColor( QColorGroup::Foreground, QColor(128,128,128)); + else + pal.setColor( QColorGroup::Foreground, _prefs->digitalShadowColor()); + setPalette( pal ); + p->translate( +1, +1 ); + QLCDNumber::drawContents( p ); + if (_prefs->digitalLCDStyle()) + pal.setColor( QColorGroup::Foreground, Qt::black); + else + pal.setColor( QColorGroup::Foreground, _prefs->digitalForegroundColor()); + setPalette( pal ); + p->translate( -2, -2 ); + setUpdatesEnabled( TRUE ); + QLCDNumber::drawContents( p ); + p->translate( +1, +1 ); +} + + +// reallocate buffer pixmap +void DigitalClock::resizeEvent ( QResizeEvent *) +{ + delete _buffer; + _buffer = new QPixmap( width(), height() ); +} + + +bool DigitalClock::showDate() +{ + return _prefs->digitalShowDate(); +} + +bool DigitalClock::showDayOfWeek() +{ + return _prefs->digitalShowDayOfWeek(); +} + + +//************************************************************ + + +AnalogClock::AnalogClock(ClockApplet *applet, Prefs *prefs, QWidget *parent, const char *name) + : QFrame(parent, name), ClockWidget(applet, prefs), _spPx(NULL) +{ + setWFlags(WNoAutoErase); + setBackgroundOrigin(AncestorOrigin); + loadSettings(); +} + + +AnalogClock::~AnalogClock() +{ + delete _spPx; +} + +void AnalogClock::initBackgroundPixmap() +{ + //if no antialiasing, use pixmap as-is + if (_prefs->analogAntialias() == 0) + { + lcdPattern = KIconLoader("clockapplet").loadIcon("lcd",KIcon::User); + _bgScale = 1; + } + else + { + //make a scaled pixmap -- so when image is reduced it'll look "OK". + _bgScale = _prefs->analogAntialias()+1; + QImage bgImage = KIconLoader("clockapplet").loadIcon("lcd", KIcon::User).convertToImage(); + lcdPattern = QPixmap(bgImage.scale(bgImage.width() * _bgScale, + bgImage.height() * _bgScale)); + + } +} + +void AnalogClock::updateClock() +{ + if (!_force) + { + if (!_prefs->analogShowSeconds() && (_time.minute() == _applet->clockGetTime().minute())) + return; + } + + _time = _applet->clockGetTime(); + update(); +} + +void AnalogClock::loadSettings() +{ + if (_prefs->analogLCDStyle()) + { + initBackgroundPixmap(); + } +/* this may prevent flicker, but it also prevents transparency + else + { + setBackgroundMode(NoBackground); + }*/ + + setFrameStyle(_prefs->analogShowFrame() ? Panel | Sunken : NoFrame); + _time = _applet->clockGetTime(); + _spPx = new QPixmap(size().width() * _prefs->analogAntialias()+1, + size().height() * _prefs->analogAntialias()+1); + + update(); +} + +void AnalogClock::paintEvent( QPaintEvent * ) +{ + if ( !isVisible() ) + return; + + int aaFactor = _prefs->analogAntialias()+1; + int spWidth = size().width() * aaFactor; + int spHeight = size().height() * aaFactor; + + if ((spWidth != _spPx->size().width()) || + (spHeight != _spPx->size().height())) + { + delete _spPx; + _spPx = new QPixmap(spWidth, spHeight); + } + + QPainter paint; + paint.begin(_spPx); + + if (_prefs->analogLCDStyle()) + { + if (_bgScale != aaFactor) + { + //check to see if antialiasing has changed -- bg pixmap will need + //to be re-created + initBackgroundPixmap(); + } + + paint.drawTiledPixmap(0, 0, spWidth, spHeight, lcdPattern); + } + else if (_prefs->analogBackgroundColor() != KApplication::palette().active().background()) + { + _spPx->fill(_prefs->analogBackgroundColor()); + } + else if (paletteBackgroundPixmap()) + { + QPixmap bg(width(), height()); + QPainter p(&bg); + QPoint offset = backgroundOffset(); + p.drawTiledPixmap(0, 0, width(), height(), *paletteBackgroundPixmap(), offset.x(), offset.y()); + p.end(); + QImage bgImage = bg.convertToImage().scale(spWidth, spHeight); + paint.drawImage(0, 0, bgImage); + } + else + { + _spPx->fill(_prefs->analogBackgroundColor()); + } + + QPointArray pts; + QPoint cp(spWidth / 2, spHeight / 2); + + int d = KMIN(spWidth,spHeight) - (10 * aaFactor); + + if (_prefs->analogLCDStyle()) + { + paint.setPen( QPen(QColor(100,100,100), aaFactor) ); + paint.setBrush( QColor(100,100,100) ); + } + else + { + paint.setPen( QPen(_prefs->analogShadowColor(), aaFactor) ); + paint.setBrush( _prefs->analogShadowColor() ); + } + + paint.setViewport(2,2,spWidth,spHeight); + + for ( int c=0 ; c < 2 ; c++ ) { + QWMatrix matrix; + matrix.translate( cp.x(), cp.y()); + matrix.scale( d/1000.0F, d/1000.0F ); + + // hour + float h_angle = 30*(_time.hour()%12-3) + _time.minute()/2; + matrix.rotate( h_angle ); + paint.setWorldMatrix( matrix ); + pts.setPoints( 4, -20,0, 0,-20, 300,0, 0,20 ); + paint.drawPolygon( pts ); + matrix.rotate( -h_angle ); + + // minute + float m_angle = (_time.minute()-15)*6; + matrix.rotate( m_angle ); + paint.setWorldMatrix( matrix ); + pts.setPoints( 4, -10,0, 0,-10, 400,0, 0,10 ); + paint.drawPolygon( pts ); + matrix.rotate( -m_angle ); + + if (_prefs->analogShowSeconds()) { // second + float s_angle = (_time.second()-15)*6; + matrix.rotate( s_angle ); + paint.setWorldMatrix( matrix ); + pts.setPoints(4,0,0,0,0,400,0,0,0); + paint.drawPolygon( pts ); + matrix.rotate( -s_angle ); + } + + QWMatrix matrix2; + matrix2.translate( cp.x(), cp.y()); + matrix2.scale( d/1000.0F, d/1000.0F ); + + // quadrante + for ( int i=0 ; i < 12 ; i++ ) { + paint.setWorldMatrix( matrix2 ); + paint.drawLine( 460,0, 500,0 ); // draw hour lines + // paint.drawEllipse( 450, -15, 30, 30 ); + matrix2.rotate( 30 ); + } + + if (_prefs->analogLCDStyle()) { + paint.setPen( QPen(Qt::black, aaFactor) ); + paint.setBrush( Qt::black ); + } else { + paint.setPen( QPen(_prefs->analogForegroundColor(), aaFactor) ); + paint.setBrush( _prefs->analogForegroundColor() ); + } + + paint.setViewport(0,0,spWidth,spHeight); + } + paint.end(); + + QPainter paintFinal; + paintFinal.begin(this); + + if (aaFactor != 1) + { + QImage spImage = _spPx->convertToImage(); + QImage displayImage = spImage.smoothScale(size()); + + paintFinal.drawImage(0, 0, displayImage); + } + else + { + paintFinal.drawPixmap(0, 0, *_spPx); + } + + if (_prefs->analogShowFrame()) + { + drawFrame(&paintFinal); + } +} + + +// the background pixmap disappears during a style change +void AnalogClock::styleChange(QStyle &) +{ + if (_prefs->analogLCDStyle()) + { + initBackgroundPixmap(); + } +} + +bool AnalogClock::showDate() +{ + return _prefs->analogShowDate(); +} + +bool AnalogClock::showDayOfWeek() +{ + return _prefs->analogShowDayOfWeek(); +} + + +//************************************************************ + + +FuzzyClock::FuzzyClock(ClockApplet *applet, Prefs *prefs, QWidget *parent, const char *name) + : QFrame(parent, name), ClockWidget(applet, prefs) +{ + setBackgroundOrigin(AncestorOrigin); + loadSettings(); + hourNames << i18n("hour","one") << i18n("hour","two") + << i18n("hour","three") << i18n("hour","four") << i18n("hour","five") + << i18n("hour","six") << i18n("hour","seven") << i18n("hour","eight") + << i18n("hour","nine") << i18n("hour","ten") << i18n("hour","eleven") + << i18n("hour","twelve"); + + // xgettext:no-c-format + normalFuzzy << i18n("%0 o'clock") // xgettext:no-c-format + << i18n("five past %0") // xgettext:no-c-format + << i18n("ten past %0") // xgettext:no-c-format + << i18n("quarter past %0") // xgettext:no-c-format + << i18n("twenty past %0") // xgettext:no-c-format + << i18n("twenty five past %0") // xgettext:no-c-format + << i18n("half past %0") // xgettext:no-c-format + << i18n("twenty five to %1") // xgettext:no-c-format + << i18n("twenty to %1") // xgettext:no-c-format + << i18n("quarter to %1") // xgettext:no-c-format + << i18n("ten to %1") // xgettext:no-c-format + << i18n("five to %1") // xgettext:no-c-format + << i18n("%1 o'clock"); + + // xgettext:no-c-format + normalFuzzyOne << i18n("one","%0 o'clock") // xgettext:no-c-format + << i18n("one","five past %0") // xgettext:no-c-format + << i18n("one","ten past %0") // xgettext:no-c-format + << i18n("one","quarter past %0") // xgettext:no-c-format + << i18n("one","twenty past %0") // xgettext:no-c-format + << i18n("one","twenty five past %0") // xgettext:no-c-format + << i18n("one","half past %0") // xgettext:no-c-format + << i18n("one","twenty five to %1") // xgettext:no-c-format + << i18n("one","twenty to %1") // xgettext:no-c-format + << i18n("one","quarter to %1") // xgettext:no-c-format + << i18n("one","ten to %1") // xgettext:no-c-format + << i18n("one","five to %1") // xgettext:no-c-format + << i18n("one","%1 o'clock"); + + dayTime << i18n("Night") + << i18n("Early morning") << i18n("Morning") << i18n("Almost noon") + << i18n("Noon") << i18n("Afternoon") << i18n("Evening") + << i18n("Late evening"); + + _time = _applet->clockGetTime(); + alreadyDrawing=false; + update(); +} + +void FuzzyClock::deleteMyself() +{ + if(alreadyDrawing) // try again later + QTimer::singleShot(1000, this, SLOT(deleteMyself())); + else + delete this; +} + + +int FuzzyClock::preferedWidthForHeight(int ) const +{ + QFontMetrics fm(_prefs->fuzzyFont()); + return fm.width(_timeStr) + 8; +} + + +int FuzzyClock::preferedHeightForWidth(int ) const +{ + QFontMetrics fm(_prefs->fuzzyFont()); + return fm.width(_timeStr) + 8; +} + + +void FuzzyClock::updateClock() +{ + if (!_force) + { + if (_time.hour() == _applet->clockGetTime().hour() && + _time.minute() == _applet->clockGetTime().minute()) + return; + } + + _time = _applet->clockGetTime(); + update(); +} + +void FuzzyClock::loadSettings() +{ + setFrameStyle(_prefs->fuzzyShowFrame() ? Panel | Sunken : 0); +} + +void FuzzyClock::drawContents(QPainter *p) +{ + if (!isVisible()) + return; + + if(!_applet) + return; + + alreadyDrawing = true; + QString newTimeStr; + + if (_prefs->fuzzyness() == 1 || _prefs->fuzzyness() == 2) { + int minute = _time.minute(); + int sector = 0; + int realHour = 0; + + if (_prefs->fuzzyness() == 1) { + if (minute > 2) + sector = (minute - 3) / 5 + 1; + } else { + if (minute > 6) + sector = ((minute - 7) / 15 + 1) * 3; + } + + newTimeStr = normalFuzzy[sector]; + int phStart = newTimeStr.find("%"); + if (phStart >= 0) { // protect yourself from translations + int phLength = newTimeStr.find(" ", phStart) - phStart; + + // larrosa: we want the exact length, in case the translation needs it, + // in other case, we would cut off the end of the translation. + if (phLength < 0) phLength = newTimeStr.length() - phStart; + int deltaHour = newTimeStr.mid(phStart + 1, phLength - 1).toInt(); + + if ((_time.hour() + deltaHour) % 12 > 0) + realHour = (_time.hour() + deltaHour) % 12 - 1; + else + realHour = 12 - ((_time.hour() + deltaHour) % 12 + 1); + if (realHour==0) { + newTimeStr = normalFuzzyOne[sector]; + phStart = newTimeStr.find("%"); + // larrosa: Note that length is the same, + // so we only have to update phStart + } + if (phStart >= 0) + newTimeStr.replace(phStart, phLength, hourNames[realHour]); + newTimeStr.replace(0, 1, QString(newTimeStr.at(0).upper())); + } + } else if (_prefs->fuzzyness() == 3) { + newTimeStr = dayTime[_time.hour() / 3]; + } else { + int dow = _applet->clockGetDate().dayOfWeek(); + + if (dow == 1) + newTimeStr = i18n("Start of week"); + else if (dow >= 2 && dow <= 4) + newTimeStr = i18n("Middle of week"); + else if (dow == 5) + newTimeStr = i18n("End of week"); + else + newTimeStr = i18n("Weekend!"); + } + + if (_timeStr != newTimeStr) { + _timeStr = newTimeStr; + _applet->resizeRequest(); + } + + p->setFont(_prefs->fuzzyFont()); + p->setPen(_prefs->fuzzyForegroundColor()); + + QRect tr; + + if (_applet->getOrientation() == Vertical) + { + p->rotate(90); + tr = QRect(4, -2, height() - 8, -(width()) + 2); + } + else + tr = QRect(4, 2, width() - 8, height() - 4); + + if (!KickerSettings::transparent()) + p->drawText(tr, AlignCenter, _timeStr); + else + _applet->shadowEngine()->drawText(*p, tr, AlignCenter, _timeStr, size()); + + alreadyDrawing = false; +} + +bool FuzzyClock::showDate() +{ + return _prefs->fuzzyShowDate(); +} + +bool FuzzyClock::showDayOfWeek() +{ + return _prefs->fuzzyShowDayOfWeek(); +} + + +//************************************************************ + + +ClockApplet::ClockApplet(const QString& configFile, Type t, int actions, + QWidget *parent, const char *name) + : KPanelApplet(configFile, t, actions, parent, name), + _calendar(0), + _disableCalendar(false), + _clock(0), + _timer(new QTimer(this)), + m_layoutTimer(new QTimer(this)), + m_layoutDelay(0), + m_followBackgroundSetting(true), + m_dateFollowBackgroundSetting(true), + TZoffset(0), + _prefs(new Prefs(sharedConfig())), + zone(new Zone(config())), + menu(0), + m_tooltip(this), + m_shadowEngine(0) +{ + DCOPObject::setObjId("ClockApplet"); + _prefs->readConfig(); + configFileName = configFile.latin1(); + setBackgroundOrigin(AncestorOrigin); + + _dayOfWeek = new QLabel(this); + _dayOfWeek->setAlignment(AlignVCenter | AlignHCenter | WordBreak); + _dayOfWeek->setBackgroundOrigin(AncestorOrigin); + _dayOfWeek->installEventFilter(this); // catch mouse clicks + + _date = new QLabel(this); + _date->setAlignment(AlignVCenter | AlignHCenter | WordBreak); + _date->setBackgroundOrigin(AncestorOrigin); + _date->installEventFilter(this); // catch mouse clicks + + connect(m_layoutTimer, SIGNAL(timeout()), this, SLOT(fixupLayout())); + connect(_timer, SIGNAL(timeout()), SLOT(slotUpdate())); + connect(kapp, SIGNAL(kdisplayPaletteChanged()), SLOT(globalPaletteChange())); + + reconfigure(); // initialize clock widget + slotUpdate(); + + if (kapp->authorizeKAction("kicker_rmb")) + { + menu = new KPopupMenu(); + connect(menu, SIGNAL(aboutToShow()), SLOT(aboutToShowContextMenu())); + connect(menu, SIGNAL(activated(int)), SLOT(contextMenuActivated(int))); + setCustomMenu(menu); + } + + installEventFilter(KickerTip::the()); +} + + +ClockApplet::~ClockApplet() +{ + delete m_shadowEngine; + //reverse for the moment + KGlobal::locale()->removeCatalogue("clockapplet"); + KGlobal::locale()->removeCatalogue("timezones"); // For time zone translations + + if (_calendar) + { + // we have to take care of the calendar closing first before deleting + // the prefs + _calendar->close(); + } + + zone->writeSettings(); + + delete _prefs; _prefs = 0; + delete zone; zone = 0; + delete menu; menu = 0; + config()->sync(); +} + + +KTextShadowEngine *ClockApplet::shadowEngine() +{ + if (!m_shadowEngine) + m_shadowEngine = new KTextShadowEngine(); + + return m_shadowEngine; +} + + +int ClockApplet::widthForHeight(int h) const +{ + if (orientation() == Qt::Vertical) + { + return width(); + } + + int shareDateHeight = 0, shareDayOfWeekHeight = 0; + bool dateToSide = (h < 32); + bool mustShowDate = showDate || (zone->zoneIndex() != 0); + if (mustShowDate) + { + _date->setAlignment(AlignVCenter | AlignHCenter); + if (!dateToSide) + { + shareDateHeight = _date->sizeHint().height(); + } + } + if (showDayOfWeek) + { + _dayOfWeek->setAlignment(AlignVCenter | AlignHCenter); + if (!dateToSide) + { + shareDayOfWeekHeight = _dayOfWeek->sizeHint().height(); + } + } + + int clockWidth = _clock->preferedWidthForHeight(KMAX(0, h - shareDateHeight - shareDayOfWeekHeight)); + int w = clockWidth; + if (!mustShowDate && !showDayOfWeek) + { + // resize the date widgets in case the are to the left of the clock + _clock->widget()->setFixedSize(w, h); + _clock->widget()->move(0,0); + _dayOfWeek->move(clockWidth + 4, 0); + _date->move(clockWidth + 4, 0); + } + else + { + int dateWidth = mustShowDate ? _date->sizeHint().width() + 4 : 0; + int dayOfWeekWidth = showDayOfWeek ? _dayOfWeek->sizeHint().width() + 4 : 0; + + if (dateToSide) + { + w += dateWidth + dayOfWeekWidth; + bool dateFirst = false; + + if (mustShowDate) + { + // if the date format STARTS with a year, assume it's in descending + // order and should therefore PRECEED the date. + QString dateFormat = KGlobal::locale()->dateFormatShort(); + dateFirst = dateFormat.at(1) == 'y' || dateFormat.at(1) == 'Y'; + } + + if (dateFirst) + { + _date->setFixedSize(dateWidth, h); + _date->move(0, 0); + + if (showDayOfWeek) + { + _dayOfWeek->setFixedSize(dayOfWeekWidth, h); + _dayOfWeek->move(dateWidth, 0); + } + + _clock->widget()->setFixedSize(clockWidth, h); + _clock->widget()->move(dateWidth + dayOfWeekWidth, 0); + } + else + { + _clock->widget()->setFixedSize(clockWidth, h); + _clock->widget()->move(0,0); + + if (showDayOfWeek) + { + _dayOfWeek->setFixedSize(dayOfWeekWidth, h); + _dayOfWeek->move(clockWidth, 0); + } + + if (mustShowDate) + { + _date->setFixedSize(dateWidth, h); + _date->move(clockWidth + dayOfWeekWidth, 0); + } + } + } + else + { + w = KMAX(KMAX(w, dateWidth), dayOfWeekWidth); + + _clock->widget()->setFixedSize(w, h - shareDateHeight - shareDayOfWeekHeight); + _clock->widget()->setMinimumSize(w, h - shareDateHeight - shareDayOfWeekHeight); + _clock->widget()->move(0, 0); + if (showDayOfWeek) + { + _dayOfWeek->setFixedSize(w, _dayOfWeek->sizeHint().height()); + _dayOfWeek->move(0, _clock->widget()->height()); + } + + if (mustShowDate) + { + _date->setFixedSize(w, _date->sizeHint().height()); + _date->move(0, _clock->widget()->height() + shareDayOfWeekHeight); + } + } + } + + return w; +} + +int ClockApplet::heightForWidth(int w) const +{ + if (orientation() == Qt::Horizontal) + { + return height(); + } + + int clockHeight = _clock->preferedHeightForWidth(w); + bool mustShowDate = showDate || (zone->zoneIndex() != 0); + + _clock->widget()->setFixedSize(w, clockHeight); + + // add 4 pixels in height for each of date+dayOfWeek, if visible + if (showDayOfWeek) + { + if (_dayOfWeek->minimumSizeHint().width() > w) + { + _dayOfWeek->setAlignment(AlignVCenter | WordBreak); + } + else + { + _dayOfWeek->setAlignment(AlignVCenter | AlignHCenter | WordBreak); + } + + _dayOfWeek->setFixedSize(w, _dayOfWeek->minimumSizeHint().height()); + _dayOfWeek->move(0, clockHeight); + + clockHeight += _dayOfWeek->height(); + } + + if (mustShowDate) + { + // yes, the const_cast is ugly, but this is to ensure that we + // get a proper date label in the case that we munged it for + // display on panel that is too narrow and then they made it wider + const_cast<ClockApplet*>(this)->updateDateLabel(false); + + if (_date->minimumSizeHint().width() > w) + { + QString dateStr = _date->text(); + // if we're too wide to fit, replace the first non-digit from the end with a space + int p = dateStr.findRev(QRegExp("[^0-9]")); + if (p > 0) + { + _date->setText(dateStr.insert(p, '\n')); + } + } + + if (_date->minimumSizeHint().width() > w) + { + _date->setAlignment(AlignVCenter | WordBreak); + } + else + { + _date->setAlignment(AlignVCenter | AlignHCenter | WordBreak); + } + _date->setFixedSize(w, _date->heightForWidth(w)); + _date->move(0, clockHeight); + + clockHeight += _date->height(); + } + + return clockHeight; +} + +void ClockApplet::preferences() +{ + preferences(false); +} + +void ClockApplet::preferences(bool timezone) +{ + KConfigDialogSingle *dialog = dynamic_cast<KConfigDialogSingle*>(KConfigDialog::exists(configFileName)); + + if (!dialog) + { + dialog = new KConfigDialogSingle(zone, this, configFileName, _prefs, KDialogBase::Swallow); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(slotReconfigure())); + } + + if (timezone) + { + dialog->settings->tabs->setCurrentPage(1); + } + + dialog->show(); +} + +void ClockApplet::updateFollowBackground() +{ + QColor globalBgroundColor = KApplication::palette().active().background(); + QColor bgColor; + + switch (_prefs->type()) + { + case Prefs::EnumType::Plain: + bgColor = _prefs->plainBackgroundColor(); + break; + case Prefs::EnumType::Analog: + bgColor = _prefs->analogBackgroundColor(); + break; + case Prefs::EnumType::Fuzzy: + bgColor = _prefs->fuzzyBackgroundColor(); + break; + case Prefs::EnumType::Digital: + default: + bgColor = _prefs->digitalBackgroundColor(); + break; + } + + m_followBackgroundSetting = (bgColor == globalBgroundColor); + + bgColor = _prefs->dateBackgroundColor(); + m_dateFollowBackgroundSetting = (bgColor == globalBgroundColor); +} + +// DCOP interface +void ClockApplet::reconfigure() +{ + _timer->stop(); + + // ugly workaround for FuzzyClock: sometimes FuzzyClock + // hasn't finished drawing when getting deleted, so we + // ask FuzzyClock to delete itself appropriately + if (_clock && _clock->widget()->inherits("FuzzyClock")) + { + FuzzyClock* f = static_cast<FuzzyClock*>(_clock); + f->deleteMyself(); + } + else + { + delete _clock; + } + + int shortInterval = 500; + int updateInterval = 0; + + switch (_prefs->type()) + { + case Prefs::EnumType::Plain: + _clock = new PlainClock(this, _prefs, this); + if (_prefs->plainShowSeconds()) + updateInterval = shortInterval; + break; + case Prefs::EnumType::Analog: + _clock = new AnalogClock(this, _prefs, this); + if (_prefs->analogShowSeconds()) + updateInterval = shortInterval; + break; + case Prefs::EnumType::Fuzzy: + _clock = new FuzzyClock(this, _prefs, this); + break; + case Prefs::EnumType::Digital: + default: + _clock = new DigitalClock(this, _prefs, this); + if (_prefs->digitalShowSeconds() || _prefs->digitalBlink()) + updateInterval = shortInterval; + break; + } + + m_updateOnTheMinute = updateInterval != shortInterval; + if (m_updateOnTheMinute) + { + connect(_timer, SIGNAL(timeout()), this, SLOT(setTimerTo60())); + updateInterval = ((60 - clockGetTime().second()) * 1000) + 500; + } + else + { + // in case we reconfigure to show seconds but setTimerTo60 is going to be called + // we need to make sure to disconnect this so we don't end up updating only once + // a minute ;) + disconnect(_timer, SIGNAL(timeout()), this, SLOT(setTimerTo60())); + } + + _timer->start(updateInterval); + + // See if the clock wants to show the date. + showDate = _clock->showDate(); + if (showDate) + { + TZoffset = zone->calc_TZ_offset(zone->zone(), true); + updateDateLabel(); + } + + updateFollowBackground(); + setBackground(); + + // FIXME: this means you can't have a transparent clock but a non-transparent + // date or day =/ + + _clock->widget()->installEventFilter(this); // catch mouse clicks + _clock->widget()->show(); + + _clock->forceUpdate(); /* force repaint */ + + if (showDayOfWeek) + { + _dayOfWeek->show(); + } + else + { + _dayOfWeek->hide(); + } + + if (showDate || (zone->zoneIndex() != 0)) + { + _date->show(); + } + else + { + _date->hide(); + } + + emit(updateLayout()); + + showZone(zone->zoneIndex()); +} + +void ClockApplet::setTimerTo60() +{ +// kdDebug() << "setTimerTo60" << endl; + disconnect(_timer, SIGNAL(timeout()), this, SLOT(setTimerTo60())); + _timer->changeInterval(60000); +} + +void ClockApplet::setBackground() +{ + QColor globalBgroundColor = KApplication::palette().active().background(); + QColor fgColor, bgColor; + + if (!_clock) + return; + + switch (_prefs->type()) + { + case Prefs::EnumType::Plain: + bgColor = _prefs->plainBackgroundColor(); + fgColor = _prefs->plainForegroundColor(); + break; + case Prefs::EnumType::Analog: + bgColor = _prefs->analogBackgroundColor(); + fgColor = _prefs->analogForegroundColor(); + break; + case Prefs::EnumType::Fuzzy: + bgColor = _prefs->fuzzyBackgroundColor(); + fgColor = _prefs->fuzzyForegroundColor(); + break; + case Prefs::EnumType::Digital: + default: + bgColor = _prefs->digitalBackgroundColor(); + fgColor = _prefs->digitalForegroundColor(); + break; + } + + if (!m_followBackgroundSetting) + _clock->widget()->setPaletteBackgroundColor(bgColor); + else + _clock->widget()->unsetPalette(); + _clock->widget()->setPaletteForegroundColor(fgColor); + + bgColor = _prefs->dateBackgroundColor(); + + // See if the clock wants to show the day of week. + // use same font/color as for date + showDayOfWeek = _clock->showDayOfWeek(); + if (showDayOfWeek) + { + _dayOfWeek->setFont(_prefs->dateFont()); + + if (!m_dateFollowBackgroundSetting) + _dayOfWeek->setBackgroundColor(bgColor); + else + _dayOfWeek->unsetPalette(); + _dayOfWeek->setPaletteForegroundColor(_prefs->dateForegroundColor()); + } + + // See if the clock wants to show the date. + showDate = _clock->showDate(); + _date->setFont(_prefs->dateFont()); + + if (!m_dateFollowBackgroundSetting) + _date->setPaletteBackgroundColor(bgColor); + else + _date->unsetPalette(); + _date->setPaletteForegroundColor(_prefs->dateForegroundColor()); +} + +void ClockApplet::globalPaletteChange() +{ + if (!m_dateFollowBackgroundSetting && !m_followBackgroundSetting) + return; + + QColor globalBgroundColor = KApplication::palette().active().background(); + + if (m_dateFollowBackgroundSetting) + _prefs->setDateBackgroundColor(globalBgroundColor); + + if (m_followBackgroundSetting) + { + // we need to makes sure we have the background color synced! + // otherwise when we switch color schemes again or restart kicker + // it might come back non-transparent + switch (_prefs->type()) + { + case Prefs::EnumType::Plain: + _prefs->setPlainBackgroundColor(globalBgroundColor); + break; + case Prefs::EnumType::Analog: + _prefs->setAnalogBackgroundColor(globalBgroundColor); + break; + case Prefs::EnumType::Fuzzy: + _prefs->setFuzzyBackgroundColor(globalBgroundColor); + break; + case Prefs::EnumType::Digital: + default: + _prefs->setDigitalBackgroundColor(globalBgroundColor); + break; + } + } + + _prefs->writeConfig(); +} + +void ClockApplet::slotUpdate() +{ + if (_lastDate != clockGetDate()) + { + updateDateLabel(); + } + + if (m_updateOnTheMinute) + { + // catch drift so we're never more than a few s out + int seconds = clockGetTime().second(); +// kdDebug() << "checking for drift: " << seconds << endl; + + if (seconds > 2) + { + connect(_timer, SIGNAL(timeout()), this, SLOT(setTimerTo60())); + _timer->changeInterval(((60 - seconds) * 1000) + 500); + } + } + _clock->updateClock(); + KickerTip::Client::updateKickerTip(); +} + +void ClockApplet::slotCalendarDeleted() +{ + _calendar = 0L; + // don't reopen the calendar immediately ... + _disableCalendar = true; + QTimer::singleShot(100, this, SLOT(slotEnableCalendar())); + + // we are free to show a tip know :) + installEventFilter(KickerTip::the()); +} + + +void ClockApplet::slotEnableCalendar() +{ + _disableCalendar = false; +} + +void ClockApplet::toggleCalendar() +{ + if (_calendar && !_disableCalendar) + { + // calls slotCalendarDeleted which does the cleanup for us + _calendar->close(); + return; + } + + if (_calendar || _disableCalendar) + { + return; + } + + KickerTip::the()->untipFor(this); + removeEventFilter(KickerTip::the()); + + _calendar = new DatePicker(this, _lastDate, _prefs); + connect(_calendar, SIGNAL(destroyed()), SLOT(slotCalendarDeleted())); + + QSize size = _prefs->calendarSize(); + + if (size != QSize()) + { + _calendar->resize(size); + } + else + { + _calendar->adjustSize(); + } + + // make calendar fully visible + QPoint popupAt = KickerLib::popupPosition(popupDirection(), + _calendar, + this); + _calendar->move(popupAt); + _calendar->show(); + _calendar->setFocus(); +} + + +void ClockApplet::openContextMenu() +{ + if (!menu || !kapp->authorizeKAction("kicker_rmb")) + return; + + menu->exec( QCursor::pos() ); +} + +void ClockApplet::contextMenuActivated(int result) +{ + if ((result >= 0) && (result < 100)) + { + _prefs->setType(result); + _prefs->writeConfig(); + reconfigure(); + return; + }; + + if ((result >= 500) && (result < 600)) + { + showZone(result-500); + zone->writeSettings(); + return; + }; + + KProcess proc; + switch (result) + { + case 102: + preferences(); + break; + case 103: + proc << locate("exe", "kdesu"); + proc << "--nonewdcop"; + proc << QString("%1 kde-clock.desktop --lang %2") + .arg(locate("exe", "kcmshell")) + .arg(KGlobal::locale()->language()); + proc.start(KProcess::DontCare); + break; + case 104: + proc << locate("exe", "kcmshell"); + proc << "kde-language.desktop"; + proc.start(KProcess::DontCare); + break; + case 110: + preferences(true); + break; + } /* switch() */ +} + +void ClockApplet::aboutToShowContextMenu() +{ + bool bImmutable = config()->isImmutable(); + + menu->clear(); + menu->insertTitle( SmallIcon( "clock" ), i18n( "Clock" ) ); + + KLocale *loc = KGlobal::locale(); + QDateTime dt = QDateTime::currentDateTime(); + dt = dt.addSecs(TZoffset); + + KPopupMenu *copyMenu = new KPopupMenu( menu ); + copyMenu->insertItem(loc->formatDateTime(dt), 201); + copyMenu->insertItem(loc->formatDate(dt.date()), 202); + copyMenu->insertItem(loc->formatDate(dt.date(), true), 203); + copyMenu->insertItem(loc->formatTime(dt.time()), 204); + copyMenu->insertItem(loc->formatTime(dt.time(), true), 205); + copyMenu->insertItem(dt.date().toString(), 206); + copyMenu->insertItem(dt.time().toString(), 207); + copyMenu->insertItem(dt.toString(), 208); + copyMenu->insertItem(dt.toString("yyyy-MM-dd hh:mm:ss"), 209); + connect( copyMenu, SIGNAL( activated(int) ), this, SLOT( slotCopyMenuActivated(int) ) ); + + if (!bImmutable) + { + KPopupMenu *zoneMenu = new KPopupMenu( menu ); + connect(zoneMenu, SIGNAL(activated(int)), SLOT(contextMenuActivated(int))); + for (int i = 0; i <= zone->remoteZoneCount(); i++) + { + if (i == 0) + { + zoneMenu->insertItem(i18n("Local Timezone"), 500 + i); + } + else + { + zoneMenu->insertItem(i18n(zone->zone(i).utf8()).replace("_", " "), 500 + i); + } + } + zoneMenu->setItemChecked(500 + zone->zoneIndex(),true); + zoneMenu->insertSeparator(); + zoneMenu->insertItem(SmallIcon("configure"), i18n("&Configure Timezones..."), 110); + + KPopupMenu *type_menu = new KPopupMenu(menu); + connect(type_menu, SIGNAL(activated(int)), SLOT(contextMenuActivated(int))); + type_menu->insertItem(i18n("&Plain"), Prefs::EnumType::Plain, 1); + type_menu->insertItem(i18n("&Digital"), Prefs::EnumType::Digital, 2); + type_menu->insertItem(i18n("&Analog"), Prefs::EnumType::Analog, 3); + type_menu->insertItem(i18n("&Fuzzy"), Prefs::EnumType::Fuzzy, 4); + type_menu->setItemChecked(_prefs->type(),true); + + menu->insertItem(i18n("&Type"), type_menu, 101, 1); + menu->insertItem(i18n("Show Time&zone"), zoneMenu, 110, 2); + if (kapp->authorize("user/root")) + { + menu->insertItem(SmallIcon("date"), i18n("&Adjust Date && Time..."), 103, 4); + } + menu->insertItem(SmallIcon("kcontrol"), i18n("Date && Time &Format..."), 104, 5); + } + + menu->insertItem(SmallIcon("editcopy"), i18n("C&opy to Clipboard"), copyMenu, 105, 6); + if (!bImmutable) + { + menu->insertSeparator(7); + menu->insertItem(SmallIcon("configure"), i18n("&Configure Clock..."), 102, 8); + } +} + + +void ClockApplet::slotCopyMenuActivated( int id ) +{ + QPopupMenu *m = (QPopupMenu *) sender(); + QString s = m->text(id); + QApplication::clipboard()->setText(s); +} + +QTime ClockApplet::clockGetTime() +{ + return QTime::currentTime().addSecs(TZoffset); +} + +QDate ClockApplet::clockGetDate() +{ + return QDateTime::currentDateTime().addSecs(TZoffset).date(); +} + +void ClockApplet::showZone(int z) +{ + zone->setZone(z); + TZoffset = zone->calc_TZ_offset( zone->zone() ); + updateDateLabel(); + _clock->forceUpdate(); /* force repaint */ +} + +void ClockApplet::nextZone() +{ + zone->nextZone(); + showZone(zone->zoneIndex()); +} + +void ClockApplet::prevZone() +{ + zone->prevZone(); + showZone(zone->zoneIndex()); +} + +void ClockApplet::mousePressEvent(QMouseEvent *ev) +{ + switch (ev->button()) + { + case QMouseEvent::LeftButton: + toggleCalendar(); + break; + case QMouseEvent::RightButton: + openContextMenu(); + break; + case QMouseEvent::MidButton: + nextZone(); + QToolTip::remove(_clock->widget()); + break; + default: + break; + } +} + +void ClockApplet::wheelEvent(QWheelEvent* e) +{ + if (e->delta() < 0) + { + prevZone(); + } + else + { + nextZone(); + } + + QToolTip::remove(_clock->widget()); + KickerTip::Client::updateKickerTip(); +} + +// catch the mouse clicks of our child widgets +bool ClockApplet::eventFilter( QObject *o, QEvent *e ) +{ + if (( o == _clock->widget() || o == _date || o == _dayOfWeek) && + e->type() == QEvent::MouseButtonPress ) + { + mousePressEvent(static_cast<QMouseEvent*>(e) ); + return true; + } + + return KPanelApplet::eventFilter(o, e); +} + +void ClockApplet::positionChange(Position p) +{ + KPanelApplet::positionChange(p); + reconfigure(); +} + +void ClockApplet::updateDateLabel(bool reLayout) +{ + _lastDate = clockGetDate(); + _dayOfWeek->setText(KGlobal::locale()->calendar()->weekDayName(_lastDate)); + + if (zone->zoneIndex() != 0) + { + QString zone_s = i18n(zone->zone().utf8()); + _date->setText(zone_s.mid(zone_s.find('/') + 1).replace("_", " ")); + _date->setShown(true); + } + else + { + QString dateStr = KGlobal::locale()->formatDate(_lastDate, true); + _date->setText(dateStr); + _date->setShown(showDate); + } + + if (reLayout) + { + if (_calendar && _lastDate != _calendar->date()) + { + _calendar->setDate(_lastDate); + } + + m_layoutTimer->stop(); + m_layoutTimer->start(m_layoutDelay, true); + } +} + +void ClockApplet::updateKickerTip(KickerTip::Data& data) +{ + int zoneCount = zone->remoteZoneCount(); + + QString activeZone = zone->zone(); + if (zoneCount == 0) + { + QString _time = KGlobal::locale()->formatTime(clockGetTime(), + _prefs->plainShowSeconds()); + QString _date = KGlobal::locale()->formatDate(clockGetDate(), false); + data.message = _time; + data.subtext = _date; + + if (!activeZone.isEmpty()) + { + activeZone = i18n(activeZone.utf8()); + data.subtext.append("<br>").append(activeZone.mid(activeZone.find('/') + 1).replace("_", " ")); + } + } + else + { + int activeIndex = zone->zoneIndex(); + + for (int i = 0; i <= zone->remoteZoneCount(); i++) + { + QString m_zone = zone->zone(i); + TZoffset = zone->calc_TZ_offset(m_zone); + + if (!m_zone.isEmpty()) + { + m_zone = i18n(m_zone.utf8()); // ensure it gets translated + } + + QString _time = KGlobal::locale()->formatTime(clockGetTime(), + _prefs->plainShowSeconds()); + QString _date = KGlobal::locale()->formatDate(clockGetDate(), false); + + if (activeIndex == i) + { + data.message = m_zone.mid(m_zone.find('/') + 1).replace("_", " "); + data.message += " " + _time + "<br>" + _date; + } + else + { + if (i == 0) + { + data.subtext += "<b>" + i18n("Local Timezone") + "</b>"; + } + else + { + data.subtext += "<b>" + m_zone.mid(m_zone.find('/') + 1).replace("_", " ") + "</b>"; + } + data.subtext += " " + _time + ", " + _date + "<br>"; + } + } + + TZoffset = zone->calc_TZ_offset(activeZone); + } + + data.icon = DesktopIcon("date", KIcon::SizeMedium); + data.direction = popupDirection(); + data.duration = 4000; +} + +void ClockApplet::fixupLayout() +{ + m_layoutDelay = 0; + + // ensure we have the right widget line up in horizontal mode + // when we are showing date beside the clock + // this fixes problems triggered by having the date first + // because of the date format (e.g. YY/MM/DD) and then hiding + // the date + if (orientation() == Qt::Horizontal && height() < 32) + { + bool mustShowDate = showDate || (zone->zoneIndex() != 0); + + if (!mustShowDate && !showDayOfWeek) + { + _clock->widget()->move(0,0); + } + + int dayWidth = 0; + if (!showDayOfWeek) + { + _dayOfWeek->move(_clock->widget()->width() + 4, 0); + } + else + { + dayWidth = _dayOfWeek->width(); + } + + if (!showDate) + { + _date->move(_clock->widget()->width() + dayWidth + 4, 0); + } + } + + emit updateLayout(); +} + +int ClockApplet::type() +{ + return _prefs->type(); +} + +ClockAppletToolTip::ClockAppletToolTip( ClockApplet* clock ) + : QToolTip( clock ), + m_clock( clock ) +{ +} + +void ClockAppletToolTip::maybeTip( const QPoint & /*point*/ ) +{ + QString tipText; + if ( (m_clock->type() == Prefs::EnumType::Fuzzy) || + (m_clock->type() == Prefs::EnumType::Analog) ) + { + // show full time (incl. hour) as tooltip for Fuzzy clock + tipText = KGlobal::locale()->formatDateTime(QDateTime::currentDateTime().addSecs(m_clock->TZoffset)); + } + else + { + tipText = KGlobal::locale()->formatDate(m_clock->clockGetDate()); + } + + if (m_clock->timezones() && m_clock->timezones()->zoneIndex() > 0) + { + tipText += "\n" + i18n("Showing time for %1").arg(i18n(m_clock->timezones()->zone().utf8()), false); + } + + tip(m_clock->geometry(), tipText); +} + +//************************************************************ + +#include "clock.moc" diff --git a/kicker/applets/clock/clock.h b/kicker/applets/clock/clock.h new file mode 100644 index 000000000..efa67be46 --- /dev/null +++ b/kicker/applets/clock/clock.h @@ -0,0 +1,348 @@ +/***************************************************************** + +Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __CLOCK_H +#define __CLOCK_H + +#include <qlcdnumber.h> +#include <qlabel.h> +#include <qtoolbutton.h> +#include <qguardedptr.h> +#include <qdatetime.h> +#include <qvbox.h> +#include <qstringlist.h> +#include <qtooltip.h> +#include <qevent.h> + +#include <dcopobject.h> +#include <kpanelapplet.h> +#include <kdirwatch.h> +#include <kconfigdialog.h> + +#include <kickertip.h> +#include "settings.h" +#include "kshadowengine.h" + +class QTimer; +class QBoxLayout; +class DatePicker; +class QPixmap; +class Zone; +class KPopupMenu; +class Prefs; +class ClockApplet; + +namespace KIO +{ + class Job; +} + +class DigitalWidget; +class AnalogWidget; +class FuzzyWidget; +class ClockApplet; +class KConfigDialogManager; +class SettingsWidgetImp; + +class SettingsWidgetImp : public SettingsWidget +{ + Q_OBJECT + + public: + SettingsWidgetImp(Prefs *p=0, + Zone *z=0, + QWidget* parent=0, + const char* name=0, + WFlags fl=0); + public slots: + void OkApply(); + + private: + Prefs *prefs; + Zone *zone; +}; + +class KConfigDialogSingle : public KConfigDialog +{ + Q_OBJECT + + public: + KConfigDialogSingle(Zone *zone, + QWidget *parent=0, + const char *name=0, + Prefs *prefs=0, + KDialogBase::DialogType dialogType = KDialogBase::IconList, + bool modal=false); + + SettingsWidgetImp* settings; + + void updateSettings(); + void updateWidgets(); + void updateWidgetsDefault(); + + protected slots: + void selectPage(int p); + void dateToggled(); + + private: + DigitalWidget *digitalPage; + AnalogWidget *analogPage; + FuzzyWidget *fuzzyPage; + Prefs *_prefs; +}; + +/** + * Base class for all clock types + */ +class ClockWidget +{ + public: + ClockWidget(ClockApplet *applet, Prefs *prefs); + virtual ~ClockWidget(); + + virtual QWidget* widget()=0; + virtual int preferedWidthForHeight(int h) const =0; + virtual int preferedHeightForWidth(int w) const =0; + virtual void updateClock()=0; + virtual void forceUpdate() { _force = true; updateClock(); } + virtual void loadSettings()=0; + virtual bool showDate()=0; + virtual bool showDayOfWeek()=0; + + protected: + ClockApplet *_applet; + Prefs *_prefs; + QTime _time; + bool _force; +}; + + +class PlainClock : public QLabel, public ClockWidget +{ + Q_OBJECT + + public: + PlainClock(ClockApplet *applet, Prefs *prefs, QWidget *parent=0, const char *name=0); + + QWidget* widget() { return this; } + int preferedWidthForHeight(int h) const; + int preferedHeightForWidth(int w) const; + void updateClock(); + void loadSettings(); + bool showDate(); + bool showDayOfWeek(); + + protected: + void paintEvent(QPaintEvent *e); + void drawContents(QPainter *p); + + QString _timeStr; +}; + + +class DigitalClock : public QLCDNumber, public ClockWidget +{ + Q_OBJECT + + public: + DigitalClock(ClockApplet *applet, Prefs *prefs, QWidget *parent=0, const char *name=0); + ~DigitalClock(); + + QWidget* widget() { return this; } + int preferedWidthForHeight(int h) const; + int preferedHeightForWidth(int w) const; + void updateClock(); + void loadSettings(); + bool showDate(); + bool showDayOfWeek(); + + protected: + void paintEvent( QPaintEvent*); + void drawContents( QPainter * p); + void resizeEvent ( QResizeEvent *ev); + + QPixmap *_buffer; + QString _timeStr; + QPixmap lcdPattern; +}; + + +class AnalogClock : public QFrame, public ClockWidget +{ + Q_OBJECT + + public: + AnalogClock(ClockApplet *applet, Prefs *prefs, QWidget *parent=0, const char *name=0); + ~AnalogClock(); + + QWidget* widget() { return this; } + int preferedWidthForHeight(int h) const { return h; } + int preferedHeightForWidth(int w) const { return w; } + void updateClock(); + void loadSettings(); + bool showDate(); + bool showDayOfWeek(); + + protected: + virtual void paintEvent(QPaintEvent *); + void styleChange(QStyle&); + void initBackgroundPixmap(); + + QPixmap *_spPx; + QPixmap lcdPattern; + int _bgScale; +}; + + +class FuzzyClock : public QFrame, public ClockWidget +{ + Q_OBJECT + + public: + FuzzyClock(ClockApplet *applet, Prefs* prefs, QWidget *parent=0, const char *name=0); + + QWidget* widget() { return this; } + int preferedWidthForHeight(int h) const; + int preferedHeightForWidth(int w) const; + void updateClock(); + void loadSettings(); + bool showDate(); + bool showDayOfWeek(); + + public slots: + void deleteMyself(); + + protected: + virtual void drawContents(QPainter *p); + + QStringList hourNames; + QStringList normalFuzzy; + QStringList normalFuzzyOne; + QStringList dayTime; + + QString _timeStr; + + private: + bool alreadyDrawing; +}; + +class ClockAppletToolTip : public QToolTip +{ + public: + ClockAppletToolTip( ClockApplet* clock ); + + protected: + virtual void maybeTip( const QPoint & ); + + private: + ClockApplet *m_clock; +}; + +class ClockApplet : public KPanelApplet, public KickerTip::Client, public DCOPObject +{ + Q_OBJECT + K_DCOP + + friend class ClockAppletToolTip; + + public: + ClockApplet(const QString& configFile, Type t = Normal, int actions = 0, + QWidget *parent = 0, const char *name = 0); + ~ClockApplet(); + + int widthForHeight(int h) const; + int heightForWidth(int w) const; + void preferences(); + void preferences(bool timezone); + int type(); + Orientation getOrientation() { return orientation(); } + void resizeRequest() { emit(updateLayout()); } + const Zone* timezones() { return zone; } + + QTime clockGetTime(); + QDate clockGetDate(); + + virtual void updateKickerTip(KickerTip::Data&); + + KTextShadowEngine *shadowEngine(); + + k_dcop: + void reconfigure(); + + protected slots: + void slotReconfigure() { reconfigure(); } + void slotUpdate(); + void slotCalendarDeleted(); + void slotEnableCalendar(); + void slotCopyMenuActivated( int id ); + void contextMenuActivated(int result); + void aboutToShowContextMenu(); + void fixupLayout(); + void globalPaletteChange(); + void setTimerTo60(); + + protected: + void toggleCalendar(); + void openContextMenu(); + void updateDateLabel(bool reLayout = true); + void showZone(int z); + void nextZone(); + void prevZone(); + void updateFollowBackground(); + + void paletteChange(const QPalette &) { setBackground(); } + void setBackground(); + void mousePressEvent(QMouseEvent *ev); + void wheelEvent(QWheelEvent* e); + bool eventFilter(QObject *, QEvent *); + + virtual void positionChange(Position p); + + QCString configFileName; + DatePicker *_calendar; + bool _disableCalendar; + ClockWidget *_clock; + QLabel *_date; + QLabel *_dayOfWeek; + QDate _lastDate; + QTimer *_timer; + QTimer *m_layoutTimer; + int m_layoutDelay; + bool m_followBackgroundSetting; + bool m_dateFollowBackgroundSetting; + int TZoffset; + + // settings + Prefs *_prefs; + Zone *zone; + bool showDate; + bool showDayOfWeek; + bool m_updateOnTheMinute; + QStringList _remotezonelist; + KPopupMenu* menu; + ClockAppletToolTip m_tooltip; + KTextShadowEngine *m_shadowEngine; +}; + + +#endif diff --git a/kicker/applets/clock/clockapplet.desktop b/kicker/applets/clock/clockapplet.desktop new file mode 100644 index 000000000..6dda8818a --- /dev/null +++ b/kicker/applets/clock/clockapplet.desktop @@ -0,0 +1,151 @@ +[Desktop Entry] +Type=Plugin +Icon=clock +Name=Clock +Name[af]=Horlosie +Name[ar]=الساعة +Name[az]=Saat +Name[be]=Гадзіннік +Name[bg]=Часовник +Name[bn]=ঘড়ি +Name[br]=Eurier +Name[bs]=Sat +Name[ca]=Rellotge +Name[cs]=Hodiny +Name[csb]=Zédżer +Name[cy]=Cloc +Name[da]=Ur +Name[de]=Uhr +Name[el]=Ρολόι +Name[eo]=Horloĝo +Name[es]=Reloj +Name[et]=Kell +Name[eu]=Erlojua +Name[fa]=ساعت +Name[fi]=Kello +Name[fr]=Horloge +Name[fy]=Klok +Name[ga]=Clog +Name[gl]=Reloxo +Name[he]=שעון +Name[hi]=घड़ी +Name[hr]=Sat +Name[hu]=Óra +Name[id]=Jam +Name[is]=Klukka +Name[it]=Orologio +Name[ja]=時計 +Name[ka]=საათი +Name[kk]=Сағат +Name[km]=នាឡិកា +Name[ko]=시계 +Name[lo]=ໂມງ +Name[lt]=Laikrodis +Name[lv]=Pulkstenis +Name[mk]=Часовник +Name[mn]=Цаг +Name[ms]=Jam +Name[mt]=Arloġġ +Name[nb]=Klokke +Name[nds]=Klock +Name[ne]=घडी +Name[nl]=Klok +Name[nn]=Klokke +Name[nso]=Sesupanako +Name[oc]=Rellotge +Name[pa]=ਘੜੀ +Name[pl]=Zegar +Name[pt]=Relógio +Name[pt_BR]=Relógio +Name[ro]=Ceas +Name[ru]=Часы +Name[rw]=Isaha +Name[se]=Diibmu +Name[sk]=Hodiny +Name[sl]=Ura +Name[sr]=Часовник +Name[sr@Latn]=Časovnik +Name[ss]=Liwashi +Name[sv]=Klocka +Name[ta]=கடிகாரம் +Name[te]=గడియారం +Name[tg]=Соат +Name[th]=นาฬิกา +Name[tr]=Saat +Name[tt]=Säğät +Name[uk]=Годинник +Name[uz]=Soat +Name[uz@cyrillic]=Соат +Name[ven]=Tshifhinga +Name[vi]=Đồng hồ +Name[wa]=Ôrlodje +Name[xh]=Ikloko +Name[zh_CN]=时钟 +Name[zh_TW]=時鐘 +Name[zu]=Iwashi + +Comment=An analog and digital clock +Comment[af]='n Analoog en digitale horlosie +Comment[ar]= ساعة رقمية و عادية +Comment[be]=Аналагавы і лічбавы гадзіннік +Comment[bg]=Системен аплет за показване на датата и часа +Comment[bn]=একটি অ্যানালগ এবং ডিজিটাল ঘড়ি +Comment[bs]=Analogni i digitalni sat +Comment[ca]=Un rellotge digital i analògic +Comment[cs]=Applet s analogovými a digitálními hodinami +Comment[csb]=Analogòwi ë cyfrowi zédżer +Comment[da]=Et analogt og digitalt ur +Comment[de]=Eine analoge und digitale Uhr +Comment[el]=Ένα αναλογικό και ψηφιακό ρολόι +Comment[en_GB]=An analogue and digital clock +Comment[eo]=Analoga kaj cifereca horloĝo +Comment[es]=Reloj analógico/digital +Comment[et]=Analoog- ja digitaalkell +Comment[eu]=Erloju analogiko eta digitala +Comment[fa]=ساعت قیاسی و رقمی +Comment[fi]=Analoginen ja digitaalinen kello +Comment[fr]=Une horloge numérique et analogique +Comment[fy]=In analoge en digitale klok +Comment[ga]=Clog analógach agus digiteach +Comment[gl]=Unha applet cun reloxo analóxico e dixital. +Comment[he]=שעון אנלוגי ודיגיטלי +Comment[hr]=Analogni i digitalni sat +Comment[hu]=Egy analóg/digitális óra kisalkalmazásként +Comment[is]=Forrit sem birtir stafræna klukku eða skífuklukku +Comment[it]=Un orologio digitale o analogico +Comment[ja]=アナログ時計とデジタル時計 +Comment[ka]=ანალოგური და ციფრული საათი +Comment[kk]=Тілді немесе цифрлық сағат +Comment[km]=នាឡិកាអាណាឡូក និងឌីជីថល +Comment[lt]=Analoginis ir skaitmeninis laikrodis +Comment[mk]=Аналоген и дигитален часовник +Comment[nb]=En analog og digital klokke for panelet. +Comment[nds]=En analoog oder digitaal Klock +Comment[ne]=एनालग र डिजिटल घडी +Comment[nl]=Een analoge en digitale klok +Comment[nn]=Ei analog og digital klokke +Comment[pa]=ਇੱਕ ਐਨਾਲਾਗ ਤੇ ਡਿਜ਼ੀਟਲ ਘੜੀ ਹੈ। +Comment[pl]=Zegar analogowy i cyfrowy +Comment[pt]=Um relógio analógico e digital +Comment[pt_BR]=Um relógio analógico e digital +Comment[ro]=Un ceas analogic și digital +Comment[ru]=Аплет аналоговых и цифровых часов +Comment[se]=Analogálaš ja digitalálaš diibmo +Comment[sk]=Analógové a digitálne hodiny. +Comment[sl]=Analogna in digitalna ura +Comment[sr]=Аналогни и дигитални часовник +Comment[sr@Latn]=Analogni i digitalni časovnik +Comment[sv]=En analog och digital klocka +Comment[te]=ఎనాలాగ్ మరయూ డిజిటల్ గడియారం +Comment[th]=นาฬิกาแบบเข็มและแบบตัวเลข +Comment[tr]=Bir sayısal ve analog saat programcığı +Comment[uk]=Аналоговий або цифровий годинник +Comment[uz]=Analog va raqamli soat +Comment[uz@cyrillic]=Аналог ва рақамли соат +Comment[vi]=Đồng hồ thường và đồng hồ điện tử +Comment[wa]=Ene analodjike ou didjitåle ôrlodje. +Comment[zh_CN]=模拟和数字时钟面板小程序 +Comment[zh_TW]=一個小的類比或數字時鐘 + +X-KDE-Library=clock_panelapplet +X-KDE-UniqueApplet=false diff --git a/kicker/applets/clock/clockapplet.kcfg b/kicker/applets/clock/clockapplet.kcfg new file mode 100644 index 000000000..9b1ab3031 --- /dev/null +++ b/kicker/applets/clock/clockapplet.kcfg @@ -0,0 +1,191 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <include>kapplication.h</include> + <kcfgfile arg="true"/> + <group name="General"> + <entry name="Type" type="Enum"> + <label>Clock type</label> + <choices> + <choice name="Plain"/> + <choice name="Digital"/> + <choice name="Analog"/> + <choice name="Fuzzy"/> + </choices> + <default>Digital</default> + </entry> + </group> + <group name="Date"> + <entry name="DateForegroundColor" type="Color" key="Foreground_Color"> + <label>Foreground color.</label> + <default code="true">KApplication::palette().active().text()</default> + </entry> + <entry name="DateBackgroundColor" type="Color" key="Background_Color"> + <label>Foreground color.</label> + <default code="true">KApplication::palette().active().background()</default> + </entry> + <entry name="DateFont" type="Font" key="Font"> + <label>Font for the clock.</label> + <code> +QFont defFont=KGlobalSettings::generalFont(); +defFont.setPointSize(8); + </code> + <default code="true">defFont</default> + </entry> + </group> + <group name="Plain"> + <entry name="PlainShowSeconds" type="Bool" key="Show_Seconds"> + <label>Show seconds.</label> + <default>false</default> + </entry> + <entry name="PlainShowDate" type="Bool" key="Show_Date"> + <label>Show date.</label> + <default>true</default> + </entry> + <entry name="PlainShowDayOfWeek" type="Bool" key="Show_DayOfWeek"> + <label>Show day of week.</label> + <default>false</default> + </entry> + <entry name="PlainShowFrame" type="Bool" key="Show_Frame"> + <label>Show frame.</label> + <default>false</default> + </entry> + <entry name="PlainFont" type="Font" key="Font"> + <label>Font for the clock.</label> + <code> +defFont=KGlobalSettings::generalFont(); +defFont.setPointSize(8); +defFont.setBold(true); + </code> + <default code="true">defFont</default> + </entry> + <entry name="PlainForegroundColor" type="Color" key="Foreground_Color"> + <label>Foreground color.</label> + <default code="true">KApplication::palette().active().text()</default> + </entry> + <entry name="PlainBackgroundColor" type="Color" key="Background_Color"> + <label>Background color.</label> + <default code="true">KApplication::palette().active().background()</default> + </entry> + </group> + <group name="Digital"> + <entry name="DigitalShowSeconds" type="Bool" key="Show_Seconds"> + <label>Show seconds.</label> + <default>false</default> + </entry> + <entry name="DigitalShowDate" type="Bool" key="Show_Date"> + <label>Show date.</label> + <default>false</default> + </entry> + <entry name="DigitalShowDayOfWeek" type="Bool" key="Show_DayOfWeek"> + <label>Show day of week.</label> + <default>false</default> + </entry> + <entry name="DigitalShowFrame" type="Bool" key="Show_Frame"> + <label>Show frame.</label> + <default>false</default> + </entry> + <entry name="DigitalForegroundColor" type="Color" key="Foreground_Color"> + <label>Foreground color.</label> + <default code="true">KApplication::palette().active().text()</default> + </entry> + <entry name="DigitalBackgroundColor" type="Color" key="Background_Color"> + <label>Background color.</label> + <default code="true">KApplication::palette().active().background()</default> + </entry> + <entry name="DigitalShadowColor" type="Color" key="Shadow_Color"> + <label>Shadow color.</label> + <default code="true">KApplication::palette().active().mid()</default> + </entry> + <entry name="DigitalBlink" type="Bool" key="Blink"> + <label>Blink</label> + <default>false</default> + </entry> + <entry name="DigitalLCDStyle" type="Bool" key="LCD_Style"> + <label>LCD Style</label> + <default>false</default> + </entry> + </group> + <group name="Analog"> + <entry name="AnalogShowSeconds" type="Bool" key="Show_Seconds"> + <label>Show seconds.</label> + <default>true</default> + </entry> + <entry name="AnalogShowDate" type="Bool" key="Show_Date"> + <label>Show date.</label> + <default>false</default> + </entry> + <entry name="AnalogShowDayOfWeek" type="Bool" key="Show_DayOfWeek"> + <label>Show day of week.</label> + <default>false</default> + </entry> + <entry name="AnalogShowFrame" type="Bool" key="Show_Frame"> + <label>Show frame.</label> + <default>false</default> + </entry> + <entry name="AnalogForegroundColor" type="Color" key="Foreground_Color"> + <label>Foreground color.</label> + <default code="true">KApplication::palette().active().text()</default> + </entry> + <entry name="AnalogBackgroundColor" type="Color" key="Background_Color"> + <label>Background color.</label> + <default code="true">KApplication::palette().active().background()</default> + </entry> + <entry name="AnalogShadowColor" type="Color" key="Shadow_Color"> + <label>Shadow color.</label> + <default code="true">KApplication::palette().active().mid()</default> + </entry> + <entry name="AnalogLCDStyle" type="Bool" key="LCD_Style"> + <label>LCD Style</label> + <default>true</default> + </entry> + <entry name="AnalogAntialias" type="Int" key="Antialias"> + <label>Anti-Alias factor</label> + <default>0</default> + </entry> + </group> + <group name="Fuzzy"> + <entry name="FuzzyShowDate" type="Bool" key="Show_Date"> + <label>Show date.</label> + <default>true</default> + </entry> + <entry name="FuzzyShowDayOfWeek" type="Bool" key="Show_DayOfWeek"> + <label>Show day of week.</label> + <default>false</default> + </entry> + <entry name="FuzzyShowFrame" type="Bool" key="Show_Frame"> + <label>Show frame.</label> + <default>false</default> + </entry> + <entry name="FuzzyFont" type="Font" key="Font"> + <label>Font for the clock.</label> + <code> +defFont=KGlobalSettings::generalFont(); + </code> + <default code="true">defFont</default> + </entry> + <entry name="FuzzyForegroundColor" type="Color" key="Foreground_Color"> + <label>Foreground color.</label> + <default code="true">KApplication::palette().active().text()</default> + </entry> + <entry name="FuzzyBackgroundColor" type="Color" key="Background_Color"> + <label>Background color.</label> + <default code="true">KApplication::palette().active().background()</default> + </entry> + <entry name="Fuzzyness" type="Int"> + <label>Fuzzyness</label> + <default>1</default> + </entry> + </group> + <group name="Calendar"> + <entry name="CalendarFullWindow" type="Bool" key="FullWindow"> + <label>Show window frame</label> + <default>true</default> + </entry> + <entry name="CalendarSize" type="Size" key="Size"> + <label>Default size of the calendar</label> + </entry> + </group> +</kcfg> diff --git a/kicker/applets/clock/datepicker.cpp b/kicker/applets/clock/datepicker.cpp new file mode 100644 index 000000000..0ea677e8a --- /dev/null +++ b/kicker/applets/clock/datepicker.cpp @@ -0,0 +1,87 @@ +/************************************************************ + +Copyright (c) 1996-2002 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include "datepicker.h" +#include "prefs.h" + +#include <kdatepicker.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kwin.h> +#include <netwm.h> + +DatePicker::DatePicker(QWidget *parent, const QDate& date, Prefs* _prefs) + : QVBox( parent, 0, + _prefs->calendarFullWindow() + ? (WType_TopLevel | WDestructiveClose | + WStyle_StaysOnTop) + : (WStyle_Customize | WStyle_NoBorder | + WType_TopLevel | WDestructiveClose | + WStyle_StaysOnTop) ), + prefs(_prefs) +{ + if (prefs->calendarFullWindow()) + { + KWin::setType(winId(), NET::Utility); + setFrameStyle(QFrame::NoFrame); + } + else + { + setFrameStyle(QFrame::PopupPanel | QFrame::Raised); + } + + KWin::setOnAllDesktops(handle(), true); + picker = new KDatePicker(this, date); + picker->setCloseButton(!_prefs->calendarFullWindow()); + + /* name and icon for kicker's taskbar */ + setCaption(i18n("Calendar")); + setIcon(SmallIcon("date")); +} + +void DatePicker::closeEvent(QCloseEvent* e) +{ + prefs->setCalendarSize(size()); + QVBox::closeEvent(e); +} + +void DatePicker::keyPressEvent(QKeyEvent *e) +{ + QVBox::keyPressEvent(e); + + if (e->key() == Qt::Key_Escape) + { + close(); + } +} + +bool DatePicker::setDate(const QDate& date) +{ + return picker->setDate(date); +} + +QDate DatePicker::date() +{ + return picker->date(); +} + diff --git a/kicker/applets/clock/datepicker.h b/kicker/applets/clock/datepicker.h new file mode 100644 index 000000000..ee39261e0 --- /dev/null +++ b/kicker/applets/clock/datepicker.h @@ -0,0 +1,49 @@ +/***************************************************************** + +Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __DATEPICKER_H +#define __DATEPICKER_H + +#include <qvbox.h> +#include <qdatetime.h> + +class KDatePicker; +class Prefs; + +class DatePicker : public QVBox +{ + public: + DatePicker(QWidget*, const QDate&, Prefs* _prefs); + bool setDate(const QDate& date); + QDate date(); + + protected: + void closeEvent(QCloseEvent* e); + void keyPressEvent(QKeyEvent *e); + + private: + KDatePicker *picker; + Prefs *prefs; +}; + +#endif diff --git a/kicker/applets/clock/digital.ui b/kicker/applets/clock/digital.ui new file mode 100644 index 000000000..256bca99b --- /dev/null +++ b/kicker/applets/clock/digital.ui @@ -0,0 +1,305 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>DigitalWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>DigitalWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>553</width> + <height>251</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>ButtonGroup2_3</cstring> + </property> + <property name="title"> + <string>Display</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_DigitalShowDate</cstring> + </property> + <property name="text"> + <string>Dat&e</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_DigitalShowSeconds</cstring> + </property> + <property name="text"> + <string>Seco&nds</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_DigitalShowDayOfWeek</cstring> + </property> + <property name="text"> + <string>Da&y of week</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_DigitalBlink</cstring> + </property> + <property name="text"> + <string>Blin&king dots</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_DigitalShowFrame</cstring> + </property> + <property name="text"> + <string>&Frame</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer21</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Time</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_DigitalLCDStyle</cstring> + </property> + <property name="text"> + <string>LCD look</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout14</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>Foreground_ColorL</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Foreground color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_DigitalForegroundColor</cstring> + </property> + </widget> + <widget class="KColorButton" row="2" column="1"> + <property name="name"> + <cstring>kcfg_DigitalBackgroundColor</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>backgroundDigitalLabel</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Background color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_DigitalBackgroundColor</cstring> + </property> + </widget> + <widget class="KColorButton" row="0" column="1"> + <property name="name"> + <cstring>kcfg_DigitalForegroundColor</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="1" column="1"> + <property name="name"> + <cstring>kcfg_DigitalShadowColor</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer9</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>110</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>Shadow_ColorL</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Shadow color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_DigitalShadowColor</cstring> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer75</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>kcfg_DigitalLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_DigitalBackgroundColor</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_DigitalLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>backgroundDigitalLabel</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_DigitalLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_DigitalForegroundColor</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_DigitalLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>Foreground_ColorL</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_DigitalLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_DigitalShadowColor</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_DigitalLCDStyle</sender> + <signal>toggled(bool)</signal> + <receiver>Shadow_ColorL</receiver> + <slot>setDisabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>kcfg_DigitalShowDate</tabstop> + <tabstop>kcfg_DigitalShowSeconds</tabstop> + <tabstop>kcfg_DigitalBlink</tabstop> + <tabstop>kcfg_DigitalShowFrame</tabstop> + <tabstop>kcfg_DigitalLCDStyle</tabstop> + <tabstop>kcfg_DigitalForegroundColor</tabstop> + <tabstop>kcfg_DigitalShadowColor</tabstop> + <tabstop>kcfg_DigitalBackgroundColor</tabstop> +</tabstops> +<includes> + <include location="local" impldecl="in implementation">kdialog.h</include> + <include location="local" impldecl="in implementation">kfontrequester.h</include> + <include location="local" impldecl="in implementation">digital.ui.h</include> +</includes> +<slots> + <slot>kcfg_DigitalLCDStyle_stateChanged( int )</slot> +</slots> +<layoutdefaults spacing="3" margin="6"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +<includehints> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> +</includehints> +</UI> diff --git a/kicker/applets/clock/fuzzy.ui b/kicker/applets/clock/fuzzy.ui new file mode 100644 index 000000000..04e910340 --- /dev/null +++ b/kicker/applets/clock/fuzzy.ui @@ -0,0 +1,287 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>FuzzyWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>FuzzyWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>306</width> + <height>299</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>GroupBox1</cstring> + </property> + <property name="frameShape"> + <enum>GroupBoxPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="title"> + <string>Display</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_FuzzyShowDate</cstring> + </property> + <property name="text"> + <string>Dat&e</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_FuzzyShowDayOfWeek</cstring> + </property> + <property name="text"> + <string>Da&y of week</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_FuzzyShowFrame</cstring> + </property> + <property name="text"> + <string>&Frame</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer13</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Time</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="3" column="2"> + <property name="name"> + <cstring>spacer46</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>30</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Font:</string> + </property> + </widget> + <widget class="QLayoutWidget" row="1" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>TextLabel1_2_3_4_3</cstring> + </property> + <property name="text"> + <string>Background color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_FuzzyBackgroundColor</cstring> + </property> + </widget> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>51</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel1_4_3_2</cstring> + </property> + <property name="text"> + <string>Foreground color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_FuzzyForegroundColor</cstring> + </property> + </widget> + <widget class="KColorButton" row="1" column="1"> + <property name="name"> + <cstring>kcfg_FuzzyBackgroundColor</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="0" column="1"> + <property name="name"> + <cstring>kcfg_FuzzyForegroundColor</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget" row="0" column="2"> + <property name="name"> + <cstring>Layout7_3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel4_3</cstring> + </property> + <property name="text"> + <string>Low</string> + </property> + </widget> + <widget class="QSlider"> + <property name="name"> + <cstring>kcfg_Fuzzyness</cstring> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="maxValue"> + <number>4</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="value"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Both</enum> + </property> + <property name="tickInterval"> + <number>1</number> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel5_3</cstring> + </property> + <property name="text"> + <string>High</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>TextLabel3_3</cstring> + </property> + <property name="text"> + <string>Fuzziness:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_Fuzzyness</cstring> + </property> + </widget> + <widget class="KFontRequester" row="2" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_FuzzyFont</cstring> + </property> + <property name="title"> + <string>Date Font</string> + </property> + </widget> + </grid> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<tabstops> + <tabstop>kcfg_FuzzyShowDate</tabstop> + <tabstop>kcfg_FuzzyShowFrame</tabstop> + <tabstop>kcfg_Fuzzyness</tabstop> + <tabstop>kcfg_FuzzyForegroundColor</tabstop> + <tabstop>kcfg_FuzzyBackgroundColor</tabstop> +</tabstops> +<includes> + <include location="local" impldecl="in implementation">kdialog.h</include> + <include location="local" impldecl="in implementation">kfontrequester.h</include> +</includes> +<layoutdefaults spacing="3" margin="6"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +<includehints> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kfontrequester.h</includehint> +</includehints> +</UI> diff --git a/kicker/applets/clock/lcd.png b/kicker/applets/clock/lcd.png Binary files differnew file mode 100644 index 000000000..32e5e90d8 --- /dev/null +++ b/kicker/applets/clock/lcd.png diff --git a/kicker/applets/clock/prefs.kcfgc b/kicker/applets/clock/prefs.kcfgc new file mode 100644 index 000000000..890209526 --- /dev/null +++ b/kicker/applets/clock/prefs.kcfgc @@ -0,0 +1,6 @@ +# Code generation options for kconfig_compiler +File=clockapplet.kcfg +ClassName=Prefs +Singleton=false +Mutators=Type,CalendarSize,CalendarFullWindow,DateBackgroundColor,PlainBackgroundColor,AnalogBackgroundColor,FuzzyBackgroundColor,DigitalBackgroundColor +# MemberVariables=public diff --git a/kicker/applets/clock/settings.ui b/kicker/applets/clock/settings.ui new file mode 100644 index 000000000..215aa5433 --- /dev/null +++ b/kicker/applets/clock/settings.ui @@ -0,0 +1,515 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>SettingsWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>SettingsWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>605</width> + <height>639</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>tabs</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&Appearance</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout14</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <property name="text"> + <string>Clock type:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>clockCombo</cstring> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>Plain Clock</string> + </property> + </item> + <item> + <property name="text"> + <string>Digital Clock</string> + </property> + </item> + <item> + <property name="text"> + <string>Analog Clock</string> + </property> + </item> + <item> + <property name="text"> + <string>Fuzzy Clock</string> + </property> + </item> + <property name="name"> + <cstring>kcfg_Type</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QWidgetStack"> + <property name="name"> + <cstring>widgetStack</cstring> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>page</cstring> + </property> + <attribute name="id"> + <number>0</number> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>ButtonGroup2_3_2_2</cstring> + </property> + <property name="title"> + <string>Display</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_PlainShowDate</cstring> + </property> + <property name="text"> + <string>Dat&e</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_PlainShowSeconds</cstring> + </property> + <property name="text"> + <string>&Seconds</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_PlainShowDayOfWeek</cstring> + </property> + <property name="text"> + <string>Da&y of week</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_PlainShowFrame</cstring> + </property> + <property name="text"> + <string>&Frame</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer14</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Time</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>TextLabel1_3_3_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Font:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>TextLabel1_2_3_4_3_2</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Background color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_PlainBackgroundColor</cstring> + </property> + </widget> + <widget class="KColorButton" row="1" column="2"> + <property name="name"> + <cstring>kcfg_PlainBackgroundColor</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="0" column="2"> + <property name="name"> + <cstring>kcfg_PlainForegroundColor</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>TextLabel1_4_3_2_2</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Foreground color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_PlainForegroundColor</cstring> + </property> + </widget> + <spacer row="0" column="3"> + <property name="name"> + <cstring>spacer17</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>230</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KFontRequester" row="2" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_PlainFont</cstring> + </property> + </widget> + <spacer row="3" column="3"> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + </vbox> + </widget> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>dateBox</cstring> + </property> + <property name="title"> + <string>Date</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KColorButton" row="1" column="1"> + <property name="name"> + <cstring>kcfg_DateBackgroundColor</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>Foreground color:</string> + </property> + </widget> + <widget class="KColorButton" row="0" column="1"> + <property name="name"> + <cstring>kcfg_DateForegroundColor</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Background color:</string> + </property> + </widget> + <spacer row="0" column="2"> + <property name="name"> + <cstring>spacer15</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>343</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Font:</string> + </property> + </widget> + <widget class="KFontRequester"> + <property name="name"> + <cstring>kcfg_DateFont</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </hbox> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer6_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>100</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&Timezones</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>City</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Comment</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>tzListView</cstring> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>A list of timezones known to your system. Press the middle mouse button on the clock in the taskbar and it shows you the time in the selected cities.</string> + </property> + </widget> + </vbox> + </widget> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<tabstops> + <tabstop>tabs</tabstop> + <tabstop>kcfg_Type</tabstop> + <tabstop>kcfg_PlainShowDate</tabstop> + <tabstop>kcfg_PlainShowSeconds</tabstop> + <tabstop>kcfg_PlainShowFrame</tabstop> + <tabstop>kcfg_PlainForegroundColor</tabstop> + <tabstop>kcfg_PlainBackgroundColor</tabstop> + <tabstop>kcfg_DateBackgroundColor</tabstop> + <tabstop>kcfg_DateForegroundColor</tabstop> + <tabstop>kcfg_DateFont</tabstop> + <tabstop>tzListView</tabstop> +</tabstops> +<includes> + <include location="local" impldecl="in implementation">kdialog.h</include> + <include location="local" impldecl="in implementation">kfontrequester.h</include> +</includes> +<slots> + <slot>configureType()</slot> +</slots> +<layoutdefaults spacing="3" margin="6"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +<includehints> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kfontrequester.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kcolorbutton.h</includehint> + <includehint>kfontrequester.h</includehint> + <includehint>klistview.h</includehint> +</includehints> +</UI> diff --git a/kicker/applets/clock/zone.cpp b/kicker/applets/clock/zone.cpp new file mode 100644 index 000000000..1d952a765 --- /dev/null +++ b/kicker/applets/clock/zone.cpp @@ -0,0 +1,180 @@ +/************************************************************ + +Copyright (c) 1996-2002 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "zone.h" + +#include <kcolorbutton.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kstandarddirs.h> +#include <kstringhandler.h> +#include <klocale.h> + +#include <qfile.h> +#include <qtooltip.h> +#include <klistview.h> + +#include <time.h> +#include <stdlib.h> // for getenv() + +Zone::Zone(KConfig* conf): + config(conf), + _zoneIndex(0) +{ + _defaultTZ = ::getenv("TZ"); + tzset(); + + config->setGroup("General"); + + /* default displayable timezones */ + QString tzList = config->readEntry("RemoteZones"); + _remotezonelist = QStringList::split(",", tzList); + setZone(config->readNumEntry("Initial_TZ", 0)); +} + +Zone::~Zone() +{ + writeSettings(); +} + +void Zone::setZone(int z) +{ + if (_zoneIndex > _remotezonelist.count()) + z = 0; + + _zoneIndex = z; +} + +QString Zone::zone(int z) const +{ + return (z == 0 ? _defaultTZ : _remotezonelist[z-1]); +} + +int Zone::calc_TZ_offset(const QString& zone, bool /* reset */) +{ + const KTimezone *z = zone.isEmpty() ? m_zoneDb.local() : m_zoneDb.zone(zone); + + if (!z) + { + z = m_zoneDb.local(); + } + + if (z) + { + return -z->offset(Qt::LocalTime); + } + + return 0; +} + +void Zone::readZoneList(KListView *listView ) +{ + const KTimezones::ZoneMap zones = m_zoneDb.allZones(); + QMap<QString, QListViewItem*> KontinentMap; + + listView->setRootIsDecorated(true); + for (KTimezones::ZoneMap::ConstIterator it = zones.begin(); it != zones.end(); ++it) + { + const KTimezone *zone = it.data(); + QString tzName = zone->name(); + QString comment = zone->comment(); + if (!comment.isEmpty()) + comment = i18n(comment.utf8()); + + const QStringList KontCity = QStringList::split("/", i18n(tzName.utf8()).replace("_", " ")); + QListViewItem* Kontinent = KontinentMap[KontCity[0]]; + if (!Kontinent) { + KontinentMap[KontCity[0]] = new QListViewItem(listView, KontCity[0]); + Kontinent = KontinentMap[KontCity[0]]; + Kontinent->setExpandable(true); + } + + QCheckListItem *li = new QCheckListItem(Kontinent, KontCity[1], QCheckListItem::CheckBox); + li->setText(1, comment); + li->setText(2, tzName); /* store complete path in ListView */ + + if (_remotezonelist.findIndex(tzName) != -1) + li->setOn(true); + + // locate the flag from /l10n/%1/flag.png + // if not available select default "C" flag + QString flag = locate( "locale", QString("l10n/%1/flag.png").arg(zone->countryCode().lower()) ); + if (!QFile::exists(flag)) + flag = locate( "locale", "l10n/C/flag.png" ); + if (QFile::exists(flag)) + li->setPixmap(0, QPixmap(flag)); + } +} + +void Zone::getSelectedZonelist(KListView *listView) +{ + _remotezonelist.clear(); + + /* loop through all entries */ + QListViewItem *root = listView->firstChild(); + while (root) { + if (root->firstChild()) { + root = root->firstChild(); + continue; + } + + QCheckListItem *cl = (QCheckListItem*) root; + if (cl->isOn()) { + _remotezonelist.append(cl->text(2)); + } + + if (root->nextSibling()) { + root = root->nextSibling(); + continue; + } + root = root->parent(); + if (root) + root = root->nextSibling(); + } +} + +void Zone::nextZone() +{ + if (++_zoneIndex > _remotezonelist.count()) + _zoneIndex = 0; +} + +void Zone::prevZone() +{ + if (_zoneIndex == 0) + _zoneIndex = _remotezonelist.count(); + else + --_zoneIndex; +} + +void Zone::writeSettings() +{ + config->setGroup("General"); + config->writeEntry("RemoteZones", _remotezonelist.join(",")); + config->writeEntry("Initial_TZ",_zoneIndex); + config->sync(); +} diff --git a/kicker/applets/clock/zone.h b/kicker/applets/clock/zone.h new file mode 100644 index 000000000..34371c6ae --- /dev/null +++ b/kicker/applets/clock/zone.h @@ -0,0 +1,62 @@ +/***************************************************************** + +Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __ZONE_H +#define __ZONE_H + +#include <ktimezones.h> +#include <qstringlist.h> + +class KConfig; +class KListView; + +class Zone { + +public: + Zone(KConfig* conf); + ~Zone(); + + void writeSettings(); + + QString zone() const { return zone(_zoneIndex); }; + QString zone(int z) const; + QStringList remoteZoneList() const { return _remotezonelist; }; + int remoteZoneCount() { return _remotezonelist.count(); }; + unsigned int zoneIndex() const { return _zoneIndex; } + void setZone(int z = 0); + + void nextZone(); + void prevZone(); + int calc_TZ_offset(const QString& zone, bool reset=false); + void readZoneList(KListView *listView); + void getSelectedZonelist(KListView *listView); + +protected: + KTimezones m_zoneDb; + QStringList _remotezonelist; + KConfig *config; + QString _defaultTZ; + unsigned int _zoneIndex; +}; + +#endif diff --git a/kicker/applets/launcher/ChangeLog b/kicker/applets/launcher/ChangeLog new file mode 100644 index 000000000..b86dbd7aa --- /dev/null +++ b/kicker/applets/launcher/ChangeLog @@ -0,0 +1,40 @@ +2004-06-15 Dan Bullok <dan.kde@bullok.com> + * Fixed flicker on drop/arrange. + * Can now drop more than one item at a time + * Empty panel now has size>0, so user can still drop icons on it. + * Fixed gcc 3.4 incompatibilities in EasyVector + * Fixed Drag and Drop Bugs : + * Crash when non-url dropped on quicklauncher + * Crash panel button dropped on quicklauncher + * Able to drop objects when quicklauncher is locked + * Add application context menu now inserts new app at current location + +2004-06-14 Dan Bullok <dan.kde@bullok.com> + * Fixed License statements (added email & pointer to COPYING) + * GUI changes made in previous commit. + * continued separating QuickURL and QuickButton. + * Rearranged menu items to make more sense + +2004-06-13 Dan Bullok <dan.kde@bullok.com> + * Fixes Bugs #42278, #55625, #63506, #67891, #76868, #79848, #80530 - also makes bacon & eggs for breakfast (toast & jam available for vegetarians) + * v2.0alpha + * Replaced most of the innards of QuickLauncher class. + * Icons are positions evenly across available space. + * Drag and drop works for all kicker widths. + * Visual Feedback of drop location + * Icon Size is user-definable. Sensible(?) values are used by default (Icons grow slightly as panel size increases. #icons per row for given panel size is Tiny,Small: 1, Normal: 2, Large:3, 110pix: 4). + * User can lock icon drag and drop (prevents accidentally screwing things up). + * Icons can either take up exactly the defined amount of space (ConserveSpace=true - this is the default), or grow slightly to take advantage of unused space. + * Spouts tons of debugging info to kdDebug (for now). + +2004-06-12 Dan Bullok <dan.kde@bullok.com> + * Fixed bug #75351: Tooltips change to filenames after rearranging applications in quicklauncher. + * Moved the URL->(menuID,service,kurl) functionality from the QuickButton constructor to its own class: QuickURL. Very similar code is used elsewhere in kicker, and should eventually be merged. + * Renamed some methods in QuickButton (getId -> menuId, getURL -> url) This matches the predominant KDE naming style. + * Groundwork laid for variable-sized buttons. + +2004-06-12 Dan Bullok <dan.kde@bullok.com> + * Changed member variable names: myVar -> _myVar. + +2004-06-12 Dan Bullok <dan.kde@bullok.com> + * Fixed formatting only - no code changes. There were a few conflicting indenting styles. I picked the one that looked like it was the oldest, and applied it to all the files. diff --git a/kicker/applets/launcher/Makefile.am b/kicker/applets/launcher/Makefile.am new file mode 100644 index 000000000..a101c6ccf --- /dev/null +++ b/kicker/applets/launcher/Makefile.am @@ -0,0 +1,27 @@ + +INCLUDES = -I$(top_srcdir)/kicker/libkicker -I$(top_srcdir)/kicker/kicker/ui $(all_includes) + +kde_module_LTLIBRARIES = launcher_panelapplet.la + +launcher_panelapplet_la_SOURCES = quicklauncher.skel quicklauncher.cpp quickbutton.cpp quickaddappsmenu.cpp flowgridmanager.cpp popularity.cpp configdlgbase.ui prefs.kcfgc configdlg.cpp + +METASOURCES = AUTO +noinst_HEADERS = quicklauncher.h quickbutton.h quickaddappsmenu.h easyvector.h quickbuttongroup.h flowgridmanager.h popularity.h configdlg.h + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = quicklauncher.desktop + +EXTRA_DIST = $(lnk_DATA) + +launcher_panelapplet_la_LDFLAGS = -module $(KDE_RPATH) $(all_libraries) -avoid-version -no-undefined +launcher_panelapplet_la_LIBADD = ../../kicker/core/libkicker_core.la ../../kicker/buttons/libkicker_buttons.la \ + ../../kicker/ui/libkicker_ui.la ../../libkicker/libkickermain.la $(LIB_KIO) \ + $(LIB_KSYCOCA) $(LIB_KDEUI) $(LIB_KUTILS) + +kde_kcfg_DATA = launcherapplet.kcfg + +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/quicklauncher.pot + +srcdoc: + kdoc -a -p -H -d $$HOME/web/src/quicklauncher quicklauncher *.h -lqt -lkdecore -lkdeui diff --git a/kicker/applets/launcher/ToDo b/kicker/applets/launcher/ToDo new file mode 100644 index 000000000..c7c852a36 --- /dev/null +++ b/kicker/applets/launcher/ToDo @@ -0,0 +1,8 @@ +TODO: + X * Geometry isn't always updated when panel size changes. + x * Remove debugging code (makes everything slow). + * Tooltip or other popup to alert user that buttons are locked (with a "don't show this again" checkbox). + * Docs. + * Use old quicklauncher config file if found. #77959 + X * "Add application" context menu should add the new application at the index of the button that summoned the context menu. + X * Make sure to always have a little space to drop things on, even when there are no buttons. diff --git a/kicker/applets/launcher/configdlg.cpp b/kicker/applets/launcher/configdlg.cpp new file mode 100644 index 000000000..9950dd2a9 --- /dev/null +++ b/kicker/applets/launcher/configdlg.cpp @@ -0,0 +1,101 @@ +/***************************************************************** + +Copyright (c) 2005 Fred Schaettgen <kde.sch@ttgen.net> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + + +#include <qcombobox.h> +#include <klocale.h> +#include <kdebug.h> + +#include "prefs.h" +#include "configdlg.h" +#include "configdlgbase.h" + +ConfigDlg::ConfigDlg(QWidget *parent, const char *name, Prefs *config, + int autoSize, KConfigDialog::DialogType dialogType, + int dialogButtons) : + KConfigDialog(parent, name, config, dialogType, dialogButtons), + m_settings(config), + m_autoSize(autoSize) +{ + m_ui = new ConfigDlgBase(this->plainPage()); + addPage(m_ui, i18n("Configure"), "config"); + + m_ui->iconDim->clear(); + m_ui->iconDim->insertItem(i18n("Automatic")); + for (int n=0; n<int(m_settings->iconDimChoices().size()); ++n) + { + m_ui->iconDim->insertItem(QString::number( + m_settings->iconDimChoices()[n])); + } + connect(m_ui->iconDim, SIGNAL(textChanged(const QString&)), + this, SLOT(updateButtons())); + updateWidgets(); + m_oldIconDimText = m_ui->iconDim->currentText(); + updateButtons(); +} + +void ConfigDlg::updateSettings() +{ + kdDebug() << "updateSettings" << endl; + KConfigDialog::updateSettings(); + if (!hasChanged()) + { + return; + } + m_oldIconDimText = m_ui->iconDim->currentText(); + if (m_ui->iconDim->currentText() == i18n("Automatic")) + { + m_settings->setIconDim(m_autoSize); + } + else + { + m_settings->setIconDim(m_ui->iconDim->currentText().toInt()); + } + settingsChangedSlot(); +} + +void ConfigDlg::updateWidgets() +{ + KConfigDialog::updateWidgets(); + if (m_settings->iconDim() == m_autoSize) + { + m_ui->iconDim->setEditText(i18n("Automatic")); + } + else + { + m_ui->iconDim->setEditText(QString::number(m_settings->iconDim())); + } +} + +void ConfigDlg::updateWidgetsDefault() +{ + KConfigDialog::updateWidgetsDefault(); +} + +bool ConfigDlg::hasChanged() +{ + return m_oldIconDimText != m_ui->iconDim->currentText() || + KConfigDialog::hasChanged(); +} + +#include "configdlg.moc" diff --git a/kicker/applets/launcher/configdlg.h b/kicker/applets/launcher/configdlg.h new file mode 100644 index 000000000..1d03a9381 --- /dev/null +++ b/kicker/applets/launcher/configdlg.h @@ -0,0 +1,55 @@ +/***************************************************************** + +Copyright (c) 2005 Fred Schaettgen <kde.sch@ttgen.net> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef CONFIG_DLG_H +#define CONFIG_DLG_H + +#include <kconfigdialog.h> + +class ConfigDlgBase; +class Prefs; + +class ConfigDlg : public KConfigDialog +{ + Q_OBJECT + +public: + ConfigDlg(QWidget *parent, const char *name, Prefs *config, int autoSize, + KConfigDialog::DialogType dialogType, int dialogButtons); + +protected: + virtual bool hasChanged(); + +protected slots: + virtual void updateSettings(); + virtual void updateWidgets(); + virtual void updateWidgetsDefault(); + +private: + ConfigDlgBase *m_ui; + Prefs* m_settings; + int m_autoSize; + QString m_oldIconDimText; +}; + +#endif diff --git a/kicker/applets/launcher/configdlgbase.ui b/kicker/applets/launcher/configdlgbase.ui new file mode 100644 index 000000000..bfb1bc4e6 --- /dev/null +++ b/kicker/applets/launcher/configdlgbase.ui @@ -0,0 +1,273 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ConfigDlgBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ConfigDlgBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>371</width> + <height>338</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_DragEnabled</cstring> + </property> + <property name="text"> + <string>Allow drag and drop</string> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Layout</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_ConserveSpace</cstring> + </property> + <property name="text"> + <string>Conserve space</string> + </property> + <property name="toolTip" stdset="0"> + <string>Do not expand icons to the size of the panel</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Icon size:</string> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <property name="name"> + <cstring>iconDim</cstring> + </property> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + <spacer row="0" column="2"> + <property name="name"> + <cstring>spacer4_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>332</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>autoAdjustGroup</cstring> + </property> + <property name="title"> + <string>Most Popular Applications</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="3" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSlider" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_HistoryHorizon</cstring> + </property> + <property name="maxValue"> + <number>100</number> + </property> + <property name="lineStep"> + <number>0</number> + </property> + <property name="value"> + <number>10</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <spacer row="1" column="1"> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>140</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Short Term</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Long Term</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + </grid> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Maximum number of applications:</string> + </property> + </widget> + <widget class="KIntSpinBox" row="1" column="1"> + <property name="name"> + <cstring>kcfg_AutoAdjustMinItems</cstring> + </property> + </widget> + <spacer row="2" column="2"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>50</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KIntSpinBox" row="2" column="1"> + <property name="name"> + <cstring>kcfg_AutoAdjustMaxItems</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="text"> + <string>Minimum number of applications:</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_AutoAdjustEnabled</cstring> + </property> + <property name="text"> + <string>Add/remove applications based on their popularity</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>kcfg_AutoAdjustEnabled</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_AutoAdjustMinItems</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_AutoAdjustEnabled</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_AutoAdjustMaxItems</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_AutoAdjustEnabled</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_HistoryHorizon</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_AutoAdjustEnabled</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_AutoAdjustEnabled</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel2</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_AutoAdjustEnabled</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel3</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_AutoAdjustEnabled</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel3_2</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/kicker/applets/launcher/easyvector.h b/kicker/applets/launcher/easyvector.h new file mode 100644 index 000000000..cad9a2c86 --- /dev/null +++ b/kicker/applets/launcher/easyvector.h @@ -0,0 +1,147 @@ +/* Copyright 2004, Daniel Woods Bullok <dan.devel@bullok.com> + distributed under the terms of the + GNU GENERAL PUBLIC LICENSE Version 2 - + See the file kdebase/COPYING for details +*/ + +#ifndef __easyvector_h__ +#define __easyvector_h__ +#include <vector> +#include <algorithm> +#include <assert.h> + +template < class VALUE > +class __Valtype { +public: + typedef const VALUE& CVALUE; +}; + + +template < class VALUE > +class __Valtype< VALUE* > { +public: + typedef const VALUE* CVALUE; +}; + + +template <class VALUE, bool CHECKINDEX=true> +class EasyVector: public std::vector< VALUE > { +public: + typedef int Index; + typedef std::vector< Index > Indices; + typedef typename __Valtype< VALUE >::CVALUE CVALUE; + + static const Index NotFound=-2; + static const Index Append=-1; + + template < class PTYPE, class PROP_FUNC > + Index findProperty(const PTYPE &property, + PROP_FUNC prop_func) const; + Index findValue(CVALUE value) const; + + Index lastIndex() const {return this->size()-1;} + + void eraseAt(Index index); + + VALUE takeFrom(Index index); + + void insertAt(Index index,const VALUE &value); + void insertAt(Index index,const EasyVector &values); + + bool isValidIndex(Index index) const; + bool isValidInsertIndex(Index index) const; + virtual ~EasyVector(){}; + + +protected: + void _checkInsertIndex(Index index) const; + void _checkIndex(Index index) const; + Index _convertInsertIndex(Index index) const; +}; + + +template < class VALUE, bool CHECKINDEX > +template < class PTYPE, class PROP_FUNC > +typename EasyVector< VALUE, CHECKINDEX >::Index + EasyVector< VALUE, CHECKINDEX >::findProperty(const PTYPE &property, + PROP_FUNC prop_func) const +{ typename EasyVector< VALUE, CHECKINDEX >::const_iterator i; + for (i=this->begin();i!=this->end();++i) { + if (prop_func(*i)==property) + return i-this->begin(); + } + return NotFound; +} + + +template < class VALUE, bool CHECKINDEX > +typename EasyVector< VALUE, CHECKINDEX >::Index + EasyVector< VALUE, CHECKINDEX >::findValue(CVALUE value) const +{ typename EasyVector< VALUE, CHECKINDEX >::const_iterator i; + i=std::find(this->begin(),this->end(),value); + if (i==this->end()) return NotFound; + return i-this->begin(); +} + + +template < class VALUE, bool CHECKINDEX > +void EasyVector< VALUE, CHECKINDEX >::eraseAt(Index index) +{ _checkIndex(index); + erase(this->begin()+index); +} + + +template < class VALUE, bool CHECKINDEX > +VALUE EasyVector< VALUE, CHECKINDEX >::takeFrom(Index index) +{ _checkIndex(index); + VALUE result=(*this)[index]; + eraseAt(index); + return result; +} + + +template < class VALUE, bool CHECKINDEX > +void EasyVector< VALUE, CHECKINDEX >::insertAt(EasyVector< VALUE, CHECKINDEX >::Index index,const VALUE &value) +{ index=_convertInsertIndex(index); + _checkInsertIndex(index); + if (index==int(this->size())) { + this->push_back(value); + return; + } + insert(this->begin()+index,value); +} + + +template < class VALUE, bool CHECKINDEX > +void EasyVector< VALUE, CHECKINDEX >::insertAt(EasyVector< VALUE, CHECKINDEX >::Index index,const EasyVector< VALUE, CHECKINDEX > &v) +{ index=_convertInsertIndex(index); + _checkInsertIndex(index); + insert(this->begin()+index,v.begin(),v.end()); +} + + +template < class VALUE, bool CHECKINDEX > +bool EasyVector< VALUE, CHECKINDEX >::isValidIndex(EasyVector< VALUE, CHECKINDEX >::Index index) const +{ return(0<=index && index<int(this->size()));} + +template < class VALUE, bool CHECKINDEX > +bool EasyVector< VALUE, CHECKINDEX >::isValidInsertIndex(EasyVector< VALUE, CHECKINDEX >::Index index) const +{ return(index==Append)||(0<=index && index<=int(this->size()));} + +template < class VALUE, bool CHECKINDEX > +inline typename EasyVector< VALUE, CHECKINDEX >::Index EasyVector< VALUE, CHECKINDEX >::_convertInsertIndex(Index index) const +{ if (index==Append) return this->size(); + return index; +} + +template < class VALUE, bool CHECKINDEX > +void EasyVector< VALUE, CHECKINDEX >::_checkInsertIndex(Index index) const +{ if (CHECKINDEX) assert (isValidInsertIndex(index));} + +template < class VALUE, bool CHECKINDEX > +void EasyVector< VALUE, CHECKINDEX >::_checkIndex(Index index) const +{ if (CHECKINDEX) assert (isValidIndex(index));} + + +#endif + diff --git a/kicker/applets/launcher/flowgridmanager.cpp b/kicker/applets/launcher/flowgridmanager.cpp new file mode 100644 index 000000000..b5715097b --- /dev/null +++ b/kicker/applets/launcher/flowgridmanager.cpp @@ -0,0 +1,316 @@ +/* Copyright 2004, Daniel Woods Bullok <dan.devel@bullok.com> + distributed under the terms of the + GNU GENERAL PUBLIC LICENSE Version 2 - + See the file kdebase/COPYING for details +*/ + +#include "flowgridmanager.h" +#include <kdebug.h> +#ifdef DEBUG + #define DEBUGSTR kdDebug() +#else + #define DEBUGSTR kndDebug() +#endif + + +FlowGridManager::FlowGridManager(QSize p_item_size, + QSize p_space_size, + QSize p_border_size, + QSize p_frame_size, + Qt::Orientation orient, + int num_items, + Slack slack_x,Slack slack_y) +{ + _pItemSize=p_item_size; + _pSpaceSize=p_space_size; + _pBorderSize=p_border_size; + _pFrameSize=p_frame_size; + _orientation=orient; + _numItems=num_items; + _slackX=slack_x; + _slackY=slack_y; + _conserveSpace=false; + + _dirty=true; + _valid=false; +} + +// set members. +// These all set the _dirty flag if the new value is different. +void FlowGridManager::setNumItems(int num_items) +{ if (_numItems==num_items) + return; + _numItems=num_items; _dirty=true; +} +void FlowGridManager::setItemSize(QSize p_item_size) +{ if (_pItemSize==p_item_size) + return; + _pItemSize=p_item_size; _dirty=true; +} + +void FlowGridManager::setSpaceSize(QSize p_space_size) +{ if (_pSpaceSize==p_space_size) + return; + _pSpaceSize=p_space_size; _dirty=true; +} + +void FlowGridManager::setBorderSize(QSize p_border_size) +{ if (_pBorderSize==p_border_size) + return; + _pBorderSize=p_border_size; _dirty=true; +} + +void FlowGridManager::setFrameSize(QSize p_frame_size) +{ if (_pFrameSize==p_frame_size) + return; + _pFrameSize=p_frame_size; + if (_pFrameSize.width()<=0) { + _orientation=Qt::Vertical; + } + if (_pFrameSize.height()<=0) { + _orientation=Qt::Horizontal; + } + _dirty=true; +} + +void FlowGridManager::setOrientation(Qt::Orientation orient) +{ if (orient==_orientation) + return; + _orientation=orient; _dirty=true; +} + +void FlowGridManager::setSlack(Slack slack_x, Slack slack_y) +{ if (slack_x==_slackX && slack_y==_slackY) return; + _slackX=slack_x; _slackY=slack_y; _dirty=true;} + + +void FlowGridManager::setConserveSpace(bool conserve) +{ if (_conserveSpace==conserve) + return; + _conserveSpace=conserve; _dirty=true; +} + + + +// get members +QSize FlowGridManager::itemSize() const +{ _checkReconfigure(); return _itemSize;} + +QSize FlowGridManager::spaceSize() const +{ _checkReconfigure(); return _spaceSize;} + +QSize FlowGridManager::borderSize() const +{ _checkReconfigure(); return _borderSize;} + +QSize FlowGridManager::gridDim() const +{ _checkReconfigure(); return _gridDim;} + +QSize FlowGridManager::gridSpacing() const +{ _checkReconfigure(); return _gridSpacing;} + +QSize FlowGridManager::frameSize() const +{ _checkReconfigure(); return _frameSize;} + +QPoint FlowGridManager::origin() const +{ _checkReconfigure(); return _origin;} + +Qt::Orientation FlowGridManager::orientation() const +{ _checkReconfigure(); return _orientation;} + +/*Slack FlowGridManager::slackX() const +{ return _slackY;} + +Slack FlowGridManager::slackY() const +{ return _slackY;} +*/ + +bool FlowGridManager::conserveSpace() const +{ return _conserveSpace; } + + +bool FlowGridManager::isValid() const +{ _checkReconfigure(); return _valid;} + +QPoint FlowGridManager::posAtCell(int x,int y) const +{ _checkReconfigure(); + return _origin+QPoint(_gridSpacing.width()*x,_gridSpacing.height()*y); +} + +QPoint FlowGridManager::pos(int i) const +{ return posAtCell(cell(i).x(),cell(i).y()); +} + +QPoint FlowGridManager::cell(int index) const +{ _checkReconfigure(); + //assert((index>=0) && (index<_gridDim.width()*_gridDim.height())); + int x=index % _gridDim.width(), + y=index / _gridDim.width(); + return QPoint(x,y); +} + + + + +// return height if orientation is Horizontal +// return width if orientation is Vertical +int FlowGridManager::_getHH(QSize size) const +{ if (_orientation==Qt::Horizontal) + return size.height(); + return size.width(); +} + +// return height if orientation is Vertical +// return width if orientation is Horizontal +int FlowGridManager::_getWH(QSize size) const +{ if (_orientation==Qt::Horizontal) + return size.width(); + return size.height(); +} + +// swap horizontal and vertical if orientation is Vertical, otherwise return arg +QSize FlowGridManager::_swapHV(QSize hv) const +{ if (_orientation==Qt::Horizontal) + return hv; + QSize temp=hv; + temp.transpose(); + return temp; +} + + +// return the amount of slack when: +// nitems = # of items +// length = total length of space where items will be placed +// item, space, border = length of respective entities +int FlowGridManager::_slack(int nitems,int length,int item,int space,int border) const +{ return length-(2*border)-(nitems-1)*space-nitems*item;} + + +void FlowGridManager::_clear() const +{ + _borderSize=QSize(0,0); + _spaceSize=QSize(0,0); + _itemSize=QSize(0,0); + _gridDim=QSize(0,0); + _gridSpacing=QSize(0,0); + _origin=QPoint(0,0); + _frameSize=QSize(0,0); + + _dirty=false; + _valid=false; +} + + +int FlowGridManager::indexNearest(QPoint p) const +{ if (!isValid()) return -1; + QPoint c=(p-_origin)-QPoint(_spaceSize.width(),_spaceSize.height())/2; + int x=c.x()/_gridSpacing.width(), + y=c.y()/_gridSpacing.height(); + int i= x+y*_gridDim.width(); + if (i>_numItems) return -1; + return i; +} + + + +// Redistribute the boxes +void FlowGridManager::_reconfigure() const +{ if ((!_pFrameSize.isValid()) || + (!_pItemSize.isValid()) || + _numItems==0 ) { + _clear(); + return; + } + int height=_getHH(_pFrameSize), + pItemHeight=_getHH(_pItemSize), + pSpaceHeight=_getHH(_pSpaceSize), + pBorderHeight=_getHH(_pBorderSize), + spanlen=(height-2*pBorderHeight+pSpaceHeight)/(pItemHeight+pSpaceHeight); + int slack,iSlack; + + if (spanlen==0) { + _dirty=false; + _valid=false; + return; + } + // figure out the number of spans required for all items + int numspans=_numItems/spanlen; + if (numspans*spanlen<_numItems) { + numspans++; + } + + slack=_slack(spanlen,height,pItemHeight,pSpaceHeight,pBorderHeight); // total slack + iSlack=slack/spanlen; // slack per item + // Items pick up extra slack + if (_slackX==ItemSlack) pItemHeight+=iSlack; + slack=_slack(spanlen,height,pItemHeight,pSpaceHeight,pBorderHeight); + + // space picks up extra slack + if (spanlen>1) { + iSlack=slack/(spanlen+1); + pSpaceHeight+=iSlack; + } + + slack=_slack(spanlen,height,pItemHeight,pSpaceHeight,pBorderHeight); + iSlack=slack/2; + pBorderHeight+=iSlack; + if (_conserveSpace) { + _itemSize=_swapHV(QSize(_getWH(_pItemSize),pItemHeight)); + _spaceSize=_swapHV(QSize(_getWH(_pSpaceSize),pSpaceHeight)); + _borderSize=_swapHV(QSize(_getWH(_pBorderSize),pBorderHeight)); + } + else { + _itemSize=_swapHV(QSize(pItemHeight,pItemHeight)); + _spaceSize=_swapHV(QSize(pSpaceHeight,pSpaceHeight)); + _borderSize=_swapHV(QSize(pBorderHeight,pBorderHeight)); + } + _gridDim=_swapHV(QSize(numspans,spanlen)); + + _gridSpacing=_itemSize+_spaceSize; + _origin=QPoint(_borderSize.width(),_borderSize.height()); + _frameSize=2*_borderSize+QSize(_gridDim.width()*_gridSpacing.width()-_spaceSize.width(), + _gridDim.height()*_gridSpacing.height()-_spaceSize.height()); + + _dirty=false; + _valid=true; +} + + +void FlowGridManager::dump() +{ + DEBUGSTR<<endl<<flush; + + DEBUGSTR<<"_pItemSize=("<<_pItemSize.width()<<","<<_pItemSize.height()<<")"<<endl<<flush; + DEBUGSTR<<"_pSpaceSize=("<<_pSpaceSize.width()<<","<<_pSpaceSize.height()<<")"<<endl<<flush; + DEBUGSTR<<"_pBorderSize=("<<_pBorderSize.width()<<","<<_pBorderSize.height()<<")"<<endl<<flush; + DEBUGSTR<<"_pFrameSize=("<<_pFrameSize.width()<<","<<_pFrameSize.height()<<")"<<endl<<flush; + DEBUGSTR<<"_borderSize=("<<_borderSize.width()<<","<<_borderSize.height()<<")"<<endl<<flush; + DEBUGSTR<<"_spaceSize=("<<_spaceSize.width()<<","<<_spaceSize.height()<<")"<<endl<<flush; + DEBUGSTR<<"_itemSize=("<<_itemSize.width()<<","<<_itemSize.height()<<")"<<endl<<flush; + DEBUGSTR<<"_gridDim=("<<_gridDim.width()<<","<<_gridDim.height()<<")"<<endl<<flush; + DEBUGSTR<<"_gridSpacing=("<<_gridSpacing.width()<<","<<_gridSpacing.height()<<")"<<endl<<flush; + DEBUGSTR<<"_origin=("<<_origin.x()<<","<<_origin.y()<<")"<<endl<<flush; + DEBUGSTR<<"_frameSize=("<<_frameSize.width()<<","<<_frameSize.height()<<")"<<endl<<flush; + DEBUGSTR<<"_conserveSpace="<<_conserveSpace<<endl<<flush; + + DEBUGSTR<<"_orientation="<<_orientation<<endl<<flush; + DEBUGSTR<<"_numItems="<<_numItems<<endl<<flush; + DEBUGSTR<<"_slackX="<<_slackX<<endl<<flush; + DEBUGSTR<<"_slackY="<<_slackY<<endl<<flush; + DEBUGSTR<<"_dirty="<<_dirty<<endl<<flush; + DEBUGSTR<<"_valid="<<_valid<<endl<<flush; + DEBUGSTR<<endl<<flush; +} + + + +bool operator== ( const FlowGridManager & csg1, const FlowGridManager & csg2 ) +{ + return csg1.gridDim()==csg2.gridDim() && + csg1.origin()==csg2.origin() && + csg1.gridSpacing()==csg2.gridSpacing() && + csg1.frameSize()==csg2.frameSize(); +} + + + + diff --git a/kicker/applets/launcher/flowgridmanager.h b/kicker/applets/launcher/flowgridmanager.h new file mode 100644 index 000000000..9d100c74f --- /dev/null +++ b/kicker/applets/launcher/flowgridmanager.h @@ -0,0 +1,99 @@ +/* Copyright 2004, Daniel Woods Bullok <dan.devel@bullok.com> + distributed under the terms of the + GNU GENERAL PUBLIC LICENSE Version 2 - + See the file kdebase/COPYING for details +*/ + +#ifndef __const_space_grid_h__ +#define __const_space_grid_h__ + +#include <qnamespace.h> +#include <qpoint.h> +#include <qsize.h> + + +class FlowGridManager { +// Determine if two FlowGridManager objs have the same layout. They may or +// may not have the same input parameters, but the resulting layout is identical. + friend bool operator== ( const FlowGridManager & gp1, const FlowGridManager & gp2 ); + +public: + typedef enum { + ItemSlack,SpaceSlack,BorderSlack,NoSlack + } Slack; + + FlowGridManager(QSize p_item_size=QSize(0,0), + QSize p_space_size=QSize(0,0), + QSize p_border_size=QSize(0,0), + QSize frame_size=QSize(0,0), + Qt::Orientation orient=Qt::Horizontal, + int num_items=0, + Slack slack_x=ItemSlack, + Slack slack_y=ItemSlack); + + + void setNumItems(int num_items); + void setItemSize(QSize item_size); + void setSpaceSize(QSize space_size); + void setBorderSize(QSize border_size); + void setOrientation(Qt::Orientation orient); + void setFrameSize(QSize frame_size); + void setSlack(Slack slack_x, Slack slack_y); + void setConserveSpace(bool conserve); + + + QSize itemSize() const; + QSize spaceSize() const; + QSize borderSize() const; + QSize gridDim() const; + QSize gridSpacing() const; + QSize frameSize() const; + QPoint origin() const; + Qt::Orientation orientation() const; + bool conserveSpace() const; + +// Slack slackX() const; +// Slack slackY() const; + + QPoint posAtCell(int x,int y) const; + QPoint pos(int i) const; + QPoint cell(int index) const; + bool isValid() const; + int indexNearest(QPoint p) const; + + void dump(); +protected: + int _getHH(QSize size) const; + int _getWH(QSize size) const; + QSize _swapHV(QSize hv) const; + inline void _checkReconfigure() const; + int _slack(int nitems,int length,int item,int space,int border) const; + void _reconfigure() const; + void _clear() const; + +protected: + // user-definable data + QSize _pItemSize,_pSpaceSize,_pBorderSize,_pFrameSize; + Slack _slackX, _slackY; + bool _conserveSpace; + Qt::Orientation _orientation; + int _numItems; + + // results + mutable QSize _itemSize, _spaceSize, _borderSize, _gridDim, _gridSpacing, _frameSize; + mutable QPoint _origin; + + // status + mutable bool _dirty, _valid; + +}; + + +// reconfigure the grid if necessary. +inline void FlowGridManager::_checkReconfigure() const +{ if (!_dirty) return; + _reconfigure(); +} + +#endif + diff --git a/kicker/applets/launcher/launcherapplet.kcfg b/kicker/applets/launcher/launcherapplet.kcfg new file mode 100644 index 000000000..3433bf437 --- /dev/null +++ b/kicker/applets/launcher/launcherapplet.kcfg @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile arg="true"/> + <group name="General"> + <entry name="ConserveSpace" type="Bool"> + <label>Conserve Space</label> + <default>true</default> + </entry> + <entry name="DragEnabled" type="Bool"> + <label>Drag Enabled</label> + <default>true</default> + </entry> + <entry name="IconDim" type="Int"> + <label>Icon Size</label> + <default>0</default> + </entry> + <entry name="IconDimChoices" type="IntList"> + <label>Offered Icon Sizes</label> + <default>16,20,24,28,32,48,64</default> + </entry> + <entry name="Buttons" type="StringList"> + <label>Buttons</label> + <default>kde-Home.desktop,kde-konsole.desktop,kde-KControl.desktop,kde-Help.desktop,kde-kwrite.desktop</default> + </entry> + <entry name="VolatileButtons" type="StringList"> + <label>Volatile Buttons</label> + <whatsthis>Buttons that can be removed dynamically if they become unpopular</whatsthis> + <default></default> + </entry> + <entry name="ShowVolatileButtonIndicator" type="Bool"> + <label>Show frame for volatile buttons</label> + <default>true</default> + </entry> + <entry name="AutoAdjustEnabled" type="Bool"> + <label>Auto Adjust Enabled</label> + <default>false</default> + </entry> + <entry name="AutoAdjustMinItems" type="Int"> + <label>Minimum Number of Items</label> + <min>0</min> + <default>3</default> + </entry> + <entry name="AutoAdjustMaxItems" type="Int"> + <label>Maximum Number of Items</label> + <min>0</min> + <default>6</default> + </entry> + <entry name="HistoryHorizon" type="Int"> + <label>History Weight</label> + <min>0</min> + <max>100</max> + <default>70</default> + </entry> + </group> + <group name="PopularityData"> + <entry name="ServiceCacheSize" key="ServiceCacheSize" type="Int"> + <label>Service Cache Size</label> + <whatsthis>Number of services to remember</whatsthis> + <default>500</default> + </entry> + <entry name="ServiceNames" key="ServiceNames" type="StringList"> + <label>Service Names</label> + <whatsthis>Name of known services</whatsthis> + </entry> + <entry name="ServiceInspos" key="ServiceInspos" type="IntList"> + <label>Service Insertion Positions</label> + <whatsthis>Position where services are inserted when they regain popularity</whatsthis> + </entry> + <entry name="ServiceHistories" key="ServiceHistories" type="StringList"> + <label>Service History Data</label> + <whatsthis>History Data used to determine the popularity of a service</whatsthis> + </entry> + </group> +</kcfg> diff --git a/kicker/applets/launcher/popularity.cpp b/kicker/applets/launcher/popularity.cpp new file mode 100644 index 000000000..3bfcdd872 --- /dev/null +++ b/kicker/applets/launcher/popularity.cpp @@ -0,0 +1,424 @@ +/***************************************************************** + +Copyright (c) 2005 Fred Schaettgen <kde.sch@ttgen.net> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include "popularity.h" +#include "prefs.h" +#include <assert.h> +#include <algorithm> +#include <iterator> +#include <kconfig.h> +#include <kdebug.h> +#include <list> +#include <set> +#include <map> +#include <cmath> +#include <vector> + +using namespace std; + +class PopularityStatisticsImpl +{ +public: + struct SingleFalloffHistory + { + public: + // fallof is a number between 0 and 1. The popularity of + // each service is multiplied with the falloff value + // every time a service is used. Only the used service + // gets also 1-falloff added to its popularity. + double falloff; + // popularity values for each service + map<QString, double> vals; + // accumulated popularity of the unknown programs + // started before the statistic started + double iniVal; + }; + + struct Popularity + { + QString service; + double popularity; + bool operator<(const Popularity& p) const + { + return popularity > p.popularity; + } + }; + + PopularityStatisticsImpl(); + void normalizeHistory(SingleFalloffHistory& h); + void updateServiceRanks(); + + vector<SingleFalloffHistory> m_stats; + vector<Popularity> m_servicesByPopularity; + map<QString, int> m_serviceRanks; + double m_historyHorizon; +}; + +// ---- Public methods ---- + +PopularityStatistics::PopularityStatistics() : + d(new PopularityStatisticsImpl()) +{ +} + +PopularityStatistics::~PopularityStatistics() +{ + delete d; +} + +void PopularityStatistics::useService(const QString& service) +{ + vector<PopularityStatisticsImpl::SingleFalloffHistory>::iterator + it(d->m_stats.begin()), end(d->m_stats.end()); + for (; it != end; ++it) + { + map<QString, double>::iterator valIt; + bool found(false); + for (valIt = it->vals.begin(); valIt != it->vals.end(); ++valIt) + { + valIt->second = valIt->second * it->falloff; + if (valIt->first == service) + { + found = true; + valIt->second += 1-it->falloff; + } + } + it->iniVal = it->iniVal * it->falloff; + if (found == false) + { + it->vals[service] = 1-it->falloff; + } + d->normalizeHistory(*it); + } + d->updateServiceRanks(); +} + +void PopularityStatistics::moveToTop(const QStringList& newTopServiceList) +{ + vector<PopularityStatisticsImpl::SingleFalloffHistory>::iterator + histIt(d->m_stats.begin()), histEnd(d->m_stats.end()); + for (; histIt != histEnd; ++histIt) + { + set<QString> newTopServices; + for (uint n=0; n<newTopServiceList.size(); ++n) + newTopServices.insert(newTopServiceList[n]); + + // Sort by popularity + vector<PopularityStatisticsImpl::Popularity> ranking; + map<QString, double>::iterator valIt; + for (valIt = histIt->vals.begin(); valIt != histIt->vals.end(); ++valIt) + { + PopularityStatisticsImpl::Popularity pop; + pop.service = valIt->first; + pop.popularity = valIt->second; + ranking.push_back(pop); + } + stable_sort(ranking.begin(), ranking.end()); + + // Get the new positions of each service in the ranking. + // We don't touch the popularity values in the ranking. + list<QString> topServiceList, bottomServiceList; + vector<PopularityStatisticsImpl::Popularity>:: iterator rankIt; + for (rankIt = ranking.begin(); rankIt != ranking.end(); ++rankIt) + { + if (newTopServices.find(rankIt->service) != newTopServices.end()) + { + topServiceList.push_back(rankIt->service); + //kdDebug() << "top service: " << valIt->first << endl; + newTopServices.erase(rankIt->service); + } + else + { + //kdDebug() << "bottom service: " << valIt->first << endl; + bottomServiceList.push_back(rankIt->service); + } + } + // Append remaining new services to the topServices list + while (newTopServices.size() > 0) + { + topServiceList.push_back(*newTopServices.begin()); + newTopServices.erase(newTopServices.begin()); + } + + list<QString> newServiceList; + copy(topServiceList.begin(), topServiceList.end(), + back_insert_iterator<list<QString> >(newServiceList)); + copy(bottomServiceList.begin(), bottomServiceList.end(), + back_insert_iterator<list<QString> >(newServiceList)); + + // Merge the old list of service popularities + // with the new ordering of the services + histIt->vals.clear(); + list<QString>::iterator servIt; + uint serviceIndex = 0; + //kdDebug() << endl; + + for (servIt = newServiceList.begin(); servIt != newServiceList.end(); + ++servIt) + { + if (serviceIndex < ranking.size()) + { + histIt->vals[*servIt] = ranking[serviceIndex].popularity; + //kdDebug() << "->Re-Added service " << + //ranking[serviceIndex].popularity + // << " " << *servIt << endl; + //kdDebug() << "...was replaced by " << *servIt << endl; + } + else + { + //kdDebug() << "Service " << *servIt << endl; + //kdDebug() << "...was set to popularity=0" << endl; + histIt->vals[*servIt] = 0.00001; + } + // Make sure that the topServices are actually bigger than + // the bottomServices and not just bigger or equal + // and also that no services have popularity==0 + if (serviceIndex >= topServiceList.size()) + { + histIt->vals[*servIt] *= histIt->falloff; + } + ++serviceIndex; + } + d->normalizeHistory(*histIt); + } + d->updateServiceRanks(); +} + +/*v +Old version - moves everything else one position up +and 'service' to the bottom +void PopularityStatistics::moveToBottom(const QString& service) +{ + // Moves a service to the bottom of the ranking + // by moving everything else to the top + d->updateServiceRanks(); + QStringList allButOneServices; + vector<PopularityStatisticsImpl::Popularity>::iterator + it(d->m_servicesByPopularity.begin()), + end(d->m_servicesByPopularity.end()); + for (; it != end; ++it) + { + if (it->service != service) + allButOneServices << it->service; + } + moveToTop(allButOneServices); +}*/ + +void PopularityStatistics::moveToBottom(const QString& service) +{ + vector<PopularityStatisticsImpl::SingleFalloffHistory>::iterator + it(d->m_stats.begin()), end(d->m_stats.end()); + for (; it != end; ++it) + { + it->iniVal += it->vals[service]; + it->vals[service] = 0; + d->normalizeHistory(*it); + } + d->updateServiceRanks(); +} + +QString PopularityStatistics::serviceByRank(int n) const +{ + if (n >= 0 && n < int(d->m_servicesByPopularity.size())) + return d->m_servicesByPopularity[n].service; + else + return QString(); +} + +double PopularityStatistics::popularityByRank(int n) const +{ + if (n >= 0 && n < int(d->m_servicesByPopularity.size())) + return d->m_servicesByPopularity[n].popularity; + else + return 0.0; +} + +int PopularityStatistics::rankByService(const QString service) +{ + if (d->m_serviceRanks.find(service) != d->m_serviceRanks.end()) + { + return d->m_serviceRanks[service]; + } + return -1; +} + +void PopularityStatistics::writeConfig(Prefs* prefs) const +{ + QStringList serviceNames, serviceHistories; + int limit = prefs->serviceCacheSize(); + //kdDebug() << "popularityData: writeConfig" << endl; + for (int n=0; n<int(d->m_servicesByPopularity.size()) && n<limit; ++n) + { + PopularityStatisticsImpl::Popularity pop = d->m_servicesByPopularity[n]; + QStringList historyData; + for (int i=0; i<int(d->m_stats.size()); ++i) + { + historyData << QString::number(d->m_stats[i].vals[pop.service]); + } + serviceNames << pop.service; + serviceHistories << historyData.join("/"); + //kdDebug() << "popularityData: writeConfig -- " << pop.service << endl; + } + prefs->setServiceNames(serviceNames); + prefs->setServiceHistories(serviceHistories); +} + +void PopularityStatistics::readConfig(Prefs* prefs) +{ + int n = 0; + QStringList serviceNames = prefs->serviceNames(); + QStringList histories = prefs->serviceHistories(); + for (n = std::min(serviceNames.size(), histories.size())-1; n>=0; --n) + { + QString serviceName = serviceNames[n]; + QStringList serviceHistory = + QStringList::split("/", histories[n]); + for (int i=min(serviceHistory.size(), d->m_stats.size())-1; i>=0; --i) + { + d->m_stats[i].vals[serviceName] = serviceHistory[i].toDouble(); + } + } + + for (int i=0; i<int(d->m_stats.size()); ++i) + { + map<QString, double>::iterator valIt; + double valSum = 0; + for (valIt = d->m_stats[i].vals.begin(); + valIt != d->m_stats[i].vals.end(); ++valIt) + { + if (valIt->second < 0) valIt->second = 0; + valSum += valIt->second; + } + // Scale down values if their sum is bigger than 1 + // because of rounding errors or a corrupted config file + if (valSum > 1) + { + for (valIt = d->m_stats[i].vals.begin(); + valIt != d->m_stats[i].vals.end(); ++valIt) + { + valIt->second = valIt->second / valSum; + } + } + d->m_stats[i].iniVal = 1-valSum; + } + d->updateServiceRanks(); +} + +void PopularityStatistics::setHistoryHorizon(double h) +{ + d->m_historyHorizon = std::max(std::min(h, 1.0), 0.0); + d->updateServiceRanks(); +} + +double PopularityStatistics::historyHorizon() +{ + return d->m_historyHorizon; +} + + +// ---- Implementation methods ---- + +PopularityStatisticsImpl::PopularityStatisticsImpl() +{ + const int rateBaseCount(8); + + m_historyHorizon = 0.0; + + for (int n=0; n<rateBaseCount; ++n) + { + SingleFalloffHistory h; + h.falloff = 1.0 - (0.5 / std::exp(double(n)*1.5)); + m_stats.push_back(h); + } +} + +void PopularityStatisticsImpl::normalizeHistory(SingleFalloffHistory& h) +{ + //kdDebug() << "Normalize history" << endl; + double sum = h.iniVal; + map<QString, double>::iterator it; + for (it = h.vals.begin(); it != h.vals.end(); ++it) + { + sum += it->second; + } + for (it = h.vals.begin(); it != h.vals.end(); ++it) + { + it->second = it->second / sum; + } + h.iniVal = h.iniVal / sum; +} + +void PopularityStatisticsImpl::updateServiceRanks() +{ + // For each service calculate the average over the popularity + // for all falloff values and then sort by these averaged values + + vector<SingleFalloffHistory>::iterator + it(m_stats.begin()), end(m_stats.end()); + map<QString, double> serviceValSum, serviceValWeightSum; + int numStats = m_stats.size(); + for (int statIndex = 0; it != end; ++it, ++statIndex) + { + // Put more weight on the short term history if m_historyHorizon==0 + // and more on the the long term history for m_historyHorizon==1 + double a = 2*(numStats-1)*m_historyHorizon - numStats + 0.5; + if (statIndex < a || statIndex > a + numStats) + { + continue; + } + + map<QString, double>::iterator valIt; + /*double valSum = 0; + for (valIt = it->vals.begin(); valIt != it->vals.end(); ++valIt) + { + valSum += valIt->second; + } + if (valSum == 0) valSum = 1;*/ + for (valIt = it->vals.begin(); valIt != it->vals.end(); ++valIt) + { + serviceValWeightSum[valIt->first] += 1; + serviceValSum[valIt->first] += valIt->second; + } + } + + m_servicesByPopularity.clear(); + map<QString, double>::iterator sIt; + for (sIt = serviceValWeightSum.begin(); + sIt != serviceValWeightSum.end(); ++sIt) + { + Popularity p; + p.service = sIt->first; + assert(sIt->second > 0); + p.popularity = serviceValSum[sIt->first] / sIt->second; + m_servicesByPopularity.push_back(p); + } + stable_sort(m_servicesByPopularity.begin(), m_servicesByPopularity.end()); + m_serviceRanks.clear(); + for (uint n = 0; n < m_servicesByPopularity.size(); ++n) + { + m_serviceRanks[m_servicesByPopularity[n].service] = n; + /*kdDebug() << QString("Rank %1: %2 %3").arg(n) + .arg(m_servicesByPopularity[n].popularity) + .arg(m_servicesByPopularity[n].service) << endl;*/ + } +} diff --git a/kicker/applets/launcher/popularity.h b/kicker/applets/launcher/popularity.h new file mode 100644 index 000000000..b1dcb32d6 --- /dev/null +++ b/kicker/applets/launcher/popularity.h @@ -0,0 +1,127 @@ +/***************************************************************** + +Copyright (c) 2005 Fred Schaettgen <kde.sch@ttgen.net> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __popularity_h__ +#define __popularity_h__ + +#include <qstring.h> +#include <qstringlist.h> + +class PopularityStatisticsImpl; +class Prefs; + +/** + * Tracks the usage of any kind of service to offer recommendations. + * A service is identified by a string. After calling @useService + * a few times, you can get a popularity ranking for the used + * services. + * The algorithm tries to take both short- and long-term usage + * into account at the same time. + * The popularity value can be interpreted as the probability + * that the given service will be the next one to be used. + * If some new services are suddenly used a few times, their ranking + * may grow higher than the ranking of services, which were used very + * frequently a while ago. But after this short term usage has + * stopped, its influence gets weaker more quickly then for the old, + * more frequently used services. + * During first time usage, the algorithm needs some time to stabilize, + * since there is simply no dependable long term usage data available, + * so the ranking is more likely to change at first. + * But in the long run, the behaviour of the algorithm is + * completely stable, unlike simple usage counting for instance. + */ +class PopularityStatistics +{ +public: + PopularityStatistics(); + virtual ~PopularityStatistics(); + + /** + * Touch a service. This will increase the usage + * counters for the given service and decrease the + * counters for all the others. + */ + void useService(const QString& service); + + /** + * Exchange all state variables of the most + * popular service with those from services, + * moving the given services to the top of the + * list. Apart from that the order stays the same + * as before. Order of items in the string list + * does *not* matter/ + */ + void moveToTop(const QStringList& services); + + /** + * Sets all counters to zero for the given service + */ + void moveToBottom(const QString& service); + + /** + * Retrieve the name of a service by its position + * in the current popularity ranking + */ + QString serviceByRank(int n) const; + + /** + * Retrieve the popularity (0-1) of a service by + * its position in the current popularity ranking + */ + double popularityByRank(int n) const; + + /** + * Gets the rank of a given service. + * Returns -1 if the service is not in the ranking + */ + int rankByService(const QString service); + + /** + * Writes the configuration. + * A section must be set already for config. + */ + void writeConfig(Prefs* prefs) const; + + /** + * Reads the configuration. + * A section must be set already for config. + */ + void readConfig(Prefs* prefs); + + /** + * Modify the weighting of the history logs. + * 0 <= h <= 1. 1 means long term history + * 0 means short term history - in fact the popularity ranking + * becomes a recently-used list in that case. + */ + void setHistoryHorizon(double h); + double historyHorizon(); + +protected: + PopularityStatisticsImpl *d; + +private: + PopularityStatistics(const PopularityStatistics&) {} +}; + +#endif diff --git a/kicker/applets/launcher/prefs.kcfgc b/kicker/applets/launcher/prefs.kcfgc new file mode 100644 index 000000000..26a3f3d07 --- /dev/null +++ b/kicker/applets/launcher/prefs.kcfgc @@ -0,0 +1,6 @@ +# Code generation options for kconfig_compiler +File=launcherapplet.kcfg +ClassName=Prefs +Singleton=false +Mutators=AutoAdjustMaxItems,Buttons,VolatileButtons,AutoAdjustMaxItems,AutoAdjustMinItems,AutoAdjustEnabled,IconDim,DragEnabled,ConserveSpace,ServiceInspos,ServiceNames,ServiceHistories +# MemberVariables=public diff --git a/kicker/applets/launcher/quickaddappsmenu.cpp b/kicker/applets/launcher/quickaddappsmenu.cpp new file mode 100644 index 000000000..74d00a6e4 --- /dev/null +++ b/kicker/applets/launcher/quickaddappsmenu.cpp @@ -0,0 +1,67 @@ +/***************************************************************** + +Copyright (c) 2000 Bill Nagel + based on paneladdappsmenu.cpp which is + Copyright (c) 1999-2000 the kicker authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <kstandarddirs.h> +#include <kdesktopfile.h> +#include <kglobalsettings.h> +#include <ksycocaentry.h> +#include <kservice.h> +#include <kservicegroup.h> + +#include <kdebug.h> +#include "quickaddappsmenu.h" + +QuickAddAppsMenu::QuickAddAppsMenu(const QString &label, const QString &relPath, QWidget *target, QWidget *parent, const char *name, const QString &sender) + : PanelServiceMenu(label, relPath, parent, name) +{ + _targetObject = target; + _sender = sender; + connect(this, SIGNAL(addAppBefore(QString,QString)), + target, SLOT(addAppBeforeManually(QString,QString))); +} + +QuickAddAppsMenu::QuickAddAppsMenu(QWidget *target, QWidget *parent, const QString &sender, const char *name) + : PanelServiceMenu(QString::null, QString::null, parent, name) +{ + _targetObject = target; + _sender = sender; + connect(this, SIGNAL(addAppBefore(QString,QString)), + target, SLOT(addAppBeforeManually(QString,QString))); +} + +void QuickAddAppsMenu::slotExec(int id) +{ + if (!entryMap_.contains(id)) return; + KSycocaEntry * e = entryMap_[id]; + KService::Ptr service = static_cast<KService *>(e); + emit addAppBefore(locate("apps", service->desktopEntryPath()),_sender); +} + + +PanelServiceMenu *QuickAddAppsMenu::newSubMenu(const QString &label, const QString &relPath, QWidget *parent, const char *name, const QString &insertInlineHeader) +{ + return new QuickAddAppsMenu(label, relPath, _targetObject, parent, name, _sender); +} +#include "quickaddappsmenu.moc" diff --git a/kicker/applets/launcher/quickaddappsmenu.h b/kicker/applets/launcher/quickaddappsmenu.h new file mode 100644 index 000000000..88465049c --- /dev/null +++ b/kicker/applets/launcher/quickaddappsmenu.h @@ -0,0 +1,51 @@ +/***************************************************************** + +Copyright (c) 2000 Bill Nagel + based on paneladdappsmenu.h which is + Copyright (c) 1999-2000 the kicker authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +s +******************************************************************/ + +#ifndef __quickaddappsmenu_h__ +#define __quickaddappsmenu_h__ + +#include "service_mnu.h" + +class QuickAddAppsMenu: public PanelServiceMenu { + Q_OBJECT +public: + QuickAddAppsMenu(const QString &label, const QString &relPath, QWidget *target, QWidget *parent=0, const char *name=0, const QString &sender=QString("")); + QuickAddAppsMenu(QWidget *target, QWidget *parent=0, const QString &sender=QString(""), const char *name=0); +signals: + void addAppBefore(QString,QString); +protected slots: + virtual void slotExec(int id); +protected: + virtual PanelServiceMenu *newSubMenu(const QString &label, + const QString &relPath, + QWidget *parent, + const char *name, + const QString & _inlineHeader=QString::null); +private: + QWidget *_targetObject; + QString _sender; +}; + +#endif diff --git a/kicker/applets/launcher/quickbutton.cpp b/kicker/applets/launcher/quickbutton.cpp new file mode 100644 index 000000000..933088b04 --- /dev/null +++ b/kicker/applets/launcher/quickbutton.cpp @@ -0,0 +1,322 @@ +/***************************************************************** + +Copyright (c) 2000 Bill Nagel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include "quickbutton.h" +#include "quickaddappsmenu.h" + +#include <qpainter.h> +#include <qdrawutil.h> +#include <qpopupmenu.h> +#include <qtooltip.h> + +#include <kactionclasses.h> +#include <kickertip.h> +#include <klocale.h> +#include <kdesktopfile.h> +#include <krun.h> +#include <kiconeffect.h> +#include <kglobalsettings.h> +#include <kcursor.h> +#include <kapplication.h> +#include <kipc.h> +#include <kiconloader.h> +#include <kurldrag.h> +#include <kstandarddirs.h> + +#include <math.h> +#include <algorithm> + +#ifdef DEBUG + #define DEBUGSTR kdDebug() +#else + #define DEBUGSTR kndDebug() +#endif + +QuickURL::QuickURL(const QString &u) +{ DEBUGSTR<<"QuickURL::QuickURL("<<u<<")"<<endl<<flush; + KService::Ptr _service=0; + _menuId = u; + if (_menuId.startsWith("file:") && _menuId.endsWith(".desktop")) { + // this ensures that desktop entries are referenced by desktop name instead of by file name + _menuId=KURL(_menuId).path(); + } + if (_menuId.startsWith("/")) { + // Absolute path + _kurl.setPath(_menuId); + + if (_menuId.endsWith(".desktop")) { + // Strip path + QString s = _menuId; + s = s.mid(s.findRev('/')+1); + s = s.left(s.length()-8); + _service = KService::serviceByStorageId(s); + if (!_service) { + _service = new KService(_menuId); + } else { + } + } + } else if (!KURL::isRelativeURL(_menuId)) { + // Full URL + _kurl = _menuId; + } else { + // menu-id + _service = KService::serviceByMenuId(_menuId); + } + DEBUGSTR << "QuickURL: _service='"<<_service<<" _kurl="<<_kurl<<" _menuId="<<_menuId<<endl<<flush; + + if (_service) { + if (!_service->isValid()) { + DEBUGSTR << "QuickURL: _service is not valid"<<endl<<flush; + // _service is a KShared pointer, don't try to delete it! + _service = 0; + } else { + DEBUGSTR << "QuickURL: _service='"<<_service<<"' _service->desktopEntryPath()="<<_service->desktopEntryPath()<<endl<<flush; + if (_kurl.path().length() == 0) + { + _kurl.setPath(locate("apps", _service->desktopEntryPath())); + } + if (!_service->menuId().isEmpty()) + _menuId = _service->menuId(); + + m_genericName = _service->genericName(); + m_name = _service->name(); + } + } else { + m_name = _kurl.prettyURL(); + } + DEBUGSTR<<"QuickURL::QuickURL("<<u<<") END"<<endl<<flush; +} + +void QuickURL::run() const +{ kapp->propagateSessionManager(); // is this needed? + if (_service) + KRun::run(*(_service), KURL::List()); + else + new KRun(_kurl, 0, _kurl.isLocalFile()); +} + +//similar to MimeType::pixmapForURL +QPixmap QuickURL::pixmap( mode_t _mode, KIcon::Group _group, + int _force_size, int _state, QString *) const +{ // Load icon + QPixmap pxmap = KMimeType::pixmapForURL(_kurl, _mode, _group, _force_size, _state); + // Resize to fit button + pxmap.convertFromImage(pxmap.convertToImage().smoothScale(_force_size,_force_size, QImage::ScaleMin)); + return pxmap; +} + + +QuickButton::QuickButton(const QString &u, KAction* configAction, + QWidget *parent, const char *name) : + SimpleButton(parent, name), + m_flashCounter(0), + m_sticky(false) +{ + installEventFilter(KickerTip::the()); + setMouseTracking(true); + _highlight = false; + _oldCursor = cursor(); + _qurl=new QuickURL(u); + + QToolTip::add(this, _qurl->name()); + resize(int(DEFAULT_ICON_DIM),int(DEFAULT_ICON_DIM)); + QBrush bgbrush(colorGroup().brush(QColorGroup::Background)); + + QuickAddAppsMenu *addAppsMenu = new QuickAddAppsMenu( + parent, this, _qurl->url()); + _popup = new QPopupMenu(this); + _popup->insertItem(i18n("Add Application"), addAppsMenu); + configAction->plug(_popup); + _popup->insertSeparator(); + _popup->insertItem(SmallIcon("remove"), i18n("Remove"), + this, SLOT(removeApp())); + + m_stickyAction = new KToggleAction(i18n("Never Remove Automatically"), + KShortcut(), this); + connect(m_stickyAction, SIGNAL(toggled(bool)), + this, SLOT(slotStickyToggled(bool))); + m_stickyAction->plug(_popup, 2); + m_stickyId = _popup->idAt(2); + + connect(this, SIGNAL(clicked()), SLOT(launch())); + connect(this, SIGNAL(removeApp(QuickButton *)), parent, + SLOT(removeAppManually(QuickButton *))); +} + +QuickButton::~QuickButton() +{ + delete _qurl; +} + + +QString QuickButton::url() const +{ + return _qurl->url(); +} + + +QString QuickButton::menuId() const +{ return _qurl->menuId();} + + +void QuickButton::loadIcon() +{ + // Set Icon Dimension from size + _iconDim=std::min(size().width(),size().height())-2*ICON_MARGIN; + // Load icons + _icon = _qurl->pixmap(0, KIcon::Panel, _iconDim, KIcon::DefaultState); + _iconh = _qurl->pixmap(0, KIcon::Panel, _iconDim, KIcon::ActiveState); + setPixmap(_icon); +} + +void QuickButton::resizeEvent(QResizeEvent *e) +{ + loadIcon(); + SimpleButton::resizeEvent(e); +} + +void QuickButton::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == RightButton) + _popup->popup(e->globalPos()); + else if (e->button() == LeftButton) { + _dragPos = e->pos(); + QButton::mousePressEvent(e); + } +} + +void QuickButton::mouseMoveEvent(QMouseEvent *e) +{ + if ((e->state() & LeftButton) == 0) return; + QPoint p(e->pos() - _dragPos); + if (p.manhattanLength() <= KGlobalSettings::dndEventDelay()) + return; + DEBUGSTR<<"dragstart"<<endl<<flush; + setDown(false); + if (_dragEnabled) { + KURL::List uris; + uris.append(_qurl->kurl()); + DEBUGSTR<<"creating KURLDrag"<<endl<<flush; + KURLDrag *dd = new KURLDrag(uris,this); + dd->setPixmap(_icon); //PIX + DEBUGSTR<<"ready to drag"<<endl<<flush; + grabKeyboard(); + dd->drag(); + releaseKeyboard(); + } else { + setCursor(Qt::ForbiddenCursor); + } +} + +void QuickButton::slotIconChanged(int group) +{ + loadIcon(); + SimpleButton::slotIconChanged(group); + update(); +} + +void QuickButton::launch() +{ + setDown(false); + update(); + KIconEffect::visualActivate(this, rect()); + _qurl->run(); + emit executed(_qurl->menuId()); +} + +void QuickButton::setDragging(bool enable) +{ + setDown(enable); + _highlight=enable; + update(); +} + +void QuickButton::setEnableDrag(bool enable) +{ + _dragEnabled=enable; +} + +void QuickButton::removeApp() +{ + emit removeApp(this); +} + +void QuickButton::flash() +{ + m_flashCounter = 2000; + QTimer::singleShot(0, this, SLOT(slotFlash())); +} + +void QuickButton::slotFlash() +{ + static const int timeout = 500/4; + if (m_flashCounter > 0) + { + m_flashCounter -= timeout; + if (m_flashCounter < 0) m_flashCounter = 0; + update(); + QTimer::singleShot(timeout, this, SLOT(slotFlash())); + } +} + +void QuickButton::slotStickyToggled(bool isSticky) +{ + m_sticky = isSticky; + emit stickyToggled(isSticky); +} + +void QuickButton::setSticky(bool sticky) +{ + m_stickyAction->setChecked(sticky); + slotStickyToggled(sticky); +} + +void QuickButton::updateKickerTip(KickerTip::Data &data) +{ + if (!_qurl) + { + return; + } + data.message = _qurl->name(); + data.direction = m_popupDirection; + data.subtext = _qurl->genericName(); + if (data.subtext == QString()) + { + data.subtext = data.message; + } + data.icon = KMimeType::pixmapForURL(_qurl->kurl(), 0, + KIcon::Panel, KIcon::SizeHuge, KIcon::DefaultState); +} + +void QuickButton::setPopupDirection(KPanelApplet::Direction d) +{ + m_popupDirection = d; +} + +void QuickButton::setDynamicModeEnabled(bool enabled) +{ + _popup->setItemVisible(m_stickyId, enabled); +} + + +#include "quickbutton.moc" diff --git a/kicker/applets/launcher/quickbutton.h b/kicker/applets/launcher/quickbutton.h new file mode 100644 index 000000000..98eabec6e --- /dev/null +++ b/kicker/applets/launcher/quickbutton.h @@ -0,0 +1,124 @@ +/***************************************************************** + +Copyright (c) 2000 Bill Nagel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __quickbutton_h__ +#define __quickbutton_h__ + +#include <qbutton.h> +#include <qpoint.h> +#include <qstring.h> +#include <qpixmap.h> +#include <qcursor.h> + +#include <kickertip.h> +#include <kicontheme.h> +#include <kmimetype.h> +#include <kpanelapplet.h> +#include <kservice.h> +#include <kurl.h> + +#include "simplebutton.h" + +class QPopupMenu; +class KAction; +class KToggleAction; + +class QuickURL { +public: + QuickURL(const QString &u); + KURL kurl() const {return _kurl;}; + QString url() const {return _kurl.url();}; + QString menuId() const {return _menuId;}; + QString genericName() const { return m_genericName; } + QString name() const { return m_name; } + KService::Ptr service() const {return _service;}; + void run() const; + QPixmap pixmap(mode_t _mode = 0, KIcon::Group _group = KIcon::Desktop, + int _force_size = 0, int _state = 0, QString * _path = 0L) const; + +private: + KURL _kurl; + QString _menuId; + QString m_genericName; + QString m_name; + KService::Ptr _service; +}; + + +class QuickButton: public SimpleButton, public KickerTip::Client { + Q_OBJECT + +public: + enum { DEFAULT_ICON_DIM = 16 }; + enum { ICON_MARGIN = 1 }; + QuickButton(const QString &u, KAction* configAction, + QWidget *parent=0, const char *name=0); + ~QuickButton(); + QString url() const; + QString menuId() const; + QPixmap icon() const{ return _icon;} + bool sticky() { return m_sticky; } + void setSticky(bool bSticky); + void setPopupDirection(KPanelApplet::Direction d); + + void setDragging(bool drag); + void setEnableDrag(bool enable); + void setDynamicModeEnabled(bool enabled); + void flash(); + +signals: + void removeApp(QuickButton *); + void executed(QString serviceStorageID); + void stickyToggled(bool isSticky); + +protected: + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void resizeEvent(QResizeEvent *rsevent); + void loadIcon(); + void updateKickerTip(KickerTip::Data &data); + +protected slots: + void slotIconChanged(int); + void launch(); + void removeApp(); + void slotFlash(); + void slotStickyToggled(bool isSticky); + +private: + int m_flashCounter; + QuickURL *_qurl; + QPoint _dragPos; + QPopupMenu *_popup; + QPixmap _icon, _iconh; + QCursor _oldCursor; + bool _highlight, _changeCursorOverItem, _dragEnabled; + int _iconDim; + bool m_sticky; + KToggleAction *m_stickyAction; + int m_stickyId; + KPanelApplet::Direction m_popupDirection; +}; + +#endif + diff --git a/kicker/applets/launcher/quickbuttongroup.h b/kicker/applets/launcher/quickbuttongroup.h new file mode 100644 index 000000000..1d373ae92 --- /dev/null +++ b/kicker/applets/launcher/quickbuttongroup.h @@ -0,0 +1,60 @@ +/* Copyright 2004, Daniel Woods Bullok <dan.devel@bullok.com> + distributed under the terms of the + GNU GENERAL PUBLIC LICENSE Version 2 - + See the file kdebase/COPYING for details +*/ + +#ifndef __quickbuttongroup_h__ +#define __quickbuttongroup_h__ + +#include <qstring.h> +#include <functional> +#include "easyvector.h" +#include "quickbutton.h" + + +class QuickButtonGroup: virtual public EasyVector< QuickButton* > { +public: + QuickButtonGroup(const EasyVector< QuickButton* > &kv):EasyVector< QuickButton* >(kv){}; + QuickButtonGroup():EasyVector< QuickButton* >(){}; + Index findDescriptor(const QString &desc); + + void show(); + void hide(); + void setDragging(bool drag); + void setEnableDrag(bool enable); + void deleteContents(); + void setUpdatesEnabled(bool enable); +}; + +QuickButtonGroup::Index QuickButtonGroup::findDescriptor(const QString &desc) +{ return findProperty(desc, std::mem_fun(&QuickButton::url));} + +inline void QuickButtonGroup::setUpdatesEnabled(bool enable) +{ for (QuickButtonGroup::iterator i=begin();i!=end();++i) { + (*i)->setUpdatesEnabled(enable); + if (enable) { (*i)->update();} + } +} + +inline void QuickButtonGroup::show() +{ std::for_each(begin(),end(),std::mem_fun(&QWidget::show));} + +inline void QuickButtonGroup::hide() +{ std::for_each(begin(),end(),std::mem_fun(&QWidget::hide));} + +inline void QuickButtonGroup::setDragging(bool drag) +{ std::for_each(begin(),end(),std::bind2nd(std::mem_fun(&QuickButton::setDragging),drag));} + +inline void QuickButtonGroup::setEnableDrag(bool enable) +{ std::for_each(begin(),end(),std::bind2nd(std::mem_fun(&QuickButton::setEnableDrag),enable));} + +inline void QuickButtonGroup::deleteContents() +{ for (QuickButtonGroup::iterator i=begin();i!=end();++i) { + delete (*i); + (*i)=0; + } +} + +#endif + diff --git a/kicker/applets/launcher/quicklauncher.cpp b/kicker/applets/launcher/quicklauncher.cpp new file mode 100644 index 000000000..abae9efe1 --- /dev/null +++ b/kicker/applets/launcher/quicklauncher.cpp @@ -0,0 +1,1093 @@ +/***************************************************************** + +Copyright (c) 2000 Bill Nagel +Copyright (c) 2004 Dan Bullok <dan.devel@bullok.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <qpainter.h> +#include <qpopupmenu.h> +#include <qslider.h> +#include <qtimer.h> +#include <qtooltip.h> + +#include <dcopclient.h> +#include <kaction.h> +#include <kapplication.h> +#include <kaboutapplication.h> +#include <kaboutdata.h> +#include <kdialogbase.h> +#include <kglobal.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <knuminput.h> +#include <kconfig.h> +#include <kstandarddirs.h> +#include <kurldrag.h> +#include <kdebug.h> + + +#include <algorithm> +#include <list> +#include <math.h> +#include <set> +#include <assert.h> + +#include "configdlg.h" +#include "popularity.h" +#include "quicklauncher.h" +#include "quickbutton.h" +#include "quickaddappsmenu.h" +#include "quickbuttongroup.h" + +typedef ButtonGroup::iterator ButtonIter; +const ButtonGroup::Index NotFound=ButtonGroup::NotFound; +const ButtonGroup::Index Append=ButtonGroup::Append; + +#ifdef DEBUG + #define DEBUGSTR kdDebug() +#else + #define DEBUGSTR kndDebug() +#endif + +extern "C" +{ + KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile) + { + KGlobal::locale()->insertCatalogue("quicklauncher"); + return new QuickLauncher(configFile, KPanelApplet::Normal, + KPanelApplet::Preferences, + parent, "quicklauncher"); + } +} + +QuickLauncher::QuickLauncher(const QString& configFile, Type type, int actions, + QWidget *parent, const char *name) : + KPanelApplet(configFile, type, actions, parent, name) +{ + DCOPObject::setObjId("QuickLauncherApplet"); + DEBUGSTR << endl << endl << endl << "------------" << flush; + DEBUGSTR << "QuickLauncher::QuickLauncher(" << configFile << ",...)" << + endl << flush; + + m_settings = new Prefs(sharedConfig()); + m_settings->readConfig(); + + m_needsSave = false; + m_needsRefresh = false; + m_refreshEnabled = false; + + m_configDialog = 0; + m_popup = 0; + m_appletPopup = 0; + m_removeAppsMenu = 0; + + m_dragAccepted = false; + + m_buttons = new ButtonGroup; + m_manager = new FlowGridManager; + m_newButtons = 0; + m_oldButtons = 0; + m_dragButtons = 0; + + m_configAction = new KAction(i18n("Configure Quicklauncher..."), "configure", KShortcut(), + this, SLOT(slotConfigure()), this); + + m_saveTimer = new QTimer(this); + connect(m_saveTimer, SIGNAL(timeout()), this, SLOT(saveConfig())); + + m_popularity = new PopularityStatistics(); + + setBackgroundOrigin(AncestorOrigin); + + loadConfig(); + + buildPopupMenu(); + m_minPanelDim = std::max(16, m_settings->iconDimChoices()[1]); + refreshContents(); + setRefreshEnabled(true); + + setAcceptDrops(true); + //QToolTip::add(this, i18n("Drop applications here")); + DEBUGSTR << " QuickLauncher::QuickLauncher(" << configFile << + ",...) END" << endl << flush; + + DCOPClient *dcopClient = KApplication::dcopClient(); + dcopClient->connectDCOPSignal(0, "appLauncher", + "serviceStartedByStorageId(QString,QString)", + "QuickLauncherApplet", + "serviceStartedByStorageId(QString,QString)", + false); + kdDebug() << "Quicklauncher registered DCOP signal" << endl; +} + + +//TODO:? Drag/drop more than one item at a time + +QuickLauncher::~QuickLauncher() +{ + KGlobal::locale()->removeCatalogue("quicklauncher"); + setCustomMenu(0); + delete m_popup; + delete m_appletPopup; + delete m_removeAppsMenu; + delete m_popularity; + clearTempButtons(); + if (m_buttons) + { + m_buttons->deleteContents(); + delete m_buttons; + } +} + +// Builds, connects _popup menu +void QuickLauncher::buildPopupMenu() +{ + QuickAddAppsMenu *addAppsMenu = new QuickAddAppsMenu(this, this); + m_popup = new QPopupMenu(this); + m_popup->insertItem(i18n("Add Application"), addAppsMenu); + m_configAction->plug(m_popup); + + m_appletPopup = new QPopupMenu(this); + m_appletPopup->insertItem(i18n("Add Application"), addAppsMenu); + m_removeAppsMenu = new QPopupMenu(this); + connect(m_removeAppsMenu, SIGNAL(aboutToShow()), + SLOT(fillRemoveAppsMenu())); + connect(m_removeAppsMenu, SIGNAL(activated(int)), + SLOT(removeAppManually(int))); + m_appletPopup->insertItem(i18n("Remove Application"), m_removeAppsMenu); + + m_appletPopup->insertSeparator(); + m_appletPopup->setCheckable( true ); + m_appletPopup->insertItem(i18n("About"), this, SLOT(about())); + setCustomMenu(m_appletPopup); +} + + +// Fill the remove apps menu +void QuickLauncher::fillRemoveAppsMenu() +{ + m_removeAppsMenu->clear(); + ButtonIter iter(m_buttons->begin()); + int i = 0; + while (iter != m_buttons->end()) + { + QString text = QToolTip::textFor(*iter); + if (text.isEmpty()) + { + text = (*iter)->url(); + if (text.isEmpty()) + { + text = i18n("Unknown"); + } + } + m_removeAppsMenu->insertItem((*iter)->icon(), text, i); + ++iter; + ++i; + } +} + +void QuickLauncher::slotSettingsDialogChanged() +{ + // Update conserve space setting + setConserveSpace(m_settings->conserveSpace()); + m_popularity->setHistoryHorizon(m_settings->historyHorizon()/100.0); + slotAdjustToCurrentPopularity(); + kdDebug() << "Icon size: " << m_settings->iconDim() << endl; + refreshContents(); + + saveConfig(); +} + +void QuickLauncher::action(Action a) +{ + if (a == KPanelApplet::Preferences) + { + slotConfigure(); + } + else + { + KPanelApplet::action(a); + } +} + +void QuickLauncher::slotConfigure() +{ + if (!m_configDialog) + { + m_configDialog = new ConfigDlg(this, "configdialog", + m_settings, SIZE_AUTO, KDialogBase::Plain, KDialogBase::Ok | + KDialogBase::Cancel | KDialogBase::Apply | KDialogBase::Default); + connect(m_configDialog, SIGNAL(settingsChanged()), + this, SLOT(slotSettingsDialogChanged())); + } + + m_configDialog->show(); +} + + +int QuickLauncher::findApp(QuickButton *button) +{ + if (m_buttons->empty()) + { + return NotFound; + } + int pos = m_buttons->findValue(button); + return pos; +} + + +int QuickLauncher::findApp(QString url) +{ + if (m_buttons->empty()) + { + return NotFound; + } + int pos=m_buttons->findDescriptor(url); + return pos; +} + +void QuickLauncher::removeAppManually(int index) +{ + removeApp(index, true); +} + +void QuickLauncher::removeApp(int index, bool manuallyRemoved) +{ + if (m_buttons->empty()) + { + return; + } + if (!m_buttons->isValidIndex(index)) + { + kdWarning() << " removeApp (" << index << + ") *******WARNING****** index=" << index << "is out of bounds." << + endl << flush; + return; + } + DEBUGSTR << "Removing button. index=" << index << " url='" << + (*m_buttons)[index]->url() << "'" << endl << flush; + + QString removeAppUrl = (*m_buttons)[index]->url(); + QString removeAppMenuId = (*m_buttons)[index]->menuId(); + + delete (*m_buttons)[index]; + m_buttons->eraseAt(index); + refreshContents(); + + if (int(m_buttons->size()) < m_settings->autoAdjustMinItems() && manuallyRemoved) + { + m_settings->setAutoAdjustMinItems(m_buttons->size()); + } + + if (manuallyRemoved) + { + m_popularity->moveToBottom(removeAppMenuId); + slotAdjustToCurrentPopularity(); + } + + saveConfig(); +} + + +void QuickLauncher::removeApp(QString url, bool manuallyRemoved) +{ + int index = findApp(url); + if (index == NotFound) + { + kdDebug() << "removeApp: Not found: " << url << endl; + return; + } + removeApp(index, manuallyRemoved); +} + + +void QuickLauncher::removeAppManually(QuickButton *button) +{ + int index = findApp(button); + if (index == NotFound) + { + return; + } + removeApp(index, true); +} + + +int QuickLauncher::widthForHeight(int h) const +{ + FlowGridManager temp_manager = *m_manager; + temp_manager.setFrameSize(QSize(h,h)); + temp_manager.setOrientation(Qt::Horizontal); // ??? probably not necessary + if (temp_manager.isValid()) + { + return temp_manager.frameSize().width(); + } + return m_minPanelDim; +} + + +int QuickLauncher::heightForWidth(int w) const +{ + FlowGridManager temp_manager=*m_manager; + temp_manager.setFrameSize(QSize(w,w)); + temp_manager.setOrientation(Qt::Vertical); // ??? probably not necessary + if (temp_manager.isValid()) + { + return temp_manager.frameSize().height(); + } + return m_minPanelDim; +} + + +int QuickLauncher::dimension() const +{ + if (orientation()==Qt::Vertical) + { + return size().width(); + } + return size().height(); +} + +void QuickLauncher::addApp(QString url, bool manuallyAdded) +{ + assert(m_buttons); + QString newButtonId = QuickURL(url).menuId(); + if (m_appOrdering.find(newButtonId) == m_appOrdering.end()) + { + m_appOrdering[newButtonId] = m_appOrdering.size(); + } + uint appPos; + for (appPos = 0; appPos < m_buttons->size(); ++appPos) + { + QString buttonId = (*m_buttons)[appPos]->menuId(); + if (m_appOrdering[buttonId] >= m_appOrdering[newButtonId]) + { + break; + } + } + addApp(url, appPos, manuallyAdded); +} + +QuickButton* QuickLauncher::createButton(QString url) +{ + QuickButton* newButton=new QuickButton(url, m_configAction, this); + connect(newButton, SIGNAL(executed(QString)), + this, SLOT(slotOwnServiceExecuted(QString))); + connect(newButton, SIGNAL(stickyToggled(bool)), + this, SLOT(slotStickyToggled())); + newButton->setPopupDirection(popupDirection()); + return newButton; +} + +void QuickLauncher::addApp(QString url, int index, bool manuallyAdded) +{ + DEBUGSTR << endl <<"About to add: url='" << url << + "' index=" << index << endl << flush; + QuickButton *newButton; + if (!m_buttons->isValidInsertIndex(index)) + { + kdWarning() << " *******WARNING****** index=" << index << + "is out of bounds." << endl << flush; + index = m_buttons->lastIndex(); + } + int old = findApp(QuickURL(url).url()); + if (old != NotFound) + { + if (index == old) + { + return; + } + if (index > old) + { + index--; + } + newButton = (*m_buttons)[old]; + m_buttons->eraseAt(old); + } + else + { + newButton = createButton(url); + } + m_buttons->insertAt(index, newButton); + DEBUGSTR << "Added: url='"<<url<<"' index="<<index<<endl<<endl<<flush; + refreshContents(); + + if (manuallyAdded) + { + newButton->setSticky(true); + if (int(m_buttons->size()) > m_settings->autoAdjustMaxItems()) + { + m_settings->setAutoAdjustMaxItems(m_buttons->size()); + } + } + + updateInsertionPosToStatusQuo(); + saveConfig(); +} + +void QuickLauncher::updateInsertionPosToStatusQuo() +{ + // Update the app ordering map, so that next time, + // addApp(url,manAdded) (without index) will insert the + // item at the same position again. + std::list<QString> appList; + std::set<int> posList; + //kdDebug() << "Rearranging application order. Before:" << endl; + for (uint n = 0; n < m_buttons->size(); ++n) + { + QString buttonId = (*m_buttons)[n]->menuId(); + appList.push_back(buttonId); + if (m_appOrdering.find(buttonId) == m_appOrdering.end()) + { + m_appOrdering[buttonId] = m_appOrdering.size(); + } + posList.insert(m_appOrdering[buttonId]); + //kdDebug() << m_appOrdering[buttonId] << " = " << buttonId << endl; + } + //kdDebug() << "After:" << endl; + while (posList.size() > 0) + { + assert(appList.size() > 0); + m_appOrdering[*appList.begin()] = *posList.begin(); + kdDebug() << *posList.begin() << " = " << *appList.begin() << endl; + posList.erase(posList.begin()); + appList.pop_front(); + } + //kdDebug() << "Done." << endl; +} + +void QuickLauncher::addAppBeforeManually(QString url, QString sender) +{ + if (sender.isNull()) + { + addApp(url, Append, true); + } + int pos = findApp(sender); + if (pos < 0) + { + pos = Append; + } + DEBUGSTR << "QuickLauncher::addAppBefore(" << url << + "," << sender << "): pos=" << pos << endl << flush; + addApp(url, pos, true); +} + + +void QuickLauncher::about() +{ + KAboutData about("quicklauncher", I18N_NOOP("Quick Launcher"), "2.0", + I18N_NOOP("A simple application launcher"), + KAboutData::License_GPL_V2, + "(C) 2000 Bill Nagel\n(C) 2004 Dan Bullok\n(C) 2005 Fred Schaettgen"); + KAboutApplication a(&about, this); + a.exec(); +} + + +void QuickLauncher::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == RightButton) + { + m_popup->popup(e->globalPos()); + } +} + +void QuickLauncher::resizeEvent(QResizeEvent*) +{ + refreshContents(); +} + +void QuickLauncher::dragEnterEvent(QDragEnterEvent *e) +{ + DEBUGSTR << "QuickLauncher::dragEnterEvent(pos=" << e->pos() << + " type=" << e->type() << ")" << endl << flush; + m_dragAccepted=false; + KURL::List kurlList; + if (!isDragEnabled() || !KURLDrag::decode(e, kurlList)) + { + e->accept(false); + return; + } + + if (kurlList.size()<=0) + { + e->accept(false); + return; + } + m_dragButtons=new ButtonGroup; + m_oldButtons=new ButtonGroup(*m_buttons); + + QString url; + KURL::List::ConstIterator it = kurlList.begin(); + for ( ; it != kurlList.end(); ++it ) + { + url = QuickURL((*it).url()).url(); + kdDebug() << " Drag Object='"<<url<<"' " << (*it).url() << endl; + int pos = m_buttons->findDescriptor(url); + if (pos != NotFound) + { + // if it's already in m_buttons, take it out + m_dragButtons->push_back(m_buttons->takeFrom(pos)); + } + else + { + // otherwise, create a new one + QuickButton* button = createButton(url); + button->setSticky(true); + m_dragButtons->push_back(button); + } + } + if (m_dragButtons->size() > 0) + { + //make sure we can drag at least one button. + m_dragAccepted=true; + m_newButtons=new ButtonGroup(*m_buttons); + m_dropPos=NotFound; + e->accept(true); + return; + } + e->accept(false); + clearTempButtons(); +} + + +void QuickLauncher::dragMoveEvent(QDragMoveEvent *e) +{ + if (!m_dragAccepted) + { + kdWarning() << "QuickLauncher::dragMoveEvent: Drag is not accepted." << + m_dragAccepted << endl << flush; + e->accept(false); + return; + } + + e->accept(true); + int pos=m_manager->indexNearest(e->pos()); + if (pos == m_dropPos) + { + return;// Already been inserted here, no need to update + } + + if (m_newButtons->isValidInsertIndex(pos)) + { + mergeButtons(pos); + m_dropPos=pos; + } + refreshContents(); +} + + +void QuickLauncher::dragLeaveEvent(QDragLeaveEvent *e) +{ + DEBUGSTR << "QuickLauncher::dragLeaveEvent(type=" << + e->type() << ")" << endl << flush; + if (!m_dragAccepted) + { + return; + } + + // No drop. Return to starting state. + std::swap(m_buttons,m_oldButtons); + clearTempButtons(); + + refreshContents(); + saveConfig(); +} + + +void QuickLauncher::dropEvent(QDropEvent *e) +{ + DEBUGSTR << "QuickLauncher::dropEvent(pos=" << e->pos() << + " type=" << e->type() << ")" << endl << flush; + if (!m_dragAccepted) + { + e->accept(false); + return; + } + + if (e->source() == 0) + { + for (uint n=0; n<m_dragButtons->size(); ++n) + { + (*m_dragButtons)[n]->setSticky(true); + } + } + + clearTempButtons(); + refreshContents(); + saveConfig(); + updateInsertionPosToStatusQuo(); +} + +// insert dragbuttons at index in m_newButtons. Put result in m_buttons +void QuickLauncher::mergeButtons(int index) +{ + if (!m_newButtons->isValidInsertIndex(index)) + { + index=m_newButtons->size(); + } + + m_buttons->clear(); + (*m_buttons) = (*m_newButtons); + m_buttons->insertAt(index, *m_dragButtons); + refreshContents(); +} + +void QuickLauncher::clearTempButtons() +{ + std::set<QuickButton*> allButtons; + //put all the m_buttons in a set (removes duplicates automatically + if (m_newButtons) + { + allButtons.insert(m_newButtons->begin(),m_newButtons->end()); + } + if (m_oldButtons) + { + allButtons.insert(m_oldButtons->begin(),m_oldButtons->end()); + } + if (m_dragButtons) + { + allButtons.insert(m_dragButtons->begin(),m_dragButtons->end()); + } + + //delete temp ButtonGroups + delete m_newButtons; m_newButtons=0; + delete m_oldButtons; m_oldButtons=0; + delete m_dragButtons; m_dragButtons=0; + + //if an element allButtons is NOT in m_buttons (the ones we keep), delete it + std::set<QuickButton *>::iterator iter = allButtons.begin(); + while (iter != allButtons.end()) + { + if (findApp(*iter) == NotFound) + { + delete *iter; + } + ++iter; + } + m_dragAccepted = false; + m_dropPos = NotFound; +} + +void QuickLauncher::refreshContents() +{ + int idim, d(dimension()); + // determine button size + if (m_settings->iconDim() == SIZE_AUTO) + { + if (d < 18) + { + idim = std::min(16,d); + } + else if (d < 64) + { + idim = 16; + } + else if (d < 80) + { + idim = 20; + } + else if (d < 122) + { + idim = 24; + } + else + { + idim = 28; + } + } + else + { + idim = std::min(m_settings->iconDim(), d - std::max((d/8)-1, 0) * 2); + } + m_space = std::max((idim/8)-1, 0); + m_border = m_space; + m_buttonSize = QSize(idim, idim); + m_manager->setOrientation(orientation()); + m_manager->setNumItems(m_buttons->size()); + m_manager->setFrameSize(size()); + m_manager->setItemSize(m_buttonSize); + m_manager->setSpaceSize(QSize(m_space, m_space)); + m_manager->setBorderSize(QSize(m_border, m_border)); + if (!m_refreshEnabled) + { + m_needsRefresh=true; + return; + } + if (!m_manager->isValid()) + { + kdDebug()<<endl<<"******WARNING****** Layout is invalid."<< + endl << flush; + m_manager->dump(); + return; + } + + unsigned index; + QPoint pos; + setUpdatesEnabled(false); + m_buttons->setUpdatesEnabled(false); + for (index = 0; index < m_buttons->size(); index++) + { + pos = m_manager->pos(index); + QuickButton *button = (*m_buttons)[index]; + button->resize(m_manager->itemSize()); + button->move(pos.x(), pos.y()); + button->setDragging(false); + button->setEnableDrag(isDragEnabled()); + button->setDynamicModeEnabled(m_settings->autoAdjustEnabled()); + } + if (m_newButtons) + { + m_newButtons->setDragging(false); + } + if (m_dragButtons) + { + m_dragButtons->setDragging(true); + } + m_buttons->show(); + setUpdatesEnabled(true); + update(); + m_buttons->setUpdatesEnabled(true); + updateGeometry(); + emit updateLayout(); + updateStickyHighlightLayer(); +} + + +void QuickLauncher::setDragEnabled(bool enable) +{ + m_settings->setDragEnabled(enable); +} + +void QuickLauncher::setConserveSpace(bool conserve_space) +{ + m_manager->setConserveSpace(conserve_space); + if (conserve_space) + { + m_manager->setSlack(FlowGridManager::SpaceSlack, + FlowGridManager::SpaceSlack); + } + else + { + m_manager->setSlack(FlowGridManager::ItemSlack, + FlowGridManager::ItemSlack); + } + refreshContents(); +} + +class SortByPopularity { +public: + bool operator()(const QuickLauncher::PopularityInfo& a, + const QuickLauncher::PopularityInfo& b) + { + return a.popularity < b.popularity; + } +}; + +void QuickLauncher::loadConfig() +{ + DEBUGSTR << "QuickLauncher::loadConfig()" << endl << flush; + //KConfig *c = config(); + //c->setGroup("General"); + setConserveSpace(m_settings->conserveSpace()); + setDragEnabled(m_settings->dragEnabled()); + /*DEBUGSTR << " IconDim="<<m_iconDim << endl << flush; + DEBUGSTR << " ConserveSpace=" << (m_manager->conserveSpace()) << + endl << flush; + DEBUGSTR << " DragEnabled=" << isDragEnabled() << endl << flush;*/ + QStringList volatileButtons = m_settings->volatileButtons(); + QStringList urls = m_settings->buttons(); + kdDebug() << "GetButtons " << urls.join("/") << endl; + QStringList::Iterator iter(urls.begin()); + int n = 0; + while (iter != urls.end()) { + QString url = *iter; + addApp(url, n, false); + ++iter; + ++n; + } + + // Restore sticky state + for (n=0; n<int(m_buttons->size()); ++n) + { + QuickButton* button = (*m_buttons)[n]; + if (volatileButtons.contains(button->menuId()) == false) + { + button->setSticky(true); + } + button->setDynamicModeEnabled(m_settings->autoAdjustEnabled()); + } + + m_popularity->readConfig(m_settings); + m_popularity->setHistoryHorizon(m_settings->historyHorizon()/100.0); + + QStringList serviceNames = m_settings->serviceNames(); + QValueList<int> insPos = m_settings->serviceInspos(); + for (int n=std::min(serviceNames.size(),insPos.size())-1; n>=0; --n) + { + m_appOrdering[serviceNames[n]] = insPos[n]; + } +} + +void QuickLauncher::saveConfig() +{ + if (!m_refreshEnabled) + { + m_needsSave=true; + return; + } + QStringList urls, volatileUrls; + ButtonIter iter = m_buttons->begin(); + while (iter != m_buttons->end()) { + if ((*iter)->sticky() == false) + { + volatileUrls.append((*iter)->menuId()); + } + urls.append((*iter)->menuId()); + ++iter; + } + m_settings->setButtons(urls); + kdDebug() << "SetButtons " << urls.join("/") << endl; + m_settings->setVolatileButtons(volatileUrls); + m_settings->setConserveSpace(m_manager->conserveSpace()); + m_settings->setDragEnabled(isDragEnabled()); + + m_popularity->writeConfig(m_settings); + + // m_popularity must have written the current service list by now + QStringList serviceNames = m_settings->serviceNames(); + QValueList<int> insertionPositions; + for (int n=0; n<int(serviceNames.size()); ++n) + { + if (m_appOrdering.find(serviceNames[n]) != m_appOrdering.end()) + { + insertionPositions.push_back(m_appOrdering[serviceNames[n]]); + } + } + m_settings->setServiceInspos(insertionPositions); + + m_settings->writeConfig(); +} + + +void QuickLauncher::setRefreshEnabled(bool enable) +{ + m_refreshEnabled=enable; + if (m_refreshEnabled) + { + if (m_needsSave) { + saveConfig(); + } + if (m_needsRefresh) { + refreshContents(); + } + } +} + +void QuickLauncher::serviceStartedByStorageId(QString /*starter*/, QString storageId) +{ + KService::Ptr service = KService::serviceByStorageId(storageId); + if (service->icon() == QString::null) + { + kdDebug() << storageId << " has no icon. Makes no sense to add it."; + return; + } + QuickURL url = QuickURL(locate("apps", service->desktopEntryPath())); + QString desktopMenuId(url.menuId()); + kdDebug() << "storageId=" << storageId << " desktopURL=" << desktopMenuId << endl; + // A service was started somwhere else. If the quicklauncher contains + // this service too, we flash the icon + QuickButton *startedButton = 0; + std::set<QString> buttonIdSet; + for (uint n = 0; n < m_buttons->size(); ++n) + { + QuickButton *button = (*m_buttons)[n]; + QString buttonMenuId = button->menuId(); + buttonIdSet.insert(buttonMenuId); + if (desktopMenuId == buttonMenuId) + { + kdDebug() << "QuickLauncher: I know that one: " << storageId << endl; + button->flash(); + startedButton = button; + } + } + + // Update popularity info. + // We do this even if autoadjust is disabled + // so there are sane values to start with if it's turned on. + m_popularity->useService(desktopMenuId); + + if (m_settings->autoAdjustEnabled()) + { + QTimer::singleShot(0, this, SLOT(slotAdjustToCurrentPopularity())); + } +} + +void QuickLauncher::slotAdjustToCurrentPopularity() +{ + // TODO: Shrink immediately if buttons->size() > maxItems + kdDebug() << "Starting popularity update" << endl; + PopularityStatistics* stats = m_popularity; + int minItems = m_settings->autoAdjustMinItems(); + int maxItems = m_settings->autoAdjustMaxItems(); + + static const double hysteresisFactor = 0.90; + double minAddPopularity = 0; + for (int n = 0; n < maxItems; ++n) + { + // All items with a popularity not less than 0.75 of the average + // of the first maxItems apps are included in the list + double belowAvgAllowed = 0.75; + minAddPopularity += (belowAvgAllowed * stats->popularityByRank(n)) / maxItems; + } + double minDelPopularity = minAddPopularity * hysteresisFactor; + std::map<QString, QuickButton*> removeableApps; + std::set<QString> existingApps; + int numApps = m_buttons->size(); + for (int n = 0; n < int(m_buttons->size()); ++n) + { + QuickButton *button = (*m_buttons)[n]; + if (((stats->popularityByRank(stats->rankByService(button->menuId())) < + minDelPopularity) || m_settings->autoAdjustEnabled()==false) && + (button->sticky() == false)) + { + removeableApps[button->menuId()] = button; + --numApps; + } + existingApps.insert(button->menuId()); + } + for (int n = 0; + (numApps < minItems && stats->popularityByRank(n) > 0) || + (numApps < maxItems && stats->popularityByRank(n) > minAddPopularity); + ++n) + { + QString app = m_popularity->serviceByRank(n); + if (existingApps.find(app) == existingApps.end()) + { + addApp(QuickURL(m_popularity->serviceByRank(n)).url(), false); + kdDebug() << "Adding app " << app << endl; + ++numApps; + } + else if (removeableApps.find(app) != removeableApps.end()) + { + removeableApps.erase(app); + ++numApps; + } + } + while (removeableApps.size() > 0) + { + removeApp(findApp(removeableApps.begin()->second), false); + kdDebug() << "Removing app " << removeableApps.begin()->first << endl; + removeableApps.erase(removeableApps.begin()->first); + } + kdDebug() << "done popularity update" << endl; + m_settings->setAutoAdjustMinItems(minItems); + m_settings->setAutoAdjustMaxItems(maxItems); + + // TODO: Think of something better than that: + m_saveTimer->start(10000,true); +} + +void QuickLauncher::slotOwnServiceExecuted(QString serviceMenuId) +{ + m_popularity->useService(serviceMenuId); + if (m_settings->autoAdjustEnabled()) + { + QTimer::singleShot(0, this, SLOT(slotAdjustToCurrentPopularity())); + } +} + +void QuickLauncher::updateStickyHighlightLayer() +{ + // Creates a transparent image which is used + // to highlight those buttons which will never + // be removed automatically from the launcher + QPixmap areaPix(width(), height()); + QPainter areaPixPainter(&areaPix); + areaPixPainter.fillRect(0, 0, width(), height(), QColor(255, 255, 255)); + QSize itemSize = m_manager->itemSize(); + QSize spaceSize = m_manager->spaceSize(); + for (uint n=0; n<m_buttons->size(); ++n) + { + QPoint pos = m_manager->pos(n); + if ((*m_buttons)[n]->sticky() == false) + { + areaPixPainter.fillRect(pos.x()-(spaceSize.width()+1)/2, + pos.y()-(spaceSize.height()+1)/2, + itemSize.width()+spaceSize.width()+1, + itemSize.height()+spaceSize.height()+1, + QColor(0, 0, 0)); + } + } + QImage areaLayer = areaPix.convertToImage(); + m_stickyHighlightLayer = QImage(width(), height(), 32); + m_stickyHighlightLayer.setAlphaBuffer(true); + int pix, tlPix, brPix, w(width()), h(height()); + QRgb transparent(qRgba(0, 0, 0, 0)); + for (int y = h-1; y >= 0; --y) + { + for (int x = w-1; x >= 0; --x) + { + pix = qRed(areaLayer.pixel(x, y)); + if (pix == 0) + { + tlPix = (y>0 && x>0) ? qRed(areaLayer.pixel(x-1,y-1)) : 255; + brPix = (y<h-1 && x<w-1) ? qRed(areaLayer.pixel(x+1,y+1)) : 255; + int c = tlPix-brPix < 0 ? 255 : 0; + int alpha = abs(tlPix-brPix)/2; + m_stickyHighlightLayer.setPixel(x, y, qRgba(c, c, c, alpha)); + } + else + { + m_stickyHighlightLayer.setPixel(x, y, transparent); + } + } + } + repaint(); +} + +void QuickLauncher::paintEvent(QPaintEvent* e) +{ + KPanelApplet::paintEvent(e); + + if (m_settings->autoAdjustEnabled() && + m_settings->showVolatileButtonIndicator()) + { + QPainter p(this); + p.drawImage(0, 0, m_stickyHighlightLayer); + } +} + +void QuickLauncher::slotStickyToggled() +{ + updateStickyHighlightLayer(); + saveConfig(); +} + +void QuickLauncher::positionChange(Position) +{ + for (int n=0; n<int(m_buttons->size()); ++n) + { + (*m_buttons)[n]->setPopupDirection(popupDirection()); + } +} + + +#include "quicklauncher.moc" diff --git a/kicker/applets/launcher/quicklauncher.desktop b/kicker/applets/launcher/quicklauncher.desktop new file mode 100644 index 000000000..0e80149aa --- /dev/null +++ b/kicker/applets/launcher/quicklauncher.desktop @@ -0,0 +1,140 @@ +[Desktop Entry] +Type=Plugin +Name=Quick Launcher +Name[af]=Vinnige Lanseerder +Name[ar]=الإنطلاق السريع +Name[az]=Sür'ətli Başladıcı +Name[be]=Хуткі запускальнік +Name[bg]=Бързо стартиране +Name[bn]=কুইক লঞ্চার +Name[br]=Loc'her prim +Name[bs]=Brzo pokretanje +Name[ca]=Engegador ràpid +Name[cs]=Rychlé spouštění aplikací +Name[csb]=Chùtczé zrëszenié +Name[cy]=Cychwynydd Cyflym +Name[da]=Hurtigstarter +Name[de]=Schnellstarter +Name[el]=Γρήγορη φόρτωση +Name[eo]=Rapidlanĉilo +Name[es]=Lanzador rápido +Name[et]=Kiirkäivitaja +Name[eu]=Abiarazle bizkorra +Name[fa]=راهانداز سریع +Name[fi]=Sovellusten pikakäynnistin +Name[fr]=Lanceur d'applications +Name[fy]=Snel útfierder +Name[ga]=Tosaitheoir Tapa +Name[gl]=Lanzador Rápido +Name[he]=הפעלה מהירה +Name[hi]=द्रुत लांचर +Name[hr]=Brzo pokretanje +Name[hu]=Gyorsindító +Name[id]=Launcher Cepat +Name[is]=Flýtiræsir +Name[it]=Esecuzione rapida +Name[ja]=クイックランチャー +Name[ka]=სწრაფი დაწყება +Name[kk]=Жедел жегуші +Name[km]=អ្នកចាប់ផ្ដើមរហ័ស +Name[lo]=ຮງກທຳງານດ່ວນ +Name[lt]=Greitasis paleidimas +Name[lv]=Ātrais Palaidējs +Name[mk]=Брз стартувач +Name[mn]=Түргэн ажилуулагч +Name[ms]=Pelancar Pantas +Name[mt]=Ħaddem Malajr +Name[nb]=Hurtigstarter +Name[nds]=Fixstarter +Name[ne]=द्रुत सुरुआत +Name[nl]=Snelstarter +Name[nn]=Snøggstartar +Name[nso]=Ngwadisoleswa ya Kapela +Name[oc]=Engegador rapid +Name[pa]=ਚੁਸਤ ਸ਼ੁਰੂਆਤੀ +Name[pl]=Szybkie uruchamianie +Name[pt]=Execução de Aplicações +Name[pt_BR]=Lançador rápido +Name[ro]=Executor rapid +Name[ru]=Быстрый запуск +Name[rw]=Mutangiza Yihuta +Name[se]=Jođánisálggaheaddji +Name[sk]=Rýchly spúšťač +Name[sl]=Hitri zaganjalnik +Name[sr]=Брзи покретач +Name[sr@Latn]=Brzi pokretač +Name[sv]=Snabbstartare +Name[ta]=உடனடியாக திரையில் தெரிதல் +Name[te]=త్వరగా మొదలుపెట్టెది +Name[tg]=Сар додани тез +Name[th]=เรียกทำงานด่วน +Name[tr]=Hızlı Başlatıcı +Name[tt]=Tiz Cibärgeç +Name[uk]=Швидкий запуск +Name[uz]=Tez ishga tushirgich +Name[uz@cyrillic]=Тез ишга туширгич +Name[ven]=Tavhanya +Name[vi]=Khởi động nhanh +Name[wa]=Enondeu al vole di programes +Name[zh_CN]=快速启动 +Name[zh_TW]=快速起動 +Name[zu]=Umqalisi osheshayo +Comment=Directly access your frequently used applications +Comment[af]=Kry direkte toegang tot die programme wat jy gereeld gebruik +Comment[ar]=للوصول المباشر إلى تطبيقاتك الأكثر إستعمالاً +Comment[be]=Наўпрост запускае праграму +Comment[bg]=Бърз достъп до често използваните програми +Comment[bn]=আপনার সবচেয়ে ঘনঘন ব্যবহৃত অ্যাপলিকেশনগুলি সরাসরি চালু করুন +Comment[bs]=Direktno pristupite vašim često korištenim programima +Comment[ca]=Accedeix directament a les aplicacions més usades +Comment[cs]=Přímý přístup k nejčastěji používaným aplikacím +Comment[csb]=Prosti przistãp do nôczãstczi brëkòwónëch programów +Comment[da]=Direkte adgang til programmer du ofte bruger +Comment[de]=Schneller Zugriff auf häufig verwendete Programme +Comment[el]=Απευθείας πρόσβαση στις συχνά χρησιμοποιούμενες εφαρμογές σας +Comment[eo]=Rekte atingi viajn preferatajn aplikaĵojn +Comment[es]=Acceso directo a las aplicaciones usadas más frecuentemente +Comment[et]=Ligipääs sagedamini kasutatud rakendustele +Comment[eu]=Sarbide zuzena zure ohiko aplikazioei +Comment[fa]=دستیابی مستقیم به کاربردهای مکرر استفادهشدۀ شما +Comment[fi]=Siirry suoraan useimmin käyttämiisi sovelluksiin +Comment[fr]=Accès direct aux applications les plus utilisées +Comment[fy]=Direkte tagong ta jo faak brûkte programma's +Comment[gl]=Aceda directamenta ás aplicacións que use mais amiudo +Comment[he]=גישה מהירה ליישומים שאתה משתמש בהם הכי הרבה +Comment[hr]=Izravni pristup najčešće upotrebljavanim aplikacijama +Comment[hu]=A gyakran használt alkalmazások közvetlen elérése +Comment[is]=Beinn aðgangur að mest notuðu forritunum þínum +Comment[it]=Accesso diretto alle applicazioni usate più frequentemente +Comment[ja]=よく用いるアプリケーションに直接アクセス +Comment[kk]=Жиі пайдаланатын қолданбаларды тез жегу +Comment[km]=ដំណើរការកម្មវិធីដែលបានប្រើជារឿយៗរបស់អ្នកដោយផ្ទាល់ +Comment[lt]=Tiesiogiai pasiekite dažniausiai naudojamas programas +Comment[mk]=Пристапете директно на вашите често користени апликации +Comment[nb]=Få direkte tilgang til ofte brukte programmer +Comment[nds]=Direktemang Dien meist bruukte Programmen opropen +Comment[ne]=बारम्बार प्रयोग भएका अनुप्रयोगमा तपाईँको प्रत्यक्ष पहुँच +Comment[nl]=Directe toegang tot uw veelgebruikte programma's +Comment[nn]=Direkte tilgang til program du brukar ofte +Comment[pa]=ਅਕਸਰ ਵਰਤੇ ਜਾਂਦੇ ਕਾਰਜਾਂ ਲਈ ਸਿੱਧੀ ਪਹੁੰਚ +Comment[pl]=Bezpośredni dostęp do najczęściej używanych programów +Comment[pt]=Aceder directamente às aplicações usadas com mais frequência por si +Comment[pt_BR]=Acesso direito à seus aplicativos mais freqüentemente usados +Comment[ro]=Accesează direct aplicațiile folosite frecvent +Comment[ru]=Быстрый вызов часто используемых приложений +Comment[sk]=Priamo zprístupní najčastejšie používané programy. +Comment[sl]=Neposreden dostop do vaših najbolj uporabljanih programov +Comment[sr]=Директно приступите својим често коришћеним програмима +Comment[sr@Latn]=Direktno pristupite svojim često korišćenim programima +Comment[sv]=Direkt åtkomst av program du ofta använder +Comment[th]=เรียกใช้งานแอพพลิเคชั่นที่คุณใช้บ่อยๆ ได้โดยตรง +Comment[tr]=Sıkça kullanılan programlara erişim sağlar +Comment[uk]=Безпосередній доступ до програм, які часто вживаються +Comment[uz]=Eng koʻp ishlatilgan dasturlarga qisqa yoʻl +Comment[uz@cyrillic]=Энг кўп ишлатилган дастурларга қисқа йўл +Comment[vi]=Chạy ngay các trình bạn thường xuyên dùng +Comment[wa]=Accès direk ås programes sovint eployîs +Comment[zh_CN]=直接访问您最经常使用的应用程序 +Comment[zh_TW]=直接存取您最常使用的應用程式 +Icon=launch +X-KDE-Library=launcher_panelapplet diff --git a/kicker/applets/launcher/quicklauncher.h b/kicker/applets/launcher/quicklauncher.h new file mode 100644 index 000000000..c82d39661 --- /dev/null +++ b/kicker/applets/launcher/quicklauncher.h @@ -0,0 +1,138 @@ +/***************************************************************** + +Copyright (c) 2000 Bill Nagel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __quicklauncher_h__ +#define __quicklauncher_h__ + +#include <dcopobject.h> +#include <qimage.h> +#include <qstring.h> +#include <qvaluevector.h> +#include <kpanelapplet.h> +#include <map> + +#include "flowgridmanager.h" +#include "prefs.h" +#include "quickbutton.h" + +class ConfigDlg; +class QPopupMenu; +class QuickButtonGroup; +class PopularityStatistics; +class KAction; + +typedef QuickButtonGroup ButtonGroup; + +class QuickLauncher: public KPanelApplet, public DCOPObject +{ + Q_OBJECT + K_DCOP + +k_dcop: + void serviceStartedByStorageId(QString starter, QString storageId); + +public: + enum {DEFAULT_ICON_DIM=QuickButton::DEFAULT_ICON_DIM}; + enum {SIZE_AUTO=0}; + + struct PopularityInfo { + float popularity; + }; + + QuickLauncher(const QString& configFile, Type t = Normal, int actions = 0, + QWidget *parent = 0, const char *name = 0); + ~QuickLauncher(); + int widthForHeight(int height) const; + int heightForWidth(int width) const; + void addApp(QString url, int index, bool manuallyAdded); + virtual void action(Action a); + +public slots: + void addApp(QString url, bool manuallyAdded); + void addAppBeforeManually(QString url, QString sender); + void removeAppManually(QuickButton *button); + void removeApp(QString url, bool manuallyRemoved); + void removeApp(int index, bool manuallyRemoved); + void removeAppManually(int index); + void saveConfig(); + void about(); + +protected: + int findApp(QString url); + int findApp(QuickButton *button); + + void mousePressEvent(QMouseEvent *e); + void resizeEvent(QResizeEvent*); + void dragEnterEvent(QDragEnterEvent *e); + void dragLeaveEvent(QDragLeaveEvent *e); + void dragMoveEvent(QDragMoveEvent *e); + void dropEvent(QDropEvent *e); + void refreshContents(); + void setRefreshEnabled(bool enable); + void setConserveSpace(bool conserve_space); + void setDragEnabled(bool conserve_space); + + bool conserveSpace() const { return m_manager->conserveSpace(); } + bool isDragEnabled() const { return m_settings->dragEnabled(); } + + void buildPopupMenu(); + void loadConfig(); + + void mergeButtons(int index); + void clearTempButtons(); + int dimension() const; + +protected slots: + void slotConfigure(); + void slotSettingsDialogChanged(); + void fillRemoveAppsMenu(); + void slotOwnServiceExecuted(QString serviceMenuId); + void slotAdjustToCurrentPopularity(); + void slotStickyToggled(); + +protected: + void updateInsertionPosToStatusQuo(); + void updateStickyHighlightLayer(); + QuickButton* createButton(QString url); + virtual void paintEvent(QPaintEvent* e); + virtual void positionChange(Position); + + QPopupMenu *m_popup; + QPopupMenu *m_appletPopup; + QPopupMenu *m_removeAppsMenu; + QuickButtonGroup *m_buttons, *m_newButtons, *m_oldButtons, *m_dragButtons; + int m_space, m_border; + QSize m_buttonSize; + FlowGridManager *m_manager; + int m_dropLen, m_dropPos, m_minPanelDim; + bool m_dragAccepted, m_refreshEnabled, m_needsSave, m_needsRefresh; + std::map<QString, int> m_appOrdering; + Prefs* m_settings; + KAction *m_configAction; + ConfigDlg *m_configDialog; + PopularityStatistics* m_popularity; + QImage m_stickyHighlightLayer; + QTimer *m_saveTimer; +}; + +#endif diff --git a/kicker/applets/lockout/Makefile.am b/kicker/applets/lockout/Makefile.am new file mode 100644 index 000000000..f9772adf5 --- /dev/null +++ b/kicker/applets/lockout/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir)/kicker/libkicker $(all_includes) +METASOURCES = AUTO + +kde_module_LTLIBRARIES = lockout_panelapplet.la + +lockout_panelapplet_la_SOURCES = lockout.cpp +lockout_panelapplet_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +lockout_panelapplet_la_LIBADD = ../../libkicker/libkickermain.la $(LIB_KSYCOCA) + +noinst_HEADERS = lockout.h + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = lockout.desktop + +EXTRA_DIST = $(lnk_DATA) + +messages: + $(XGETTEXT) *.cpp *.h -o $(podir)/lockout.pot diff --git a/kicker/applets/lockout/README b/kicker/applets/lockout/README new file mode 100644 index 000000000..e25954c3d --- /dev/null +++ b/kicker/applets/lockout/README @@ -0,0 +1,4 @@ +A very simple applet that shows two buttons, one for locking the desktop, +the other to invoke the logout process. + +Carsten Pfeiffer <pfeiffer@kde.org> diff --git a/kicker/applets/lockout/lockout.cpp b/kicker/applets/lockout/lockout.cpp new file mode 100644 index 000000000..d22e4a8ed --- /dev/null +++ b/kicker/applets/lockout/lockout.cpp @@ -0,0 +1,278 @@ +/***************************************************************** + +Copyright (c) 2001 Carsten Pfeiffer <pfeiffer@kde.org> + 2001 Matthias Elter <elter@kde.org> + 2001 Martijn Klingens <mklingens@yahoo.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + + +#include <qlayout.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qpopupmenu.h> +#include <qtoolbutton.h> +#include <qstyle.h> +#include <qtooltip.h> + +#include <dcopclient.h> + +#include <kapplication.h> +#include <kglobal.h> +#include <klocale.h> +#include <kiconloader.h> +#include <krun.h> +#include <kdebug.h> + +#include <stdlib.h> +#include "lockout.h" + +extern "C" +{ + KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile) + { + KGlobal::locale()->insertCatalogue("lockout"); + return new Lockout(configFile, parent, "lockout"); + } +} + +Lockout::Lockout( const QString& configFile, QWidget *parent, const char *name) + : KPanelApplet( configFile, KPanelApplet::Normal, 0, parent, name ), bTransparent( false ) +{ + KConfig *conf = config(); + conf->setGroup("lockout"); + + //setFrameStyle(Panel | Sunken); + setBackgroundOrigin( AncestorOrigin ); + + if ( orientation() == Horizontal ) + layout = new QBoxLayout( this, QBoxLayout::TopToBottom ); + else + layout = new QBoxLayout( this, QBoxLayout::LeftToRight ); + + layout->setAutoAdd( true ); + layout->setMargin( 0 ); + layout->setSpacing( 0 ); + + lockButton = new SimpleButton( this, "lock"); + logoutButton = new SimpleButton( this, "logout"); + + QToolTip::add( lockButton, i18n("Lock the session") ); + QToolTip::add( logoutButton, i18n("Log out") ); + + lockButton->setPixmap( SmallIcon( "lock" )); + logoutButton->setPixmap( SmallIcon( "exit" )); + + bTransparent = conf->readBoolEntry( "Transparent", bTransparent ); + + connect( lockButton, SIGNAL( clicked() ), SLOT( lock() )); + connect( logoutButton, SIGNAL( clicked() ), SLOT( logout() )); + + lockButton->installEventFilter( this ); + logoutButton->installEventFilter( this ); + + if (!kapp->authorize("lock_screen")) + lockButton->hide(); + + if (!kapp->authorize("logout")) + logoutButton->hide(); + + lockButton->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); + logoutButton->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); + + if ( !kapp->dcopClient()->isAttached() ) + kapp->dcopClient()->attach(); + + connect( kapp, SIGNAL( iconChanged(int) ), SLOT( slotIconChanged() )); +} + +Lockout::~Lockout() +{ + KGlobal::locale()->removeCatalogue("lockout"); +} + +// the -2 is due to kicker allowing us a width/height of 42 and the buttons +// having a size of 44. So we rather cut those 2 pixels instead of changing +// direction and wasting a lot of space. +void Lockout::checkLayout( int height ) const +{ + QSize s = minimumSizeHint(); + QBoxLayout::Direction direction = layout->direction(); + + if ( direction == QBoxLayout::LeftToRight && + ( ( orientation() == Vertical && s.width() - 2 >= height ) || + ( orientation() == Horizontal && s.width() - 2 < height ) ) ) { + layout->setDirection( QBoxLayout::TopToBottom ); + } + else if ( direction == QBoxLayout::TopToBottom && + ( ( orientation() == Vertical && s.height() - 2 < height ) || + ( orientation() == Horizontal && s.height() - 2 >= height ) ) ) { + layout->setDirection( QBoxLayout::LeftToRight ); + } +} + +int Lockout::widthForHeight( int height ) const +{ + checkLayout( height ); + return sizeHint().width(); +} + +int Lockout::heightForWidth( int width ) const +{ + checkLayout( width ); + return sizeHint().height(); +} + +void Lockout::lock() +{ + QCString appname( "kdesktop" ); + int kicker_screen_number = qt_xscreen(); + if ( kicker_screen_number ) + appname.sprintf("kdesktop-screen-%d", kicker_screen_number); + kapp->dcopClient()->send(appname, "KScreensaverIface", "lock()", ""); +} + +void Lockout::logout() +{ + kapp->requestShutDown(); +} + +void Lockout::mousePressEvent(QMouseEvent* e) +{ + propagateMouseEvent(e); +} + +void Lockout::mouseReleaseEvent(QMouseEvent* e) +{ + propagateMouseEvent(e); +} + +void Lockout::mouseDoubleClickEvent(QMouseEvent* e) +{ + propagateMouseEvent(e); +} + +void Lockout::mouseMoveEvent(QMouseEvent* e) +{ + propagateMouseEvent(e); +} + +void Lockout::propagateMouseEvent(QMouseEvent* e) +{ + if ( !isTopLevel() ) { + QMouseEvent me(e->type(), mapTo( topLevelWidget(), e->pos() ), + e->globalPos(), e->button(), e->state() ); + QApplication::sendEvent( topLevelWidget(), &me ); + } +} + +bool Lockout::eventFilter( QObject *o, QEvent *e ) +{ + if (!kapp->authorizeKAction("kicker_rmb")) + return false; // Process event normally: + + if( e->type() == QEvent::MouseButtonPress ) + { + KConfig *conf = config(); + conf->setGroup("lockout"); + + QMouseEvent *me = static_cast<QMouseEvent *>( e ); + if( me->button() == QMouseEvent::RightButton ) + { + if( o == lockButton ) + { + QPopupMenu *popup = new QPopupMenu(); + + popup->insertItem( SmallIcon( "lock" ), i18n("Lock Session"), + this, SLOT( lock() ) ); + popup->insertSeparator(); + + i18n("&Transparent"); + //popup->insertItem( i18n( "&Transparent" ), 100 ); + popup->insertItem( SmallIcon( "configure" ), + i18n( "&Configure Screen Saver..." ), + this, SLOT( slotLockPrefs() ) ); + + //popup->setItemChecked( 100, bTransparent ); + //popup->connectItem(100, this, SLOT( slotTransparent() ) ); + //if (conf->entryIsImmutable( "Transparent" )) + // popup->setItemEnabled( 100, false ); + popup->exec( me->globalPos() ); + delete popup; + + return true; + } + else if ( o == logoutButton ) + { + QPopupMenu *popup = new QPopupMenu(); + + popup->insertItem( SmallIcon( "exit" ), i18n("&Log Out..."), + this, SLOT( logout() ) ); + popup->insertSeparator(); + //popup->insertItem( i18n( "&Transparent" ), 100 ); + popup->insertItem( SmallIcon( "configure" ), + i18n( "&Configure Session Manager..." ), + this, SLOT( slotLogoutPrefs() ) ); + + //popup->setItemChecked( 100, bTransparent ); + //popup->connectItem(100, this, SLOT( slotTransparent() ) ); + //if (conf->entryIsImmutable( "Transparent" )) + // popup->setItemEnabled( 100, false ); + popup->exec( me->globalPos() ); + delete popup; + + return true; + } // if o + } // if me->button + } // if e->type + + // Process event normally: + return false; +} + +void Lockout::slotLockPrefs() +{ + // Run the screensaver settings + KRun::run( "kcmshell screensaver", KURL::List() ); +} + +void Lockout::slotTransparent() +{ + bTransparent = !bTransparent; + + KConfig* conf = config(); + conf->setGroup("lockout"); + conf->writeEntry( "Transparent", bTransparent ); + conf->sync(); +} + +void Lockout::slotLogoutPrefs() +{ + // Run the logout settings. + KRun::run( "kcmshell kcmsmserver", KURL::List() ); +} + +void Lockout::slotIconChanged() +{ + lockButton->setPixmap( SmallIcon( "lock" )); + logoutButton->setPixmap( SmallIcon( "exit" )); +} + +#include "lockout.moc" diff --git a/kicker/applets/lockout/lockout.desktop b/kicker/applets/lockout/lockout.desktop new file mode 100644 index 000000000..e8681cc88 --- /dev/null +++ b/kicker/applets/lockout/lockout.desktop @@ -0,0 +1,125 @@ +[Desktop Entry] +Type=Plugin +Name=Lock/Logout Buttons +Name[af]=Sluit/Teken af Knoppies +Name[ar]=أزرار الإقفال/الخروج +Name[be]=Кнопкі блакавання/выхаду +Name[bg]=Заключване и изход +Name[bn]=লক/লগ-আউট বাটন +Name[bs]=Dugmad za zaključavanje/odjavu +Name[ca]=Botons bloqueja/surt +Name[cs]=Tlačítka odhlášení/uzamčení +Name[csb]=Knąpë blokòwaniô ekranu/wëlogòwaniô +Name[da]=Lås/Logaf-knapper +Name[de]=Bildschirmsperre und Abmeldung aus KDE +Name[el]=Κουμπιά κλειδώματος/αποσύνδεσης +Name[eo]=Ŝloso- kaj adiaŭo-butonoj +Name[es]=Botones de bloqueo/salida +Name[et]=Lukustamise/väljalogimise nupud +Name[eu]=Blokeatu/Irteteko botoiak +Name[fa]=دکمههای قفل/خروج +Name[fi]=Lukitus/Uloskirjautumispainikkeet +Name[fr]=Boutons de verrouillage et déconnexion +Name[fy]=Beskoattelje/ôfmeldknoppen +Name[ga]=Cnaipí Glasála/Logála Amach +Name[gl]=Botóns de Bloqueo/Saída +Name[he]=כפתורי נעילה\יציאה +Name[hr]=Gumb za zaključavanje/odjavljivanje +Name[hu]=Zároló/kijelentkező gombok +Name[is]=Læsa/stimpla út hnappar +Name[it]=Pulsanti di uscita e bloccaggio schermo +Name[ja]=ロック/ログアウトボタン +Name[ka]=დაბლოკვის/გამოსვლის ღილაკები +Name[kk]=Шығу не экранды бұғаттау батырмалары +Name[km]=ប៊ូតុង ចាក់សោ/ចេញ +Name[ko]=잠금/로그아웃 +Name[lt]=Užrakinimo/išsiregistravimo mygtukai +Name[mk]=Копчиња „Заклучи/Одјави се“ +Name[nb]=Panelprogram for skjermlås/utlogging +Name[nds]=Knööp för dat Afsluten oder Afmellen +Name[ne]=ताल्चा लगाउने/लगआउट गर्ने बटन +Name[nl]=Vergrendel/afmeldknoppen +Name[nn]=Knappar for skjermlås/utlogging +Name[pa]=ਤਾਲਾ/ਬਾਹਰੀ ਦਰ ਬਟਨ +Name[pl]=Przyciski blokowania ekranu/wylogowania +Name[pt]=Botões de Bloqueio/Saída +Name[pt_BR]=Botões de Travar/Sair +Name[ro]=Butoane de blocare/ieșire +Name[ru]=Кнопки выхода и запирания экрана +Name[se]=Lohkadan/olggosčálihan boalut +Name[sk]=Tlačidlá na ohlásenie/zamknutie +Name[sl]=Gumba za zaklep in odjavo +Name[sr]=Дугмад за закључавање/одјављивање +Name[sr@Latn]=Dugmad za zaključavanje/odjavljivanje +Name[sv]=Lås/Logga ut-knappar +Name[te]=తాళం వెయి/లాగౌట్ బటన్లు +Name[tg]=Тугмаҳои Қулф/Баромадан +Name[th]=ปุ่มล็อค/ล็อกเอาท์ +Name[tr]=Kilitle/Çık Düğmeleri +Name[uk]=Кнопки Замикання/Виходу з системи +Name[uz]=Qulflash/chiqish tugmalari +Name[uz@cyrillic]=Қулфлаш/чиқиш тугмалари +Name[vi]=Tiểu ứng dụng Khoá/Đăng xuất +Name[wa]=Boton po serer a clé ou dislodjî +Name[zh_CN]=锁定/注销按钮 +Name[zh_TW]=「螢幕鎖定/登出」按鈕 +Comment=Adds buttons for locking screen and session logout +Comment[af]=Voeg skerm sluit en afteken knoppies by +Comment[ar]=يضيف أزرار لإقفال الشاشة و الخروج من الجلسة +Comment[be]=Дадае кнопкі для блакіроўкі экрана і заканчэння сесіі +Comment[bg]=Добавяне на бутоните за заключване на екрана и изход +Comment[bn]=স্ক্রীণ লক এবং লগ-আউট করার জন্য বাটন যোগ করে +Comment[bs]=Dodaje na panel dugmad za zaključavanje ekrana i odjavu sa sistema +Comment[ca]=Afegeix botons per bloquejar la pantalla i sortir de la sessió +Comment[cs]=Přidá tlačítka pro uzamčení obrazovky a odhlášení z relace +Comment[csb]=Dodôwô knapë blokòwaniô ekranu ë wëlogòwaniô +Comment[da]=Tilføjer knapper for at låse skærmen og logge ud fra sessionen +Comment[de]=Fügt Knöpfe zur Bildschirmsperre und Abmeldung aus KDE hinzu +Comment[el]=Προσθέτει κουμπιά για το κλείδωμα της οθόνης και την αποσύνδεση συνεδρίας +Comment[eo]=Aldonu butonojn por ŝlosi ekranon kaj seanco-eliron +Comment[es]=Añade botones para bloquear la sesión y para salirse de esta +Comment[et]=Lisab nupud ekraani lukustamiseks ning seansi lõpetamiseks +Comment[eu]=Pantaila blokeatu eta saiotik irteteko botoiak gehitzen ditu +Comment[fa]=دکمهها را برای قفل پرده و خروج نشست اضافه میکند +Comment[fi]=Lisää painikkeet ruudun lukitsemiseen ja uloskirjautumiseen +Comment[fr]=Ajoute des boutons permettant de verrouiller l'écran et de déconnecter la session en cours +Comment[fy]=Heakket knoppen ta foar it beskoattelje fan it skerm en it sluten fan de sesje +Comment[gl]=Engade botóns para bloquear a pantalla e sair da sesión +Comment[he]=מוסיף כפתורים לנעילת המסך ויציאה מהמערכת +Comment[hr]=Dodavanje gumba za zaključavanje zaslona i odjavljivanja sesije +Comment[hu]=Nyomógombok a képernyő zárolásához és kijelentkezéshez +Comment[is]=Bætir við hnöppum til að læsa skjánum og stimpla sig út +Comment[it]=Aggiunge i pulsanti per bloccare lo schermo o uscire dalla sessione +Comment[ja]=スクリーンロックとセッションログアウト用ボタンを追加 +Comment[kk]=Экранды бұғаттау және сеанстан шығу батырмаларды қосу +Comment[km]=បន្ថែមប៊ូតុងសម្រាប់ចាក់សោអេក្រង់ និងចេញពីសម័យ +Comment[ko]=세션을 잠그거나 끝내기 +Comment[lt]=Prideda mygtukus ekrano užrakinimui ir sesijos užbaigimui +Comment[mk]=Додава копчиња за заклучување на екранот и одјавување од сесијата +Comment[nb]=Legger til knapper for å låse skjermen og logge ut av økta. +Comment[nds]=Föögt Knööp för dat Afsluten vun den Schirm oder dat Afmellen ut KDE to +Comment[ne]=पर्दा ताल्चा लगाउन र सत्र लग आउट गर्नका लागि बटनहरू थप्छ +Comment[nl]=Voegt knoppen toe voor het vergrendelen van het scherm en het afsluiten van de sessie +Comment[nn]=Legg til knappar for å låsa skjermen og logga ut av økta. +Comment[pa]=ਪਰਦੇ ਨੂੰ ਤਾਲਾਬੰਦ ਕਰਨ ਅਤੇ ਅਜਲਾਸ ਬੰਦ ਕਰਨ ਲਈ ਬਟਨ ਜੋੜਦਾ ਹੈ +Comment[pl]=Dodaje przyciski zablokowania ekranu i wylogowania +Comment[pt]=Adiciona botões para bloquear o ecrã e encerrar a sessão +Comment[pt_BR]=Adiciona os botões para bloquear a tela e finalizar a sessão +Comment[ro]=Adaugă butoane pentru blocarea ecranului și sesiunea de ieșire +Comment[ru]=Добавление кнопок выхода из KDE и запирания экрана +Comment[se]=Lasit boaluid mat sáhttet lohkadit šearpma ja heaittihit bargovuoru +Comment[sk]=Pridá tlačidlá na zamknutie obrazovky a ukončenie relácie +Comment[sl]=Doda gumba za zaklep zaslona in odjavo iz seje +Comment[sr]=Додаје дугмад за закључавање екрана и одјављивање из сесије +Comment[sr@Latn]=Dodaje dugmad za zaključavanje ekrana i odjavljivanje iz sesije +Comment[sv]=Lägger till knappar för att låsa skärmen och logga ut från sessionen +Comment[th]=เพิ่มปุ่มสำหรับล็อคหน้าจอและล็อกเอาท์ออกจากวาระที่กำลังใช้งานอยู่ +Comment[uk]=Додає кнопки для замикання екрана та виходу з сеансу +Comment[uz]=Ekranni qulflash va seansdan chiqish uchun tugmalar +Comment[uz@cyrillic]=Экранни қулфлаш ва сеансдан чиқиш учун тугмалар +Comment[vi]=Thêm nút khoá màn hình và đăng xuất khỏi phiên làm việc +Comment[wa]=Radjout des botons po serer l' waitroûle a clé et dislodjî del session +Comment[zh_CN]=添加锁定屏幕和注销会话的按钮 +Comment[zh_TW]=加入用來鎖定螢幕與登出作業階段的按鈕 +Icon=exit +X-KDE-Library=lockout_panelapplet diff --git a/kicker/applets/lockout/lockout.h b/kicker/applets/lockout/lockout.h new file mode 100644 index 000000000..606870a99 --- /dev/null +++ b/kicker/applets/lockout/lockout.h @@ -0,0 +1,52 @@ +#ifndef LOCKOUT_H +#define LOCKOUT_H + +#include <qevent.h> +#include <qstring.h> +#include <kpanelapplet.h> + +#include "simplebutton.h" + +class QBoxLayout; +class QToolButton; + +class Lockout : public KPanelApplet +{ + Q_OBJECT + +public: + Lockout( const QString& configFile, + QWidget *parent = 0, const char *name = 0 ); + ~Lockout(); + + int widthForHeight(int height) const; + int heightForWidth(int width) const; + +protected: + virtual void mousePressEvent( QMouseEvent * ); + virtual void mouseMoveEvent( QMouseEvent * ); + virtual void mouseReleaseEvent( QMouseEvent * ); + virtual void mouseDoubleClickEvent( QMouseEvent * ); + + virtual bool eventFilter( QObject *, QEvent * ); + +private slots: + void lock(); + void logout(); + + void slotLockPrefs(); + void slotLogoutPrefs(); + void slotTransparent(); + void slotIconChanged(); + +private: + void propagateMouseEvent( QMouseEvent * ); + void checkLayout( int height ) const; + + SimpleButton *lockButton, *logoutButton; + QBoxLayout *layout; + + bool bTransparent; +}; + +#endif // LOCKOUT_H diff --git a/kicker/applets/media/Makefile.am b/kicker/applets/media/Makefile.am new file mode 100644 index 000000000..0b971aef2 --- /dev/null +++ b/kicker/applets/media/Makefile.am @@ -0,0 +1,20 @@ +INCLUDES = -I$(top_srcdir)/libkonq -I$(top_srcdir)/kicker/libkicker $(all_includes) + +kde_module_LTLIBRARIES = media_panelapplet.la +media_panelapplet_la_SOURCES = preferencesdialog.cpp mediumbutton.cpp mediaapplet.cpp + +METASOURCES = AUTO + +noinst_HEADERS = mediaapplet.h mediumbutton.h preferencesdialog.h + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = mediaapplet.desktop + +EXTRA_DIST = $(lnk_DATA) + +media_panelapplet_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +media_panelapplet_la_LIBADD = ../../../libkonq/libkonq.la ../../libkicker/libkickermain.la $(LIB_KDEUI) $(LIB_KIO) $(LIB_KUTILS) + +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/mediaapplet.pot + diff --git a/kicker/applets/media/mediaapplet.cpp b/kicker/applets/media/mediaapplet.cpp new file mode 100644 index 000000000..4ccd0eeef --- /dev/null +++ b/kicker/applets/media/mediaapplet.cpp @@ -0,0 +1,444 @@ +/* This file is part of the KDE project + Copyright (c) 2004 Kevin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kglobal.h> +#include <klocale.h> +#include <kconfig.h> +#include <kapplication.h> +#include <kaboutdata.h> +#include <kaboutapplication.h> +#include <kdebug.h> +#include <kpopupmenu.h> +#include <kiconloader.h> + +#include "mediaapplet.h" + +#include "preferencesdialog.h" + +extern "C" +{ + KDE_EXPORT KPanelApplet* init( QWidget *parent, const QString configFile) + { + KGlobal::locale()->insertCatalogue("mediaapplet"); + return new MediaApplet(configFile, KPanelApplet::Normal, + KPanelApplet::About | KPanelApplet::Preferences, + parent, "mediaapplet"); + } +} + +MediaApplet::MediaApplet(const QString& configFile, Type type, int actions, QWidget *parent, const char *name) + : KPanelApplet(configFile, type, actions, parent, name), + mButtonSizeSum(0) +{ + if (!parent) + setBackgroundMode(X11ParentRelative); + + setBackgroundOrigin(AncestorOrigin); + + setAcceptDrops(true); + + loadConfig(); + + mpDirLister = new KDirLister(); + + connect( mpDirLister, SIGNAL( clear() ), + this, SLOT( slotClear() ) ); + connect( mpDirLister, SIGNAL( started(const KURL&) ), + this, SLOT( slotStarted(const KURL&) ) ); + connect( mpDirLister, SIGNAL( completed() ), + this, SLOT( slotCompleted() ) ); + connect( mpDirLister, SIGNAL( newItems( const KFileItemList & ) ), + this, SLOT( slotNewItems( const KFileItemList & ) ) ); + connect( mpDirLister, SIGNAL( deleteItem( KFileItem * ) ), + this, SLOT( slotDeleteItem( KFileItem * ) ) ); + connect( mpDirLister, SIGNAL( refreshItems( const KFileItemList & ) ), + this, SLOT( slotRefreshItems( const KFileItemList & ) ) ); + + reloadList(); +} + +MediaApplet::~MediaApplet() +{ + delete mpDirLister; + + while (!mButtonList.isEmpty()) + { + MediumButton *b = mButtonList.first(); + mButtonList.remove(b); + delete b; + } + + KGlobal::locale()->removeCatalogue("mediaapplet"); +} + +void MediaApplet::about() +{ + KAboutData data("mediaapplet", + I18N_NOOP("Media Applet"), + "1.0", + I18N_NOOP("\"media:/\" ioslave frontend applet"), + KAboutData::License_GPL_V2, + "(c) 2004, Kevin Ottens"); + + data.addAuthor("Kevin \'ervin\' Ottens", + I18N_NOOP("Maintainer"), + "ervin ipsquad net", + "http://ervin.ipsquad.net"); + + data.addCredit("Joseph Wenninger", + I18N_NOOP("Good mentor, patient and helpful. Thanks for all!"), + "jowenn@kde.org"); + + KAboutApplication dialog(&data); + dialog.exec(); +} + +void MediaApplet::preferences() +{ + PreferencesDialog dialog(mMedia); + + dialog.setExcludedMediumTypes(mExcludedTypesList); + dialog.setExcludedMedia(mExcludedList); + + if(dialog.exec()) + { + mExcludedTypesList = dialog.excludedMediumTypes(); + mExcludedList = dialog.excludedMedia(); + saveConfig(); + reloadList(); + } +} + +int MediaApplet::widthForHeight( int /*height*/ ) const +{ + return mButtonSizeSum; +} + +int MediaApplet::heightForWidth( int /*width*/ ) const +{ + return mButtonSizeSum; +} + +void MediaApplet::resizeEvent( QResizeEvent *) +{ + arrangeButtons(); +} + +void MediaApplet::arrangeButtons() +{ + int button_size = 1; + int x_offset = 0; + int y_offset = 0; + + // Determine upper bound for the button size + MediumButtonList::iterator it; + MediumButtonList::iterator end = mButtonList.end(); + for ( it = mButtonList.begin(); it != end; ++it ) + { + MediumButton *button = *it; + + button_size = std::max(button_size, + orientation() == Vertical ? + button->heightForWidth(width()) : + button->widthForHeight(height()) ); + // button->widthForHeight(height()) : + // button->heightForWidth(width()) ); + } + + int kicker_size; + if (orientation() == Vertical) + { + kicker_size = width(); + } + else + { + kicker_size = height(); + } + + unsigned int max_packed_buttons = kicker_size / button_size; + // Center icons if we only have one column/row + if (mButtonList.count() < max_packed_buttons) + { + max_packed_buttons = QMAX(uint(1), mButtonList.count()); + } + max_packed_buttons = QMAX(uint(1), max_packed_buttons); + + int padded_button_size = kicker_size / max_packed_buttons; + mButtonSizeSum = 0; + unsigned int pack_count = 0; + + // Arrange the buttons. If kicker is more than twice as high/wide + // as the maximum preferred size of an icon, we put several icons + // in one column/row + for ( it = mButtonList.begin(); it != end; ++it ) + { + MediumButton *button = *it; + + button->move(x_offset, y_offset); + button->setPanelPosition(position()); + + if(pack_count == 0) + { + mButtonSizeSum += button_size; + } + + ++pack_count; + + if(orientation() == Vertical) + { + if (pack_count < max_packed_buttons) + { + x_offset += padded_button_size; + } + else + { + x_offset = 0; + y_offset += button_size; + pack_count = 0; + } + + button->resize(padded_button_size, button_size); + } + else + { + if (pack_count < max_packed_buttons) + { + y_offset += padded_button_size; + } + else + { + y_offset = 0; + x_offset += button_size; + pack_count = 0; + } + + button->resize(button_size, padded_button_size); + } + + button->unsetPalette(); + button->setBackgroundOrigin(AncestorOrigin); + } + + updateGeometry(); + emit updateLayout(); +} + +void MediaApplet::slotClear() +{ + kdDebug()<<"MediaApplet::slotClear"<<endl; + + while (!mButtonList.isEmpty()) + { + MediumButton *b = mButtonList.first(); + mButtonList.remove(b); + delete b; + } + + arrangeButtons(); +} + +void MediaApplet::slotStarted(const KURL &/*url*/) +{ +} + +void MediaApplet::slotCompleted() +{ + mMedia = mpDirLister->items(KDirLister::AllItems); +} + +void MediaApplet::slotNewItems(const KFileItemList &entries) +{ + kdDebug()<<"MediaApplet::slotNewItems"<<endl; + + for(KFileItemListIterator it(entries); it.current(); ++it) + { + kdDebug() << "item: " << it.current()->url() << endl; + + bool found = false; + MediumButtonList::iterator it2; + MediumButtonList::iterator end = mButtonList.end(); + for ( it2 = mButtonList.begin(); it2 != end; ++it2 ) + { + MediumButton *button = *it2; + + if(button->fileItem().url()==it.current()->url()) + { + found = true; + button->setFileItem(*it.current()); + break; + } + } + + if(!found && !mExcludedList.contains(it.current()->url().url()) ) + { + MediumButton *button = new MediumButton(this, *it.current()); + button->show(); + mButtonList.append(button); + } + } + + arrangeButtons(); +} + +void MediaApplet::slotDeleteItem(KFileItem *fileItem) +{ + kdDebug()<<"MediumApplet::slotDeleteItem:"<< fileItem->url() << endl; + + MediumButtonList::iterator it; + MediumButtonList::iterator end = mButtonList.end(); + for ( it = mButtonList.begin(); it != end; ++it ) + { + MediumButton *button = *it; + + if(button->fileItem().url()==fileItem->url()) + { + mButtonList.remove(button); + delete button; + break; + } + } + slotCompleted(); + arrangeButtons(); +} + +void MediaApplet::slotRefreshItems(const KFileItemList &entries) +{ + for(KFileItemListIterator it(entries); it.current(); ++it) + { + kdDebug()<<"MediaApplet::slotRefreshItems:"<<(*it.current()).url().url()<<endl; + + QString mimetype = (*it.current()).mimetype(); + bool found = false; + + kdDebug()<<"mimetype="<<mimetype<<endl; + + MediumButtonList::iterator it2; + MediumButtonList::iterator end = mButtonList.end(); + for ( it2 = mButtonList.begin(); it2 != end; ++it2 ) + { + MediumButton *button = *it2; + + if(button->fileItem().url()==(*it.current()).url()) + { + if(mExcludedTypesList.contains(mimetype)) + { + mButtonList.remove(button); + delete button; + } + else + { + button->setFileItem(*it.current()); + } + found = true; + break; + } + } + + if(!found && !mExcludedTypesList.contains(mimetype) && !mExcludedList.contains(it.current()->url().url()) ) + { + MediumButton *button = new MediumButton(this, *it.current()); + button->show(); + mButtonList.append(button); + } + } + + arrangeButtons(); +} + +void MediaApplet::positionChange(Position) +{ + arrangeButtons(); +} + +void MediaApplet::loadConfig() +{ + KConfig *c = config(); + c->setGroup("General"); + + if(c->hasKey("ExcludedTypes")) + { + mExcludedTypesList = c->readListEntry("ExcludedTypes",';'); + } + else + { + mExcludedTypesList.clear(); + mExcludedTypesList << "media/hdd_mounted"; + mExcludedTypesList << "media/hdd_unmounted"; + mExcludedTypesList << "media/nfs_mounted"; + mExcludedTypesList << "media/nfs_unmounted"; + mExcludedTypesList << "media/smb_mounted"; + mExcludedTypesList << "media/smb_unmounted"; + } + + if(c->hasKey("ExcludedMedia")) + { + mExcludedList = c->readListEntry("ExcludedMedia",';'); + } + else + { + mExcludedList.clear(); + } +} + +void MediaApplet::saveConfig() +{ + KConfig *c = config(); + c->setGroup("General"); + + c->writeEntry("ExcludedTypes", mExcludedTypesList, ';'); + c->writeEntry("ExcludedMedia", mExcludedList, ';'); + + c->sync(); +} + +void MediaApplet::reloadList() +{ + mpDirLister->stop(); + + while (!mButtonList.isEmpty()) + { + MediumButton *b = mButtonList.first(); + mButtonList.remove(b); + delete b; + } + + mpDirLister->clearMimeFilter(); + mpDirLister->setMimeExcludeFilter(mExcludedTypesList); + mpDirLister->openURL(KURL("media:/")); +} + +void MediaApplet::mousePressEvent(QMouseEvent *e) +{ + if(e->button()==RightButton) + { + KPopupMenu menu(this); + + menu.insertTitle(i18n("Media")); + menu.insertItem(SmallIcon("configure"), i18n("&Configure..."), 1); + + int choice = menu.exec(this->mapToGlobal(e->pos())); + + if(choice==1) + { + preferences(); + } + } +} + +#include "mediaapplet.moc" diff --git a/kicker/applets/media/mediaapplet.desktop b/kicker/applets/media/mediaapplet.desktop new file mode 100644 index 000000000..958ebb753 --- /dev/null +++ b/kicker/applets/media/mediaapplet.desktop @@ -0,0 +1,130 @@ +[Desktop Entry] +Type=Plugin +Comment=Directly access your storage media +Comment[af]=Kry direkte toegang tot jou stoor media +Comment[ar]=الوصول مباشرة إلى وسائطك للتخزين +Comment[be]=Наўпрост дае доступ да носьбітаў інфармацыі +Comment[bg]=Директен достъп до съхраняващите устройства +Comment[bn]=আপনার স্টোরেজ মিডিয়া সরাসরি খুলুন +Comment[bs]=Direktno pristupite vašim uređajima +Comment[ca]=Accedeix directament als suports d'emmagatzematge +Comment[cs]=Přímý přístup k úložným zařízením +Comment[csb]=Prosti przistãp do Twòjëch zôpisownëch mediów +Comment[da]=Direkte adgang til opbevaringsmedie +Comment[de]=Direkter Zugriff auf Ihre Speichermedien +Comment[el]=Απευθείας πρόσβαση στις συσκευές αποθήκευσής σας +Comment[eo]=Rekte atingi vian konservejon +Comment[es]= Acceso directo a sus dispositivos de almacenamiento +Comment[et]=Ligipääs andmekandjatele +Comment[eu]=Atzitu zure biltegiratze-euskarriak +Comment[fa]=دستیابی مستقیم رسانۀ ذخیرهگاه شما +Comment[fi]=Tallennuslaitteet näyttävä paneelisovelma +Comment[fr]=Accès direct aux périphériques de stockage +Comment[fy]=Direkte tagong ta jo opslachmedia +Comment[gl]=Unha applet que mostra os seus dispositivos +Comment[he]=גישה ישירה אל ההתקנים שלך +Comment[hr]=Izravni pristup medijima za pohranjivanje +Comment[hu]=A tárolóeszközök közvetlen elérése +Comment[is]=Beinn aðgangur að geymslumiðlum +Comment[it]=Accesso diretto ai dispositivi di archiviazione +Comment[ja]=記憶メディアに直接アクセス +Comment[ka]=თქვენი შენახვის მედიის პირდაპირი წვდომა +Comment[kk]=Жинақтаушыларыңызды тез ашу +Comment[km]=ចូលដំណើរការឧបករណ៍ផ្ទុករបស់អ្នកដោយផ្ទាល់ +Comment[lt]=Tiesiogiai pasiekite savo saugojimo įrenginius +Comment[mk]=Пристапете директно на вашите медиуми за податоци +Comment[nb]=Direkte tilgang til lagringsenheter +Comment[nds]=Direktemang op Dien Spiekermedien togriepen +Comment[ne]=भण्डारण मिडियामा तपाईँको प्रत्यक्ष पहुँच +Comment[nl]=Directe toegang tot uw opslagmedia +Comment[nn]=Direkte tilgang til lagringseiningar +Comment[pa]=ਜੋ ਕਿ ਤੁਹਾਡਾ ਸਟੋਰੇਜ਼ ਮਾਧਿਅਮ ਵੇਖਾਉਦਾ ਹੈ। +Comment[pl]=Bezpośredni dostęp do Twoich urządzeń przechowywania danych +Comment[pt]=Aceder directamente aos seus suportes de armazenamento +Comment[pt_BR]=Acesso direto às suas mídias de armazenamento +Comment[ro]=Accesează direct dispozitivele de stocare +Comment[ru]=Аплет панели, показывающий устройства хранения +Comment[se]=Njuolggoluotta vurkenmediaide +Comment[sk]=Priamy prístup na zálohovacie médiá +Comment[sl]=Neposreden dostop do nosilcev za shranjevanje +Comment[sr]=Директно приступите складишним медијима +Comment[sr@Latn]=Direktno pristupite skladišnim medijima +Comment[sv]=Direkt åtkomst av lagringsmedia +Comment[th]=เข้าใช้งานสื่อที่เก็บข้อมูลของคุณโดยตรง +Comment[tr]=Depolama aygıtlarına doğrudan erişim +Comment[uk]=Безпосередній доступ до пристроїв зберігання інформації +Comment[uz]=Saqlash usunalariga qisqa yoʻl +Comment[uz@cyrillic]=Сақлаш усуналарига қисқа йўл +Comment[vi]=Truy cập ngay vào các ổ chứa dữ liệu của bạn +Comment[wa]=Accès direk a vos sopoirts di wårdaedje +Comment[zh_CN]=直接访问您的存储介质 +Comment[zh_TW]=直接存取您的儲存媒體 +Name=Storage Media +Name[af]=Stoor Media +Name[ar]=وسائط التخزين +Name[be]=Носьбіты +Name[bg]=Съхраняващи устройства +Name[bn]=স্টোরেজ মিডিয়া +Name[bs]=Uređaji za smještaj podataka +Name[ca]=Suports d'emmagatzematge +Name[cs]=Úložná zařízení +Name[csb]=Zôpisowné media +Name[da]=Opbevaringsmedie +Name[de]=Speichermedien +Name[el]=Συσκευές αποθήκευσης +Name[eo]=Enmemoriga Medio +Name[es]=Dispositivos de almacenamiento +Name[et]=Andmekandjad +Name[eu]=Biltegiratze-euskarria +Name[fa]=رسانۀ ذخیرهگاه +Name[fi]=Tallennusmedia +Name[fr]=Support de stockage +Name[fy]=Opslachapparaten +Name[ga]=Meán Stórais +Name[gl]=Medios de armacenaxe +Name[he]=התקנים +Name[hi]=भंडार मीडिया +Name[hr]=Mediji za pohranjivanje +Name[hu]=Tárolóeszközök +Name[is]=Geymslumiðlar +Name[it]=Dispositivi di archiviazione +Name[ja]=記憶メディア +Name[ka]=მონაცემთა შენახვის მოწყობილობები +Name[kk]=Жинақтаушы құрылғылар +Name[km]=ឧបករណ៍ផ្ទុក +Name[lt]=Saugojimo įrenginiai +Name[lv]=Datu nesējs +Name[mk]=Медиуми за податоци +Name[ms]=Media Storan +Name[nb]=Lagringsenheter +Name[nds]=Spiekermedien +Name[ne]=भण्डारण मिडिया +Name[nl]=Opslagapparaten +Name[nn]=Lagringsmedium +Name[pa]=ਸਟੋਰੇਜ਼ ਮੀਡਿਆ +Name[pl]=Urządzenia przechowywania danych +Name[pt]=Dispositivos de Armazenamento +Name[pt_BR]=Mídia de Armazenamento +Name[ro]=Mediu de stocare +Name[ru]=Устройства хранения данных +Name[rw]=Uburyo bwo Kubika +Name[se]=Vurkenmedia +Name[sk]=Zálohovacie médiá +Name[sl]=Nosilci za shranjevanje +Name[sr]=Складишни медијуми +Name[sr@Latn]=Skladišni medijumi +Name[sv]=Lagringsmedia +Name[ta]=சேகரிப்பு ஊடகம் +Name[tg]=Захирагоҳи маълумот +Name[th]=สื่อเก็บข้อมูล +Name[tr]=Depolama Ortamı +Name[tt]=Saqlawlı Media +Name[uk]=Пристрої зберігання інформації +Name[uz]=Saqlash uskunalari +Name[uz@cyrillic]=Сақлаш ускуналари +Name[vi]=Ổ chứa Dữ liệu +Name[wa]=Sopoirts di wårdaedje +Name[zh_CN]=存储介质 +Name[zh_TW]=儲存媒體 +Icon=3floppy_unmount +X-KDE-Library=media_panelapplet diff --git a/kicker/applets/media/mediaapplet.h b/kicker/applets/media/mediaapplet.h new file mode 100644 index 000000000..5e2320be0 --- /dev/null +++ b/kicker/applets/media/mediaapplet.h @@ -0,0 +1,79 @@ +/* This file is part of the KDE project + Copyright (c) 2004 Kevin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef MEDIAAPPLET_H +#define MEDIAAPPLET_H + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <kpanelapplet.h> +#include <qstring.h> +#include <kconfig.h> +#include <kurl.h> +#include <kfileitem.h> +#include <kdirlister.h> + +#include <qptrlist.h> +#include "mediumbutton.h" +typedef QValueList<MediumButton*> MediumButtonList; + + +class MediaApplet : public KPanelApplet +{ +Q_OBJECT + +public: + MediaApplet(const QString& configFile, Type t = Normal, int actions = 0, + QWidget *parent = 0, const char *name = 0); + ~MediaApplet(); + + int widthForHeight(int height) const; + int heightForWidth(int width) const; + void about(); + void preferences(); + +protected: + void arrangeButtons(); + void resizeEvent(QResizeEvent *e); + void positionChange(Position p); + void reloadList(); + void loadConfig(); + void saveConfig(); + void mousePressEvent(QMouseEvent *e); + +protected slots: + void slotClear(); + void slotStarted(const KURL &url); + void slotCompleted(); + void slotNewItems(const KFileItemList &entries); + void slotDeleteItem(KFileItem *fileItem); + void slotRefreshItems(const KFileItemList &entries); + +private: + KDirLister *mpDirLister; + MediumButtonList mButtonList; + QStringList mExcludedTypesList; + QStringList mExcludedList; + KFileItemList mMedia; + int mButtonSizeSum; +}; + +#endif diff --git a/kicker/applets/media/mediumbutton.cpp b/kicker/applets/media/mediumbutton.cpp new file mode 100644 index 000000000..2c96601ea --- /dev/null +++ b/kicker/applets/media/mediumbutton.cpp @@ -0,0 +1,202 @@ +/* This file is part of the KDE project + Copyright (c) 2004 Kevin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "mediumbutton.h" + +#include <qapplication.h> +#include <qclipboard.h> +#include <qpainter.h> +#include <qdrawutil.h> +#include <qpopupmenu.h> +#include <qstyle.h> +#include <qtooltip.h> + +#include <kmessagebox.h> +#include <kmimetype.h> +#include <klocale.h> +#include <kdesktopfile.h> +#include <krun.h> +#include <kglobalsettings.h> +#include <kcursor.h> +#include <kapplication.h> +#include <kipc.h> +#include <kiconloader.h> +#include <kurldrag.h> +#include <kpopupmenu.h> + +#include <konq_operations.h> +#include <konq_popupmenu.h> +#include <konq_drag.h> + +MediumButton::MediumButton(QWidget *parent, const KFileItem &fileItem) + : PanelPopupButton(parent), mActions(this, this), mFileItem(fileItem) +{ + KAction *a = KStdAction::paste(this, SLOT(slotPaste()), + &mActions, "pasteto"); + a->setShortcut(0); + a = KStdAction::copy(this, SLOT(slotCopy()), &mActions, "copy"); + a->setShortcut(0); + + setBackgroundOrigin(AncestorOrigin); + + resize(20, 20); + + setAcceptDrops(mFileItem.isWritable()); + + setTitle(mFileItem.text()); + + refreshType(); + + connect(&mOpenTimer, SIGNAL(timeout()), SLOT(slotDragOpen())); + + // Activate this code only if we find a way to have both an + // action and a popup menu for the same kicker button + //connect(this, SIGNAL(clicked()), this, SLOT(slotClicked())); + + setPopup(new QPopupMenu()); +} + +MediumButton::~MediumButton() +{ + QPopupMenu *menu = popup(); + setPopup(0); + delete menu; +} + +const KFileItem &MediumButton::fileItem() const +{ + return mFileItem; +} + +void MediumButton::setFileItem(const KFileItem &fileItem) +{ + mFileItem.assign(fileItem); + setAcceptDrops(mFileItem.isWritable()); + setTitle(mFileItem.text()); + refreshType(); +} + +void MediumButton::initPopup() +{ + QPopupMenu *old_popup = popup(); + + KFileItemList items; + items.append(&mFileItem); + + KonqPopupMenu::KonqPopupFlags kpf = + KonqPopupMenu::ShowProperties + | KonqPopupMenu::ShowNewWindow; + + KParts::BrowserExtension::PopupFlags bef = + KParts::BrowserExtension::DefaultPopupItems; + + KonqPopupMenu *new_popup = new KonqPopupMenu(0L, items, + KURL("media:/"), mActions, 0L, + this, kpf, bef); + KPopupTitle *title = new KPopupTitle(new_popup); + title->setTitle(mFileItem.text()); + + new_popup->insertItem(title, -1, 0); + + setPopup(new_popup); + + if (old_popup!=0L) delete old_popup; +} + +void MediumButton::refreshType() +{ + KMimeType::Ptr mime = mFileItem.determineMimeType(); + QToolTip::add(this, mime->comment()); + setIcon(mime->icon(QString::null, false)); +} + +// Activate this code only if we find a way to have both an +// action and a popup menu for the same kicker button +/* +void MediumButton::slotClicked() +{ + mFileItem.run(); +} +*/ + +void MediumButton::slotPaste() +{ + KonqOperations::doPaste(this, mFileItem.url()); +} + +void MediumButton::slotCopy() +{ + KonqDrag * obj = KonqDrag::newDrag(mFileItem.url(), false); + + QApplication::clipboard()->setData( obj ); +} + +void MediumButton::dragEnterEvent(QDragEnterEvent* e) +{ + if (mFileItem.isWritable()) + { + mOpenTimer.start(1000, true); + e->accept(true); + } +} + +void MediumButton::dragLeaveEvent(QDragLeaveEvent* e) +{ + mOpenTimer.stop(); + + PanelPopupButton::dragLeaveEvent( e ); +} + +void MediumButton::dropEvent(QDropEvent *e) +{ + mOpenTimer.stop(); + + KonqOperations::doDrop(&mFileItem, mFileItem.url(), e, this); +} + +void MediumButton::slotDragOpen() +{ + mFileItem.run(); +} + +QString MediumButton::tileName() +{ + return mFileItem.text(); +} + +void MediumButton::setPanelPosition(KPanelApplet::Position position) +{ + switch(position) + { + case KPanelApplet::pBottom: + setPopupDirection(KPanelApplet::Up); + break; + case KPanelApplet::pTop: + setPopupDirection(KPanelApplet::Down); + break; + case KPanelApplet::pRight: + setPopupDirection(KPanelApplet::Left); + break; + case KPanelApplet::pLeft: + setPopupDirection(KPanelApplet::Right); + break; + } +} + +#include "mediumbutton.moc" diff --git a/kicker/applets/media/mediumbutton.h b/kicker/applets/media/mediumbutton.h new file mode 100644 index 000000000..8d7970d89 --- /dev/null +++ b/kicker/applets/media/mediumbutton.h @@ -0,0 +1,66 @@ +/* This file is part of the KDE project + Copyright (c) 2004 Kevin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef MEDIUMBUTTON_H +#define MEDIUMBUTTON_H + +#include <panelbutton.h> +#include <qpoint.h> +#include <qstring.h> +#include <qpixmap.h> +#include <qcursor.h> +#include <qtimer.h> +#include <kfileitem.h> +#include <kpanelapplet.h> +#include <kactioncollection.h> + +class MediumButton : public PanelPopupButton +{ +Q_OBJECT + +public: + MediumButton(QWidget *parent, const KFileItem &fileItem); + ~MediumButton(); + const KFileItem &fileItem() const; + void setFileItem(const KFileItem &fileItem); + void setPanelPosition(KPanelApplet::Position position); + +protected: + QString tileName(); + void refreshType(); + void initPopup(); + void dragEnterEvent( QDragEnterEvent* ); + void dragLeaveEvent( QDragLeaveEvent* ); + void dropEvent(QDropEvent *e); + +protected slots: + // Activate this code only if we find a way to have both an + // action and a popup menu for the same kicker button + //void slotClicked(); + void slotPaste(); + void slotCopy(); + void slotDragOpen(); + +private: + KActionCollection mActions; + KFileItem mFileItem; + QTimer mOpenTimer; +}; + +#endif diff --git a/kicker/applets/media/preferencesdialog.cpp b/kicker/applets/media/preferencesdialog.cpp new file mode 100644 index 000000000..179878e89 --- /dev/null +++ b/kicker/applets/media/preferencesdialog.cpp @@ -0,0 +1,167 @@ +/* This file is part of the KDE project + Copyright (c) 2004 Kevin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "preferencesdialog.h" + +#include <klocale.h> +#include <kmimetype.h> +#include <qvbox.h> +#include <qwhatsthis.h> +#include <klistview.h> +#include <kfiledetailview.h> + +class MediumTypeItem : public QCheckListItem +{ +public: + MediumTypeItem(QListView *parent, const QString name, + const QString mimetype) + : QCheckListItem(parent, name, CheckBox), + mMimeType(mimetype) { } + + const QString &mimeType() const { return mMimeType; } + +private: + QString mMimeType; +}; + +class MediumItem : public QCheckListItem +{ +public: + MediumItem(QListView *parent, const QString name, + const KFileItem medium) + : QCheckListItem(parent, name, CheckBox), + mMedium(medium) { } + + const QString itemURL() const { return mMedium.url().url(); } + +private: + KFileItem mMedium; +}; + + + +PreferencesDialog::PreferencesDialog(KFileItemList media, QWidget *parent, + const char *name) + : KDialogBase(Tabbed, i18n("Media Applet Preferences"), Ok|Cancel|Default, + Ok, parent, name, true), + mMedia(media) +{ + QVBox *types_page = addVBoxPage( i18n("Medium Types") ); + mpMediumTypesListView = new KListView(types_page); + + //mpMediumTypesListView->setFullWidth(true); + mpMediumTypesListView->addColumn( i18n("Types to Display") ); + QWhatsThis::add(mpMediumTypesListView, i18n("Deselect the medium types which you do not want to see in the applet")); + + + + QVBox *media_page = addVBoxPage( i18n("Media") ); + mpMediaListView = new KListView(media_page); + + //mpMediaListView->setFullWidth(true); + mpMediaListView->addColumn( i18n("Media to Display") ); + QWhatsThis::add(mpMediaListView, i18n("Deselect the media which you do not want to see in the applet")); + + slotDefault(); +} + +PreferencesDialog::~PreferencesDialog() +{ +} + +void PreferencesDialog::slotDefault() +{ + QStringList defaultExclude; + + defaultExclude << "media/hdd_mounted"; + defaultExclude << "media/hdd_unmounted"; + defaultExclude << "media/nfs_mounted"; + defaultExclude << "media/nfs_unmounted"; + defaultExclude << "media/smb_mounted"; + defaultExclude << "media/smb_unmounted"; + + setExcludedMediumTypes(defaultExclude); + setExcludedMedia(QStringList()); +} + +QStringList PreferencesDialog::excludedMediumTypes() +{ + QStringList excludedTypes; + + for(MediumTypeItem *it=static_cast<MediumTypeItem *>(mpMediumTypesListView->firstChild()); + it; it=static_cast<MediumTypeItem *>(it->nextSibling())) + { + if(!it->isOn()) excludedTypes << it->mimeType(); + } + + return excludedTypes; +} + +void PreferencesDialog::setExcludedMediumTypes(QStringList excludedTypesList) +{ + mpMediumTypesListView->clear(); + mpMediumTypesListView->setRootIsDecorated(false); + KMimeType::List mimetypes = KMimeType::allMimeTypes(); + + QValueListIterator<KMimeType::Ptr> it(mimetypes.begin()); + + for(; it != mimetypes.end(); ++it) + { + if ((*it)->name().startsWith("media/")) + { + bool ok=excludedTypesList.contains((*it)->name())==0; + MediumTypeItem *item = new MediumTypeItem(mpMediumTypesListView, (*it)->comment(), (*it)->name()); + item->setOn(ok); + } + } +} + +QStringList PreferencesDialog::excludedMedia() +{ + QStringList excluded; + + for(MediumItem *it=static_cast<MediumItem *>(mpMediaListView->firstChild()); + it; it=static_cast<MediumItem *>(it->nextSibling())) + { + if(!it->isOn()) excluded << it->itemURL(); + } + + return excluded; +} + +void PreferencesDialog::setExcludedMedia(QStringList excludedList) +{ + mpMediaListView->clear(); + mpMediaListView->setRootIsDecorated(false); + + KFileItemListIterator it( mMedia ); + KFileItem *file; + while ( (file = it.current()) != 0 ) + { + ++it; + + bool ok = excludedList.contains(file->url().url())==0; + MediumItem *item = new MediumItem(mpMediaListView, + file->text(), *file); + item->setOn(ok); + } +} + + +#include "preferencesdialog.moc" diff --git a/kicker/applets/media/preferencesdialog.h b/kicker/applets/media/preferencesdialog.h new file mode 100644 index 000000000..bb400564b --- /dev/null +++ b/kicker/applets/media/preferencesdialog.h @@ -0,0 +1,53 @@ +/* This file is part of the KDE project + Copyright (c) 2004 Kevin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef PREFERENCESDIALOG_H +#define PREFERENCESDIALOG_H + +#include <qwidget.h> +#include <kdialogbase.h> +#include <kfileitem.h> + +/** + *@author ervin + */ + +class PreferencesDialog : public KDialogBase +{ + Q_OBJECT +public: + PreferencesDialog(KFileItemList media, QWidget *parent=0, const char *name=0); + ~PreferencesDialog(); + + QStringList excludedMediumTypes(); + void setExcludedMediumTypes(QStringList excludedTypesList); + + QStringList excludedMedia(); + void setExcludedMedia(QStringList excludedList); + +protected slots: + void slotDefault(); + +private: + KListView *mpMediumTypesListView; + KListView *mpMediaListView; + KFileItemList mMedia; +}; + +#endif diff --git a/kicker/applets/menu/Makefile.am b/kicker/applets/menu/Makefile.am new file mode 100644 index 000000000..5bfaeee18 --- /dev/null +++ b/kicker/applets/menu/Makefile.am @@ -0,0 +1,20 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = menu_panelapplet.la + +menu_panelapplet_la_SOURCES = menuapplet.cpp menuapplet.skel + +noinst_HEADERS = menuapplet.h + +menu_panelapplet_la_METASOURCES = AUTO + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = menuapplet.desktop + +EXTRA_DIST = $(lnk_DATA) + +menu_panelapplet_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +menu_panelapplet_la_LIBADD = $(LIB_KDEUI) + +messages: + $(XGETTEXT) *.cpp *.h -o $(podir)/kmenuapplet.pot diff --git a/kicker/applets/menu/menuapplet.cpp b/kicker/applets/menu/menuapplet.cpp new file mode 100644 index 000000000..ae10614c6 --- /dev/null +++ b/kicker/applets/menu/menuapplet.cpp @@ -0,0 +1,511 @@ +/***************************************************************** + +Copyright (c) 2002 Siegfried Nijssen <snijssen@liacs.nl> +Copyright (c) 2003 Lubos Lunak <l.lunak@suse.cz> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#define _MENUAPPLET_CPP_ + +#include "menuapplet.h" + +#include <qlayout.h> +#include <qtooltip.h> +#include <qvariant.h> // avoid X11 #define's + +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> +#include <kwin.h> +#include <kwinmodule.h> + +#include <netwm.h> + +#include <X11/Xlib.h> + +/* + + KMenuBar from KDE3.1 and older won't work very well with this applet. + This is because QMenuBar tries really hard to keep its preffered size, + se even if the X window for the menubar has the size enforced by this + applet, Qt thinks it has the size Qt wants. This results in parts + of the menubar not being repainted. Also, old KMenuBar always forced + with to be the width of the screen, so even if the menubar has only + few entries, this applet will still indicate the menubar doesn't + fit completely in it. There's no way to fix this, besides upgrading + to KDE3.2. + +*/ + + +extern Time qt_x_time; + +extern "C" +{ + KDE_EXPORT KPanelApplet* init( QWidget* parent_P, const QString& configFile_P ) + { + KGlobal::locale()->insertCatalogue("kmenuapplet"); + return new KickerMenuApplet::Applet( configFile_P, parent_P ); + } +} + +namespace KickerMenuApplet +{ + +static const int MOVE_DIFF = 100; // size increment for left/right menu moving +static const int GROW_WIDTH = 10; // width of grow buttons + +const long SUPPORTED_WINDOW_TYPES = NET::NormalMask | NET::DesktopMask | NET::DockMask + | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask + | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask; + +Applet::Applet( const QString& configFile_P, QWidget* parent_P ) + : KPanelApplet( configFile_P, Normal, 0, parent_P, "menuapplet" ), + DCOPObject( "menuapplet" ), + module( NULL ), + active_menu( NULL ), + selection( NULL ), + selection_watcher( NULL ), + desktop_menu( false ), + topEdgeOffset( 0 ) + { + setBackgroundOrigin(AncestorOrigin); + dcopclient.registerAs( "menuapplet", false ); + // toolbarAppearanceChanged(int) is sent when changing macstyle + connect( kapp, SIGNAL( toolbarAppearanceChanged( int )), + this, SLOT( readSettings())); + claimSelection(); + readSettings(); + updateTopEdgeOffset(); + } + +Applet::~Applet() + { + lostSelection(); // release all menu's before really loosing the selection + delete selection; + delete selection_watcher; + delete module; + KGlobal::locale()->removeCatalogue("kmenuapplet"); + } + +void Applet::windowAdded( WId w_P ) + { + NETWinInfo info( qt_xdisplay(), w_P, qt_xrootwin(), NET::WMWindowType ); + if( info.windowType( SUPPORTED_WINDOW_TYPES ) != NET::TopMenu ) + return; +// kdDebug() << "embedding:" << w_P << endl; + Window transient_for = KWin::transientFor( w_P ); + if( transient_for == None ) + return; + MenuEmbed* embed; + if( transient_for == qt_xrootwin()) + { + embed = new MenuEmbed( transient_for, true, this ); + } + else + { + KWin::WindowInfo info2 = KWin::windowInfo( transient_for, NET::WMWindowType ); + embed = new MenuEmbed( transient_for, + info2.windowType( SUPPORTED_WINDOW_TYPES ) == NET::Desktop, this ); + } + embed->hide(); + embed->move( 0, -topEdgeOffset ); + embed->resize( embed->width(), height() + topEdgeOffset ); + embed->embed( w_P ); + if( embed->embeddedWinId() == None ) + { + delete embed; + return; + } + menus.append( embed ); + // in case the app mapped its menu after its mainwindow, check which menu should be shown + activeWindowChanged( module->activeWindow()); + } + +// - if the active window has its topmenu -> show the menu +// - if desktop menu is enabled (i.e. explicitly in kdesktop) : +// - show it +// - otherwise show nothing +void Applet::activeWindowChanged( WId w_P ) + { +// kdDebug() << "active:" << w_P << endl; + for( WId window = w_P; + window != None; + window = tryTransientFor( window )) + { + for( QValueList< MenuEmbed* >::ConstIterator it = menus.begin(); + it != menus.end(); + ++it ) + { + if( window == (*it)->mainWindow()) + { + activateMenu( *it ); + return; + } + } + } +// kdDebug() << "no active" << endl; + // No menu for active window found - if desktop menu + // (in kdesktoprc) is enabled, use kdesktop's menu instead of none. + bool try_desktop = desktop_menu; + if( !try_desktop && w_P != None ) + { // also use the desktop menu if the active window is desktop + KWin::WindowInfo info = KWin::windowInfo( w_P, NET::WMWindowType ); + if( info.windowType( SUPPORTED_WINDOW_TYPES ) == NET::Desktop ) + try_desktop = true; + } + if( try_desktop ) + { + for( QValueList< MenuEmbed* >::ConstIterator it = menus.begin(); + it != menus.end(); + ++it ) + { + if( (*it)->isDesktopMenu()) + { + activateMenu( *it ); + return; + } + } + } + activateMenu( NULL ); + } + +void Applet::activateMenu( MenuEmbed* embed_P ) + { + if( embed_P != active_menu ) + { +// kdDebug() << "activate:" << embed_P << endl; + if( active_menu != NULL ) + active_menu->hide(); + active_menu = embed_P; + if( active_menu != NULL ) + { + active_menu->show(); + //if (embed->isDesktopMenu()) + { + active_menu->setMinimumSize( width(), height() + topEdgeOffset ); + } + } + emit updateLayout(); + } + + setBackground(); + } + +void Applet::updateMenuGeometry( MenuEmbed* embed ) + { + if( embed == active_menu ) + emit updateLayout(); + } + +// If there's no menu for the window, try finding menu for its mainwindow +// (where the window's WM_TRANSIENT_FOR property points). +// If the window is modal (_NET_WM_STATE_MODAL), stop. +WId Applet::tryTransientFor( WId w_P ) + { + KWin::WindowInfo info = KWin::windowInfo( w_P, NET::WMState ); + if( info.state() & NET::Modal ) + return None; + WId ret = KWin::transientFor( w_P ); + if( ret == qt_xrootwin()) + ret = None; + return ret; + } + +void Applet::menuLost( MenuEmbed* embed ) + { + for( QValueList< MenuEmbed* >::Iterator it = menus.begin(); + it != menus.end(); + ++it ) + { + if( *it == embed ) + { + menus.remove( it ); + embed->deleteLater(); +// kdDebug() << "deleting:" << (*it)->mainWindow() << endl; + if( embed == active_menu ) + { + active_menu = NULL; + // trigger selecting new active menu + activeWindowChanged( module->activeWindow()); + } + return; + } + } + } + +void Applet::positionChange( Position ) + { + updateTopEdgeOffset(); + } + +// Detect mouse movement at the top screen edge also if the menubar +// has a popup open - in such case, Qt has a grab, and this avoids +// Kicker's FittsLawFrame. Therefore move the menubar a bit higher, +// so that it actually is positioned exactly at the screen edge +// (i.e. at a negative y coordinate within this applet, due to +// Kicker's frame). +void Applet::updateTopEdgeOffset() + { + QPoint p = topLevelWidget()->mapToGlobal( QPoint( 0, 0 )); + if( p.y() <= 2 ) // 2 = work also when running in appletproxy + topEdgeOffset = mapToGlobal( QPoint( 0, 0 )).y() - p.y(); + else + topEdgeOffset = 0; + if( active_menu != NULL ) + active_menu->move( active_menu->x(), -topEdgeOffset ); + } + +void Applet::paletteChange(const QPalette & /* oldPalette */) +{ + setBackground(); +} + +void Applet::moveEvent( QMoveEvent* ) +{ + setBackground(); +} + +void Applet::setBackground() +{ + if (active_menu) + active_menu->setBackground(); +} + +void Applet::claimSelection() + { + assert( selection == NULL ); + selection = new KSelectionOwner( makeSelectionAtom(), DefaultScreen( qt_xdisplay())); +// force taking the selection, but don't kill previous owner + if( selection->claim( true, false )) + { + delete selection_watcher; + selection_watcher = NULL; + connect( selection, SIGNAL( lostOwnership()), SLOT( lostSelection())); + module = new KWinModule; + connect( module, SIGNAL( windowAdded( WId )), this, SLOT( windowAdded( WId ))); + connect( module, SIGNAL( activeWindowChanged( WId )), + this, SLOT( activeWindowChanged( WId ))); + QValueList< WId > windows = module->windows(); + for( QValueList< WId >::ConstIterator it = windows.begin(); + it != windows.end(); + ++it ) + windowAdded( *it ); + activeWindowChanged( module->activeWindow()); + } + else + lostSelection(); + } + +void Applet::lostSelection() + { + if( selection == NULL ) + return; +// kdDebug() << "lost selection" << endl; + for( QValueList< MenuEmbed* >::ConstIterator it = menus.begin(); + it != menus.end(); + ++it ) + delete (*it); // delete all MenuEmbed's = release all menus + menus.clear(); + active_menu = NULL; + if( selection_watcher == NULL ) + { + selection_watcher = new KSelectionWatcher( makeSelectionAtom(), DefaultScreen( qt_xdisplay())); + connect( selection_watcher, SIGNAL( lostOwner()), this, SLOT( claimSelection())); + } + delete module; + module = NULL; + selection->deleteLater(); + selection = NULL; + // selection_watcher stays + } + +void Applet::readSettings() + { + KConfig cfg( "kdesktoprc", true ); + cfg.setGroup( "Menubar" ); + desktop_menu = cfg.readBoolEntry( "ShowMenubar", false ); + cfg.setGroup( "KDE" ); + if( cfg.readBoolEntry( "macStyle", false ) || desktop_menu ) + QToolTip::remove( this ); + else + QToolTip::add( this, i18n( + "You do not appear to have enabled the standalone menubar; " + "enable it in the Behavior control module for desktop." )); + if( !isDisabled() && active_menu == NULL ) + activeWindowChanged( module->activeWindow()); //enforce desktop_menu + } + +void Applet::configure() + { + readSettings(); + } + +int Applet::widthForHeight( int ) const + { + if (active_menu) + return active_menu->width(); + return 0; // we're stretch applet + } + +int Applet::heightForWidth( int ) const + { + // *shrug* running this applet in vertical mode is a bad idea anyway + return 50; + } + +static Atom selection_atom = None; +static Atom msg_type_atom = None; + +static +void initAtoms() + { + char nm[ 100 ]; + sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( qt_xdisplay())); + char nm2[] = "_KDE_TOPMENU_MINSIZE"; + char* names[ 2 ] = { nm, nm2 }; + Atom atoms[ 2 ]; + XInternAtoms( qt_xdisplay(), names, 2, False, atoms ); + selection_atom = atoms[ 0 ]; + msg_type_atom = atoms[ 1 ]; + } + +Atom Applet::makeSelectionAtom() + { + if( selection_atom == None ) + initAtoms(); + return selection_atom; + } + +MenuEmbed::MenuEmbed( WId mainwindow_P, bool desktop_P, + QWidget* parent_P, const char* name_P ) + : QXEmbed( parent_P, name_P ), + main_window( mainwindow_P ), + desktop( desktop_P ) + { + setAutoDelete( false ); + } + +void MenuEmbed::windowChanged( WId w_P ) + { + if( w_P == None ) + static_cast< Applet* >( parent())->menuLost( this ); + } + +bool MenuEmbed::x11Event( XEvent* ev_P ) + { + if( ev_P->type == ConfigureRequest + && ev_P->xconfigurerequest.window == embeddedWinId() + && ev_P->xconfigurerequest.value_mask & ( CWWidth | CWHeight )) + { + XConfigureRequestEvent& ev = ev_P->xconfigurerequest; + QSize new_size = size(); + if( ev.value_mask & CWWidth ) + new_size.setWidth( ev.width ); + if( ev.value_mask & CWHeight ) + new_size.setHeight( ev.height ); + // resize when the embedded window resizes (still obey min size) +// kdDebug() << "RES:" << embeddedWinId() << ":" << ev.width << ":" << ev.height << endl; + if( ev.width != width() || ev.height != height()) + { + resize( ev.width, ev.height ); + static_cast< Applet* >( parent())->updateMenuGeometry( this ); + } + sendSyntheticConfigureNotifyEvent(); +// int x, y; +// unsigned int w, h, d, b; +// Window root; +// XGetGeometry( qt_xdisplay(), embeddedWinId(), &root, &x, &y, &w, &h, &b, &d ); +// kdDebug() << "RES3:" << width() << ":" << height() << ":" << w << ":" << h << endl; + return true; + } + return QXEmbed::x11Event( ev_P ); + } + +void MenuEmbed::sendSyntheticConfigureNotifyEvent() +{ + QPoint globalPos = mapToGlobal(QPoint(0,0)); + if (embeddedWinId()) { + XConfigureEvent c; + memset(&c, 0, sizeof(c)); + c.type = ConfigureNotify; + c.display = qt_xdisplay(); + c.send_event = True; + c.event = embeddedWinId(); + c.window = winId(); + c.x = globalPos.x(); + c.y = globalPos.y(); + c.width = width(); + c.height = height(); + c.border_width = 0; + c.above = None; + c.override_redirect = 0; + XSendEvent(qt_xdisplay(), c.event, true, StructureNotifyMask, (XEvent*)&c); + } +} + +void MenuEmbed::setMinimumSize( int w, int h ) +{ + QXEmbed::setMinimumSize( w, h ); + // tell the menubar also the allowed minimum size + // the applet won't allow resizing to smaller size + if( embeddedWinId() != None ) + { +// kdDebug() << "RES2:" << width() << ":" << height() << ":" << minimumWidth() << ":" << minimumHeight() << endl; + XEvent ev; + ev.xclient.display = qt_xdisplay(); + ev.xclient.type = ClientMessage; + ev.xclient.window = embeddedWinId(); + assert( msg_type_atom != None ); + ev.xclient.message_type = msg_type_atom; + ev.xclient.format = 32; + ev.xclient.data.l[0] = qt_x_time; + ev.xclient.data.l[1] = minimumWidth(); + ev.xclient.data.l[2] = minimumHeight(); + ev.xclient.data.l[3] = 0; + ev.xclient.data.l[4] = 0; + XSendEvent( qt_xdisplay(), embeddedWinId(), False, NoEventMask, &ev ); + } +} + +void MenuEmbed::setBackground() +{ + const QPixmap *pbg = parentWidget()->backgroundPixmap(); + + if (pbg) + { + QPixmap bg(width(), height()); + bg.fill(parentWidget(), pos()); + setPaletteBackgroundPixmap(bg); + setBackgroundOrigin(WidgetOrigin); + } + else + unsetPalette(); + + hide(); + show(); + //XClearArea(x11Display(), embeddedWinId(), 0, 0, 0, 0, True); +} + +} // namespace + +#include "menuapplet.moc" diff --git a/kicker/applets/menu/menuapplet.desktop b/kicker/applets/menu/menuapplet.desktop new file mode 100644 index 000000000..741c2ae29 --- /dev/null +++ b/kicker/applets/menu/menuapplet.desktop @@ -0,0 +1,128 @@ +[Desktop Entry] +Hidden=true +Type=Plugin +Name=Menu +Name[af]=Kieslys +Name[ar]=قائمة +Name[be]=Меню +Name[bg]=Меню +Name[bn]=মেনু +Name[br]=Meuziad +Name[bs]=Meni +Name[ca]=Menú +Name[cs]=Nabídka +Name[cy]=Dewislen +Name[de]=Menü +Name[el]=Μενού +Name[eo]=Menuo +Name[es]=Menú +Name[et]=Menüü +Name[eu]=Menua +Name[fa]=گزینگان +Name[fi]=Valikko +Name[ga]=Roghchlár +Name[he]=תפריט +Name[hi]=मेन्यू +Name[hr]=Izbornik +Name[hu]=Menü +Name[is]=Valmynd +Name[ja]=メニュー +Name[ka]=მენიუ +Name[kk]=Мәзір +Name[km]=ម៉ឺនុយ +Name[lt]=Meniu +Name[lv]=Izvēlne +Name[mk]=Мени +Name[mn]=Цэс +Name[nb]=Meny +Name[nds]=Menü +Name[ne]=मेनु +Name[nn]=Meny +Name[pa]=ਮੇਨੂ +Name[ro]=Meniu +Name[ru]=Меню +Name[rw]=Ibikubiyemo +Name[sl]=Meni +Name[sr]=Мени +Name[sr@Latn]=Meni +Name[sv]=Meny +Name[ta]=பட்டி +Name[te]=పట్టి +Name[tg]=Меню +Name[th]=เมนู +Name[tr]=Menü +Name[tt]=Saylaq +Name[uk]=Меню +Name[uz]=Menyu +Name[uz@cyrillic]=Меню +Name[vi]=Thực đơn +Name[wa]=Dressêye +Name[zh_CN]=菜单 +Name[zh_TW]=選單 +Comment=Applet embedding standalone menubars +Comment[ar]=بريمج يضمّن أشرطة قوائم منفردة بذاتها +Comment[be]=Аплет з убудаваным меню +Comment[bg]=Системен панел за вграждане на самостоятелни менюта +Comment[bn]=আলাদা মেনুবার ধারণ করার উপযোগী অ্যাপলেট +Comment[bs]=Applet koji uvezuje samostalne menije +Comment[ca]=Un applet encastat estàndard per a les barres de menú +Comment[cs]=Applet pohlcující samostatné lišty s nabídkami +Comment[csb]=Aplet zbiérający ùwòlnioné lëstwë menu +Comment[cy]=Rhaglennig sy'n mewn-adeiladu bariau dewislen unigol +Comment[da]=Applet der indlejrer alenestående menulinjer +Comment[de]=Programm zur Einbettung einzelner Menüleisten +Comment[el]=Μικροεφαρμογή που ενσωματώνει αυτόνομες γραμμές μενού +Comment[eo]=Aplikaĵeniganta solfunkcia menuzono +Comment[es]=Una miniaplicación que incluye barras de menú autónomas +Comment[et]=Aplett, mis põimib endasse isseisvaid menüüribasid +Comment[eu]=Menu-barrak txertaturik dituen appleta +Comment[fa]=میله گزینگان خوداتکای نهفتۀ برنامک +Comment[fi]=Sovelma, joka pystyy upottamaan yksittäisiä valikkorivejä +Comment[fr]=Une applet intégrant les barres de menu externes +Comment[fy]=Een applet die lossteande menubalken ynslút +Comment[gl]=Barras de menu estándar con incrustamento de applets +Comment[he]=שורת תפריטים משובצת יישומונים העומדת בפני עצמה +Comment[hi]=ऐपलेट एम्बेडिंग स्टैंडअलोन मेन्यूबार्स +Comment[hr]=Samostalne trake izbornika s ugrađenim apletima +Comment[hu]=Kisalkalmazásokat tartalmazni képes önálló menüsorok +Comment[is]=Smáforrit sem byggir inn sjálfstæðar valmyndastikur +Comment[it]=Applet per inglobare le barre dei menu +Comment[ja]=単独のメニューバーに組み込むアプレット +Comment[ka]=აპლეტი, რომელიც ამატებს ავტონომიურ პანელებს +Comment[kk]=Бөлек мәзір панельдерді құру апплеті +Comment[km]=របារម៉ឺនុយនៅតែឯងដែលបង្កប់ក្នុងអាប់ភ្លេត +Comment[lt]=Įskiepis, rodantis atskiras meniu dalis +Comment[lv]=Sīklietotne, kas iegulda pastāvīgas izvēlnes +Comment[mk]=Аплет кој ги вгнездува самостојните менија +Comment[ms]=Menu bar tersendiri pembenaman aplet +Comment[mt]=Applet li fiha menubars indipendenti +Comment[nb]=Et panelprogram som bygger inn frittstående menylinjer +Comment[nds]=Lüttprogramm för't Inbetten vun enkelte Warktüüchbalkens +Comment[ne]=एप्लेट सम्मिलित गर्ने स्ट्यान्डअलोन मेनुपट्टी +Comment[nl]=Een applet die losstaande menubalken insluit +Comment[nn]=Eit panelprogram som kan innehalda frittståande menylinjer +Comment[pa]=ਇੱਕਲੀ ਮੇਨੂਪੱਟੀ ਐਪਲਿਟ +Comment[pl]=Aplet zbierający uwolnione paski menu +Comment[pt]=Uma 'applet' que incorpora barras de menu autónomas +Comment[pt_BR]=Mini-aplicativo para embutir barras de menu ao painel +Comment[ro]=O miniaplicație ce înglobează bare de meniu +Comment[ru]=Аплет, встраивающий автономные панели меню +Comment[rw]=Apuleti zifitemo umwanya-ibikubiyemo wigenga +Comment[se]=Prográmmaš mii vuojuha oktanas fálloholggaid +Comment[sk]=Applet pre samostatné pruhy menu +Comment[sl]=Vstavek, ki vključuje samostojne menijske vrstice +Comment[sr]=Аплет за уграђивање самосталних менија +Comment[sr@Latn]=Aplet za ugrađivanje samostalnih menija +Comment[sv]=Miniprogram för inbäddning av fristående menyer +Comment[ta]=குறுநிரல் பொதிந்த தனியான மெனுபட்டிகள் +Comment[th]=แอพเพล็ตสำหรับฝังแถบเมนูลงพาเนล +Comment[tr]=Yalnız menü çubuklarını gömen uygulamacık +Comment[tt]=Ayırım torğan saylaq-tirälär berläşterüçe applet +Comment[uk]=Аплет вбудовних окремих смужок меню +Comment[vi]=Tiểu ứng dụng nhúng các thanh thực đơn đứng riêng +Comment[wa]=Aplikete po fé ravaler des bårs di dressêye totes seules +Comment[zh_CN]=嵌入独立菜单栏的小程序 +Comment[zh_TW]=單獨置於面板中的選單列 + +X-KDE-Library=menu_panelapplet +X-KDE-UniqueApplet=true diff --git a/kicker/applets/menu/menuapplet.h b/kicker/applets/menu/menuapplet.h new file mode 100644 index 000000000..8374e897b --- /dev/null +++ b/kicker/applets/menu/menuapplet.h @@ -0,0 +1,258 @@ +/***************************************************************** + +Copyright (c) 2002 Siegfried Nijssen <snijssen@liacs.nl> +Copyright (c) 2003 Lubos Lunak <l.lunak@suse.cz> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef _MENUAPPLET_H_ +#define _MENUAPPLET_H_ + +#include <assert.h> + +#include <qvaluelist.h> +#include <qevent.h> +#include <qxembed.h> + +#include <kpanelapplet.h> +#include <kmanagerselection.h> + +#include <dcopobject.h> +#include <dcopclient.h> + +#include <karrowbutton.h> + +class KWinModule; + +namespace KickerMenuApplet +{ + +class MenuEmbed; + +/** + * @short A central menu bar + * + * @description All status change, such as when an window is activated, + * a new window popped up, etc, is received via @ref KWin::WindowInfo and @ref + * NETWinInfo. Status changes for X selections are done via KSelectionWatcher. + * + * How it works in broad terms: KickerMenuApplet gets notified as soon a window + * changes(a new pops up etc.) and accordingly updates the list @ref menus, + * which contains all known menus. When a new window gains focus, it looks up the + * correct MenuEmbed in @ref menus, and then switches to that one. + * + * The documentation is a bit rusty -- read it with a critical eye. + * + * @author Siegfried Nijssen <snijssen@liacs.nl> + * @author Lubos Lunak <l.lunak@suse.cz> + */ + +class Applet : public KPanelApplet, public DCOPObject +{ + Q_OBJECT + K_DCOP + +k_dcop: + + /** + * Called by the Kicker configuration(KCM). Does in turn call + * readSettings(). + */ + ASYNC configure(); + +public: + Applet( const QString& configFile, QWidget *parent ); + virtual ~Applet(); + virtual int widthForHeight( int height ) const; + virtual int heightForWidth( int width ) const; + + /** + * Looks up @param embed in @ref menus, and removes it. + */ + void menuLost( MenuEmbed* embed ); + void updateMenuGeometry( MenuEmbed* embed ); + void setBackground(); + +protected: + + virtual void paletteChange(const QPalette& ); + virtual void positionChange( Position p ); + virtual void moveEvent(QMoveEvent *); + +private slots: + + /** + * Called each time a window is added. When the selection is + * initially claimed, it is called for every window. It does the big + * work, and does the embedding with MenuEmbed. + */ + void windowAdded( WId w ); + + /** + * Finds @p w's menubar in @see menus, and then activates it. + * + * @param w the activated window. + */ + void activeWindowChanged( WId w ); + + /** + * Called when the selection(selection_atom) is lost. Deletes the + * embedded menus, and starts listening for the selection again. + * + */ + void lostSelection(); + + /** + * Reads whether a central menu bar should be used or not, basically. + */ + void readSettings(); + void claimSelection(); + +private: + + /** + * Returns true if the selection is Not owned. That is, the menu applet + * isn't "running" and is listening for the selection to be released. + */ + bool isDisabled() const; + + WId tryTransientFor( WId w ); + + /** + * Does some sanity checks, and then sets active_menu to @param embed. + */ + void activateMenu( MenuEmbed* embed ); + + /** + * Creates msg_type_atom and selection_atom, and returns the latter. + */ + static Atom makeSelectionAtom(); + void updateTopEdgeOffset(); + KWinModule* module; + + /** + * List of all known menus. + */ + QValueList< MenuEmbed* > menus; + + /** + * A pointer to the current active menu, which is member + * of @ref menus. + */ + MenuEmbed* active_menu; + + KSelectionOwner* selection; + + /** + * Only the messenger. Dispatches signals to claimSelection(). + */ + KSelectionWatcher* selection_watcher; + + /** + * Whether the Desktop menu should be used, when a window + * with no menu is used. + */ + bool desktop_menu; + DCOPClient dcopclient; + + /** + * The distance to the top of the screen. + */ + int topEdgeOffset; +}; + +/** + * + * @author Siegfried Nijssen <snijssen@liacs.nl> + * @author Lubos Lunak <l.lunak@suse.cz> + */ +class MenuEmbed + : public QXEmbed +{ + Q_OBJECT + +public: + + /** + * Default constructor + * + * @param mainwindow window ID for the window to be plugged + * @param desktop true if @p mainwindow is the desktop + */ + MenuEmbed( WId mainwindow, bool desktop, + QWidget* parent = NULL, const char* name = NULL ); + + void setBackground(); + + /** + * @returns the window ID for the handled window. + */ + WId mainWindow() const; + + /** + */ + bool isDesktopMenu() const; + virtual void setMinimumSize( int w, int h ); + void setMinimumSize( const QSize& s ) { setMinimumSize( s.width(), s.height()); } + +protected: + + /** + * When @p w is None, that is the embedded window was lost, it calls + * menuLost() such that the this is deleted from @ref menus list. + */ + virtual void windowChanged( WId w ); + + virtual bool x11Event( XEvent* ev ); + +private: + + void sendSyntheticConfigureNotifyEvent(); + WId main_window; + + /** + * If the window is the desktop window. + */ + bool desktop; +}; + +inline +bool Applet::isDisabled() const +{ + assert( ( selection == NULL && selection_watcher != NULL ) + || ( selection != NULL && selection_watcher == NULL )); + return selection == NULL; +} + +inline +WId MenuEmbed::mainWindow() const +{ + return main_window; +} + +inline +bool MenuEmbed::isDesktopMenu() const +{ + return desktop; +} + +} // namespace + +#endif diff --git a/kicker/applets/minipager/Makefile.am b/kicker/applets/minipager/Makefile.am new file mode 100644 index 000000000..da149f468 --- /dev/null +++ b/kicker/applets/minipager/Makefile.am @@ -0,0 +1,25 @@ +INCLUDES = $(all_includes) -I$(srcdir)/../../taskmanager \ + -I$(srcdir)/../../libkicker -I../../libkicker + +kde_module_LTLIBRARIES = minipager_panelapplet.la + +minipager_panelapplet_la_SOURCES = pagerapplet.cpp pagerbutton.cpp pagersettings.kcfgc + +minipager_panelapplet_la_METASOURCES = AUTO +noinst_HEADERS = pagerapplet.h pagerbutton.h + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = minipagerapplet.desktop + +EXTRA_DIST = $(lnk_DATA) + +minipager_panelapplet_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +minipager_panelapplet_la_LIBADD = $(LIB_KDEUI) \ + ../../taskmanager/libtaskmanager.la \ + ../../libkicker/libkickermain.la + +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/kminipagerapplet.pot + +srcdoc: + kdoc -a -p -H -d $$HOME/web/src/kminipagerapplet kminipagerapplet *.h -lqt -lkdecore -lkdeui -lkfile diff --git a/kicker/applets/minipager/minipagerapplet.desktop b/kicker/applets/minipager/minipagerapplet.desktop new file mode 100644 index 000000000..df5dcfb03 --- /dev/null +++ b/kicker/applets/minipager/minipagerapplet.desktop @@ -0,0 +1,122 @@ +[Desktop Entry] +Type=Plugin +Name=Desktop Preview & Pager +Name[af]=Werkskerm Voorskou & Boodskapper +Name[be]=Прагляд працоўных сталоў і пэйджар +Name[bg]=Изглед на екрана и пейджър +Name[bn]=ডেস্কটপ প্রাকদর্শন এবং পেজার +Name[bs]=Pregled i promjena radnih površina +Name[ca]=Vista prèvia d'escriptori i paginador +Name[cs]=Přepínač a náhled ploch +Name[csb]=Przestôwnik ë przezérnik pùltów +Name[da]=Desktop-forhåndsviser & -pager +Name[de]=Umschalten zwischen Arbeitsflächen +Name[el]=Προεπισκόπηση και αλλαγή επιφάνειας εργασίας +Name[eo]=Tabula antaŭrigardilo kaj paĝilo +Name[es]=Paginador y previsualizador del escritorio +Name[et]=Töölaudade eelvaatlus ja vahetaja +Name[eu]=Mahaigain aurrebista eta orrialdekatzailea +Name[fa]=پیشنمایش و پیجوی رومیزی +Name[fi]=Työpöydän esikatselu ja sivutus +Name[fr]=Gestionnaire et aperçu des bureaux +Name[fy]=Buroblêdfoarbyld en Pager +Name[gl]=Antevisor e Paxinador do Escritório +Name[hr]=Pager i preglednik radne površine +Name[hu]=Asztali előnézet és lapozó +Name[is]=Skjáborðs forskoðari & flettir +Name[it]=Anteprime e gestione dei desktop +Name[ja]=デスクトッププレビューとページャ +Name[ka]=სამუშაო დაფისა და გვერდების ჩვენება +Name[kk]=Үстелдер ақтарғышы +Name[km]=ការមើលផ្ទៃតុជាមុន និងភេកយ័រ +Name[lt]=Darbastalių peržiūrėjimo ir puslapiavimo priemonė +Name[mk]=Преглед и пејџер на раб. површина +Name[nb]=Forhåndsvising og bytte av skrivebord +Name[nds]=Schriefdisch-Ümschalter & Vöransicht +Name[ne]=डेस्कटप पूर्वावलोकन र पेजर +Name[nl]=Bureaubladvoorbeeld en pager +Name[nn]=Førehandsvising og byte av skrivebord +Name[pa]=ਵੇਹੜਾ ਝਲਕ ਅਤੇ ਪੇਜ਼ਰ +Name[pl]=Przełącznik i przeglądarka pulpitów +Name[pt]=Antevisão e Paginador do Ecrã +Name[pt_BR]=Pager & Pré-visualizador de Área de Trabalho +Name[ro]=Paginator și previzualizor pentru desktop +Name[ru]=Переключатель рабочих столов +Name[se]=Čállinbevddiid ovdačájeheaddji ja molssodeaddji +Name[sk]=Náhľad a stránkovač pracovnej plochy +Name[sl]=Ogledovalnik in pozivnik namizja +Name[sr]=Прегледач и пејџер радних површина +Name[sr@Latn]=Pregledač i pejdžer radnih površina +Name[sv]=Förhandsgranskning och hantering av skrivbord +Name[te]=రంగస్థల ముందు వీక్షణం & పెజర్ +Name[th]=แสดงตัวอย่างและเปลี่ยนพื้นที่ทำงาน +Name[tr]=Masaüstü Önizleyici ve Sayfalayıcı +Name[uk]=Перегляд стільниці і пейджер +Name[uz]=Ish stolining peyjeri +Name[uz@cyrillic]=Иш столининг пейжери +Name[vi]=Xem thử Màn hình nền & Chuyển đổi +Name[wa]=Préveyeu et pådjeu do scribanne +Name[zh_CN]=桌面预览器和页面切换器 +Name[zh_TW]=桌面預覽與縮圖 + +Comment=Preview, manage and switch to multiple virtual desktops +Comment[af]=Voorskou van, bestuur van en wissel tussen veelvuldige virtuelewerkskerms +Comment[ar]=عايين، دبِر و بدل إلى أسطح المكتب الوهمية المتعددة +Comment[be]=Прагляд, кіраванне і пераключэнне між некалькімі віртуальнымі працоўнымі сталамі +Comment[bg]=Преглед, управление и превключване към виртуалните работни плотове +Comment[bs]=Pregledajte, upravljajte i mijenjajte radne površine +Comment[ca]=Vista prèvia, gestió i canvi entre diversos escriptoris virtuals +Comment[cs]=Správa, náhled a přepínání virtuálních pracovních ploch +Comment[csb]=Pòdzérk, sprôwianié ë przestôwianié wirtualnëch pùltów +Comment[da]=Forhåndsvis, håndtér og skift mellem flere virtuelle desktoppe +Comment[de]=Vorschau und Verwaltung der virtuellen Arbeitsflächen. +Comment[el]=Προεπισκόπηση, διαχείριση και εναλλαγή σε πολλαπλές εικονικές επιφάνειες εργασίας +Comment[eo]=Antaŭrigardi, mastrumi kaj komuti al pluraj virtualaj labortabloj +Comment[es]=Previsualizar, gestionar y cambiar a múltiples escritorios virtuales +Comment[et]=Virtuaalsete töölaudade eelvaatlus, haldus ja vahetamine +Comment[eu]=Aurrebista, kudeatu eta aldatu mahaigain birtual anitzez +Comment[fa]=پیشنمایش، مدیریت و سودهی به رومیزیهای مجازی چندگانه +Comment[fi]=Esikatsele, hallitse ja vaihda virtuaalisia työpöytiä +Comment[fr]=Affichage, gestion et changement des bureaux virtuels multiples +Comment[fy]=Foarbyld, beheare en skeakel nei meardere firtuele buroblêden +Comment[gl]=Xestione, cambie e antevexa múltiplos escritórios virtuais +Comment[he]=תצוגה מקדימה, והעברה של שולחנות עבודה וירטואלים +Comment[hr]=Pregled, upravljanje i prebacivanje između višestrukih virtualnih radnih površina +Comment[hu]=A virtuális asztalok előnézete, kezelése, használata +Comment[is]=Forskoðaðu, stjórnaðu og flettu á milli marga sýndarskjáborða +Comment[it]=Gestisce, cambia e mostra le anteprime dei desktop virtuali multipli +Comment[ja]=複数の仮想デスクトップのプレビュー、管理と切り替え +Comment[kk]=Көп қиртуалды үстелдерді қарау, басқару және олаға ауысу +Comment[km]=ការមើលជាមុន, គ្រប់គ្រង និងប្តូរទៅពហុផ្ទៃតុនិម្មិត +Comment[lt]=Peržiūrėkite, tvarkykite ir lengvai vaikščiokite tarp daugelio menamų darbastalių +Comment[mk]=Преглед, управување и префрлање на повеќе виртуелни работни површини +Comment[nb]=Forhåndsvisning, håndtere og bytte til virtuelle skrivebord +Comment[nds]=Vöransicht un Pleeg vun virtuelle Schriefdischen +Comment[ne]=पूर्वावलोकन अनि प्रबन्ध गर्नुहोस् र अवास्तविक डेस्कटपमा स्विच गर्नुहोस् +Comment[nl]=Vooruitblik, beheer en schakel naar meerdere virtuele bureaubladen +Comment[nn]=Førehandsvis, handter og byt til virtuelle skrivebord +Comment[pa]=ਕਈ ਫ਼ਰਜ਼ੀ ਵੇਹੜਿਆਂ ਦੀ ਝਲਕ ਵੇਖਣ, ਉਹਨਾਂ ਦੇ ਪਰਬੰਧ ਅਤੇ ਤਬਦੀਲ ਕਰਨ ਲਈ ਹੈ +Comment[pl]=Podgląd, zarządzanie i przełączanie wirtualnych pulpitów +Comment[pt]=Antever, gerir e mudar para vários ecrãs virtuais +Comment[pt_BR]=Fornece uma pré-visualização, gerencia e alterna entre múltiplos ambientes de trabalho virtuais +Comment[ro]=Previzualizează, gestionează și schimbă desktop-uri virtuale +Comment[ru]=Переключение между виртуальными рабочими столами с возможностью показа их содержимого +Comment[sk]=Náhľad, nastavenie a prepínanie viacerých virtuálnych pracovných plôch +Comment[sl]=Predogled, upravljanje in preklapljanje za več navideznih namizij +Comment[sr]=Прегледајте, управљајте, и пребацујте се између радних површина +Comment[sr@Latn]=Pregledajte, upravljajte, i prebacujte se između radnih površina +Comment[sv]=Förhandsgranska, hantera och byt mellan flera virtuella skrivbord +Comment[te]=ముందు వీక్షణ, ఎక్కువ మిధ్యా రంగస్థలాల నియంత్రణ మరయు మార్పు +Comment[th]=ดูตัวอย่าง, จัดการและเปลี่ยนไปใช้พื้นที่ทำงานเสมือนอื่นๆ +Comment[uk]=Перегляд, керування та перемикання на багато віртуальних стільниць +Comment[uz]=Virtual ish stollarini koʻrib chiqish, boshqarish va ularga oʻtish uchun qulay vosita +Comment[uz@cyrillic]=Виртуал иш столларини кўриб чиқиш, бошқариш ва уларга ўтиш учун қулай восита +Comment[vi]=Xem thử, quản lý và chuyển đổi giữa các màn hình nền ảo +Comment[wa]=Prévey, manaedjî et candjî viè sacwants forveyous scribannes +Comment[zh_CN]=预览、管理及切换多个虚拟桌面 +Comment[zh_TW]=預覽、管理並切換到多個虛擬桌面 + +Icon=kpager + +X-KDE-Library=minipager_panelapplet +X-KDE-UniqueApplet=false diff --git a/kicker/applets/minipager/pagerapplet.cpp b/kicker/applets/minipager/pagerapplet.cpp new file mode 100644 index 000000000..3ba87c0b1 --- /dev/null +++ b/kicker/applets/minipager/pagerapplet.cpp @@ -0,0 +1,906 @@ +/***************************************************************** + +Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <qpainter.h> +#include <qtooltip.h> +#include <qlineedit.h> +#include <qpopupmenu.h> +#include <qlayout.h> +#include <qbuttongroup.h> + +#include <dcopref.h> +#include <kglobalsettings.h> +#include <kwin.h> +#include <kwinmodule.h> +#include <kapplication.h> +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> +#include <kprocess.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> +#include <kiconloader.h> +#include <dcopclient.h> +#include <netwm.h> +#include <kmanagerselection.h> + +#include "global.h" +#include "kickertip.h" +#include "kickerSettings.h" +#include "kshadowengine.h" +#include "kshadowsettings.h" +#include "paneldrag.h" +#include "taskmanager.h" + +#include "pagerapplet.h" +#include "pagerapplet.moc" + +#ifdef FocusOut +#undef FocusOut +#endif + +const int knDesktopPreviewSize = 12; +const int knBtnSpacing = 1; + +// The previews tend to have a 4/3 aspect ratio +static const int smallHeight = 32; +static const int smallWidth = 42; + +// config menu id offsets +static const int rowOffset = 2000; +static const int labelOffset = 200; +static const int bgOffset = 300; + +extern "C" +{ + KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile) + { + KGlobal::locale()->insertCatalogue("kminipagerapplet"); + return new KMiniPager(configFile, KPanelApplet::Normal, 0, parent, "kminipagerapplet"); + } +} + +KMiniPager::KMiniPager(const QString& configFile, Type type, int actions, + QWidget *parent, const char *name) + : KPanelApplet( configFile, type, actions, parent, name ), + m_layout(0), + m_desktopLayoutOwner( NULL ), + m_shadowEngine(0), + m_contextMenu(0), + m_settings( new PagerSettings(sharedConfig()) ) +{ + setBackgroundOrigin( AncestorOrigin ); + int scnum = QApplication::desktop()->screenNumber(this); + QRect desk = QApplication::desktop()->screenGeometry(scnum); + if (desk.width() <= 800) + { + KConfigSkeleton::ItemBool* item = dynamic_cast<KConfigSkeleton::ItemBool*>(m_settings->findItem("Preview")); + if (item) + { + item->setDefaultValue(false); + } + } + m_settings->readConfig(); + m_windows.setAutoDelete(true); + if (m_settings->preview()) + { + TaskManager::the()->trackGeometry(); + } + + m_group = new QButtonGroup(this); + m_group->setBackgroundOrigin(AncestorOrigin); + m_group->setFrameStyle(QFrame::NoFrame); + m_group->setExclusive( true ); + + setFont( KGlobalSettings::taskbarFont() ); + + m_kwin = new KWinModule(this); + m_activeWindow = m_kwin->activeWindow(); + m_curDesk = m_kwin->currentDesktop(); + + if (m_curDesk == 0) // kwin not yet launched + { + m_curDesk = 1; + } + + desktopLayoutOrientation = Qt::Horizontal; + desktopLayoutX = -1; + desktopLayoutY = -1; + + QSize s(m_kwin->numberOfViewports(m_kwin->currentDesktop())); + m_useViewports = s.width() * s.height() > 1; + + drawButtons(); + + connect( m_kwin, SIGNAL( currentDesktopChanged(int)), SLOT( slotSetDesktop(int) ) ); + connect( m_kwin, SIGNAL( currentDesktopViewportChanged(int, const QPoint&)), + SLOT(slotSetDesktopViewport(int, const QPoint&))); + connect( m_kwin, SIGNAL( numberOfDesktopsChanged(int)), SLOT( slotSetDesktopCount(int) ) ); + connect( m_kwin, SIGNAL( activeWindowChanged(WId)), SLOT( slotActiveWindowChanged(WId) ) ); + connect( m_kwin, SIGNAL( windowAdded(WId) ), this, SLOT( slotWindowAdded(WId) ) ); + connect( m_kwin, SIGNAL( windowRemoved(WId) ), this, SLOT( slotWindowRemoved(WId) ) ); + connect( m_kwin, SIGNAL( windowChanged(WId,unsigned int) ), this, SLOT( slotWindowChanged(WId,unsigned int) ) ); + connect( m_kwin, SIGNAL( desktopNamesChanged() ), this, SLOT( slotDesktopNamesChanged() ) ); + connect( kapp, SIGNAL(backgroundChanged(int)), SLOT(slotBackgroundChanged(int)) ); + + if (kapp->authorizeKAction("kicker_rmb") && kapp->authorizeControlModule("kde-kcmtaskbar.desktop")) + { + m_contextMenu = new QPopupMenu(); + connect(m_contextMenu, SIGNAL(aboutToShow()), SLOT(aboutToShowContextMenu())); + connect(m_contextMenu, SIGNAL(activated(int)), SLOT(contextMenuActivated(int))); + setCustomMenu(m_contextMenu); + } + + QValueList<WId>::ConstIterator it; + QValueList<WId>::ConstIterator itEnd = m_kwin->windows().end(); + for ( it = m_kwin->windows().begin(); it != itEnd; ++it) + { + slotWindowAdded( (*it) ); + } + + slotSetDesktop( m_curDesk ); + updateLayout(); +} + +KMiniPager::~KMiniPager() +{ + KGlobal::locale()->removeCatalogue("kminipagerapplet"); + delete m_contextMenu; + delete m_settings; + delete m_shadowEngine; +} + +void KMiniPager::slotBackgroundChanged(int desk) +{ + unsigned numDesktops = m_kwin->numberOfDesktops(); + if (numDesktops != m_desktops.count()) + { + slotSetDesktopCount(numDesktops); + } + + if (desk < 1 || (unsigned) desk > m_desktops.count()) + { + // should not happen, but better to be paranoid than crash + return; + } + + m_desktops[desk - 1]->backgroundChanged(); +} + +void KMiniPager::slotSetDesktop(int desktop) +{ + if (m_kwin->numberOfDesktops() > static_cast<int>(m_desktops.count())) + { + slotSetDesktopCount( m_kwin->numberOfDesktops() ); + } + + if (!m_useViewports && (desktop != KWin::currentDesktop())) + { + // this can happen when the user clicks on a desktop, + // holds down the key combo to switch desktops, lets the + // mouse go but keeps the key combo held. the desktop will switch + // back to the desktop associated with the key combo and then it + // becomes a race condition between kwin's signal and the button's + // signal. usually kwin wins. + return; + } + + m_curDesk = desktop; + if (m_curDesk < 1) + { + m_curDesk = 1; + } + + KMiniPagerButton* button = m_desktops[m_curDesk - 1]; + if (!button->isOn()) + { + button->toggle(); + } +} + +void KMiniPager::slotSetDesktopViewport(int desktop, const QPoint& viewport) +{ + // ### + Q_UNUSED(desktop); + QSize s(m_kwin->numberOfViewports(m_kwin->currentDesktop())); + slotSetDesktop((viewport.y()-1) * s.width() + viewport.x() ); +} + +void KMiniPager::slotButtonSelected( int desk ) +{ + if (m_kwin->numberOfViewports(m_kwin->currentDesktop()).width() * + m_kwin->numberOfViewports(m_kwin->currentDesktop()).height() > 1) + { + QPoint p; + + p.setX( (desk-1) * QApplication::desktop()->width()); + p.setY( 0 ); + + KWin::setCurrentDesktopViewport(m_kwin->currentDesktop(), p); + } + else + KWin::setCurrentDesktop( desk ); + + slotSetDesktop( desk ); +} + +int KMiniPager::widthForHeight(int h) const +{ + if (orientation() == Qt::Vertical) + { + return width(); + } + + int deskNum = m_kwin->numberOfDesktops() * m_kwin->numberOfViewports(0).width() + * m_kwin->numberOfViewports(0).height(); + + int rowNum = m_settings->numberOfRows(); + if (rowNum == 0) + { + if (h <= 32 || deskNum <= 1) + { + rowNum = 1; + } + else + { + rowNum = 2; + } + } + + int deskCols = deskNum/rowNum; + if(deskNum == 0 || deskNum % rowNum != 0) + deskCols += 1; + + int bw = (h / rowNum); + if( m_settings->labelType() != PagerSettings::EnumLabelType::LabelName ) + { + if (desktopPreview() || m_settings->backgroundType() == PagerSettings::EnumBackgroundType::BgLive) + { + bw = (int) ( bw * (double) QApplication::desktop()->width() / QApplication::desktop()->height() ); + } + } + else + { + // scale to desktop width as a minimum + bw = (int) (bw * (double) QApplication::desktop()->width() / QApplication::desktop()->height()); + QFontMetrics fm = fontMetrics(); + for (int i = 1; i <= deskNum; i++) + { + int sw = fm.width( m_kwin->desktopName( i ) ) + 8; + if (sw > bw) + { + bw = sw; + } + } + } + + // we add one to the width for the spacing in between the buttons + // however, the last button doesn't have a space on the end of it (it's + // only _between_ buttons) so we then remove that one pixel + return (deskCols * (bw + 1)) - 1; +} + +int KMiniPager::heightForWidth(int w) const +{ + if (orientation() == Qt::Horizontal) + { + return height(); + } + + int deskNum = m_kwin->numberOfDesktops() * m_kwin->numberOfViewports(0).width() + * m_kwin->numberOfViewports(0).height(); + int rowNum = m_settings->numberOfRows(); // actually these are columns now... oh well. + if (rowNum == 0) + { + if (w <= 48 || deskNum == 1) + { + rowNum = 1; + } + else + { + rowNum = 2; + } + } + + int deskCols = deskNum/rowNum; + if(deskNum == 0 || deskNum % rowNum != 0) + { + deskCols += 1; + } + + int bh = (w/rowNum) + 1; + if ( desktopPreview() ) + { + bh = (int) ( bh * (double) QApplication::desktop()->height() / QApplication::desktop()->width() ); + } + else if ( m_settings->labelType() == PagerSettings::EnumLabelType::LabelName ) + { + bh = fontMetrics().lineSpacing() + 8; + } + + // we add one to the width for the spacing in between the buttons + // however, the last button doesn't have a space on the end of it (it's + // only _between_ buttons) so we then remove that one pixel + int nHg = (deskCols * (bh + 1)) - 1; + + return nHg; +} + +void KMiniPager::updateDesktopLayout(int o, int x, int y) +{ + if ((desktopLayoutOrientation == o) && + (desktopLayoutX == x) && + (desktopLayoutY == y)) + { + return; + } + + desktopLayoutOrientation = o; + desktopLayoutX = x; + desktopLayoutY = y; + if( x == -1 ) // do-the-maths-yourself is encoded as 0 in the wm spec + x = 0; + if( y == -1 ) + y = 0; + if( m_desktopLayoutOwner == NULL ) + { // must own manager selection before setting global desktop layout + int screen = DefaultScreen( qt_xdisplay()); + m_desktopLayoutOwner = new KSelectionOwner( QString( "_NET_DESKTOP_LAYOUT_S%1" ).arg( screen ).latin1(), + screen, this ); + if( !m_desktopLayoutOwner->claim( false )) + { + delete m_desktopLayoutOwner; + m_desktopLayoutOwner = NULL; + return; + } + } + NET::Orientation orient = o == Qt::Horizontal ? NET::OrientationHorizontal : NET::OrientationVertical; + NETRootInfo i( qt_xdisplay(), 0 ); + i.setDesktopLayout( orient, x, y, NET::DesktopLayoutCornerTopLeft ); +} + +void KMiniPager::resizeEvent(QResizeEvent*) +{ + bool horiz = orientation() == Horizontal; + + int deskNum = m_desktops.count(); + int rowNum = m_settings->numberOfRows(); + if (rowNum == 0) + { + if (((horiz && height()<=32)||(!horiz && width()<=48)) || deskNum <= 1) + rowNum = 1; + else + rowNum = 2; + } + + int deskCols = deskNum/rowNum; + if(deskNum == 0 || deskNum % rowNum != 0) + deskCols += 1; + + if (m_layout) + { + delete m_layout; + m_layout = 0; + } + + int nDX, nDY; + if (horiz) + { + nDX = rowNum; + nDY = deskCols; + updateDesktopLayout(Qt::Horizontal, -1, nDX); + } + else + { + nDX = deskCols; + nDY = rowNum; + updateDesktopLayout(Qt::Horizontal, nDY, -1); + } + + // 1 pixel spacing. + m_layout = new QGridLayout(this, nDX, nDY, 0, 1); + + QValueList<KMiniPagerButton*>::Iterator it = m_desktops.begin(); + QValueList<KMiniPagerButton*>::Iterator itEnd = m_desktops.end(); + int c = 0, + r = 0; + while( it != itEnd ) { + c = 0; + while( (it != itEnd) && (c < nDY) ) { + m_layout->addWidget( *it, r, c ); + ++it; + ++c; + } + ++r; + } + + m_layout->activate(); + updateGeometry(); +} + +void KMiniPager::wheelEvent( QWheelEvent* e ) +{ + int newDesk; + int desktops = KWin::numberOfDesktops(); + if (m_kwin->numberOfViewports(0).width() * m_kwin->numberOfViewports(0).height() > 1 ) + desktops = m_kwin->numberOfViewports(0).width() * m_kwin->numberOfViewports(0).height(); + if (e->delta() < 0) + { + newDesk = m_curDesk % desktops + 1; + } + else + { + newDesk = (desktops + m_curDesk - 2) % desktops + 1; + } + + slotButtonSelected(newDesk); +} + +void KMiniPager::drawButtons() +{ + int deskNum = m_kwin->numberOfDesktops(); + KMiniPagerButton *desk; + + int count = 1; + int i = 1; + do + { + QSize viewportNum = m_kwin->numberOfViewports(i); + for (int j = 1; j <= viewportNum.width() * viewportNum.height(); ++j) + { + QSize s(m_kwin->numberOfViewports(m_kwin->currentDesktop())); + QPoint viewport( (j-1) % s.width(), (j-1) / s.width()); + desk = new KMiniPagerButton( count, m_useViewports, viewport, this ); + if ( m_settings->labelType() != PagerSettings::EnumLabelType::LabelName ) + { + QToolTip::add( desk, desk->desktopName() ); + } + + m_desktops.append( desk ); + m_group->insert( desk, count ); + + connect(desk, SIGNAL(buttonSelected(int)), + SLOT(slotButtonSelected(int)) ); + connect(desk, SIGNAL(showMenu(const QPoint&, int )), + SLOT(slotShowMenu(const QPoint&, int )) ); + + desk->show(); + ++count; + } + } + while ( ++i <= deskNum ); +} + +void KMiniPager::slotSetDesktopCount( int ) +{ + QValueList<KMiniPagerButton*>::ConstIterator it; + QValueList<KMiniPagerButton*>::ConstIterator itEnd = m_desktops.end(); + for( it = m_desktops.begin(); it != itEnd; ++it ) + { + delete (*it); + } + m_desktops.clear(); + + drawButtons(); + + m_curDesk = m_kwin->currentDesktop(); + if ( m_curDesk == 0 ) + { + m_curDesk = 1; + } + + resizeEvent(0); + updateLayout(); +} + +void KMiniPager::slotActiveWindowChanged( WId win ) +{ + if (desktopPreview()) + { + KWin::WindowInfo* inf1 = m_activeWindow ? info( m_activeWindow ) : NULL; + KWin::WindowInfo* inf2 = win ? info( win ) : NULL; + m_activeWindow = win; + + QValueList<KMiniPagerButton*>::ConstIterator it; + QValueList<KMiniPagerButton*>::ConstIterator itEnd = m_desktops.end(); + for ( it = m_desktops.begin(); it != itEnd; ++it) + { + if ( ( inf1 && (*it)->shouldPaintWindow(inf1)) || + ( inf2 && (*it)->shouldPaintWindow(inf2)) ) + { + (*it)->windowsChanged(); + } + } + } +} + +void KMiniPager::slotWindowAdded( WId win) +{ + if (desktopPreview()) + { + KWin::WindowInfo* inf = info( win ); + + if (inf->state() & NET::SkipPager) + { + return; + } + + QValueList<KMiniPagerButton*>::ConstIterator it; + QValueList<KMiniPagerButton*>::ConstIterator itEnd = m_desktops.end(); + for ( it = m_desktops.begin(); it != itEnd; ++it) + { + if ( (*it)->shouldPaintWindow(inf) ) + { + (*it)->windowsChanged(); + } + } + } +} + +void KMiniPager::slotWindowRemoved(WId win) +{ + if (desktopPreview()) + { + KWin::WindowInfo* inf = info(win); + bool onAllDesktops = inf->onAllDesktops(); + bool onAllViewports = inf->hasState(NET::Sticky); + bool skipPager = inf->state() & NET::SkipPager; + int desktop = inf->desktop(); + + if (win == m_activeWindow) + m_activeWindow = 0; + + m_windows.remove((long) win); + + if (skipPager) + { + return; + } + + QValueList<KMiniPagerButton*>::ConstIterator it; + QValueList<KMiniPagerButton*>::ConstIterator itEnd = m_desktops.end(); + for (it = m_desktops.begin(); it != itEnd; ++it) + { + if (onAllDesktops || onAllViewports || desktop == (*it)->desktop()) + { + (*it)->windowsChanged(); + } + } + } + else + { + m_windows.remove(win); + return; + } + +} + +void KMiniPager::slotWindowChanged( WId win , unsigned int properties ) +{ + if ((properties & (NET::WMState | NET::XAWMState | NET::WMDesktop)) == 0 && + (!desktopPreview() || (properties & NET::WMGeometry) == 0) && + !(desktopPreview() && windowIcons() && + (properties & NET::WMIcon | NET::WMIconName | NET::WMVisibleIconName) == 0)) + { + return; + } + + if (desktopPreview()) + { + KWin::WindowInfo* inf = m_windows[win]; + bool skipPager = inf->hasState(NET::SkipPager); + QMemArray<bool> old_shouldPaintWindow(m_desktops.size()); + QValueList<KMiniPagerButton*>::ConstIterator it; + QValueList<KMiniPagerButton*>::ConstIterator itEnd = m_desktops.end(); + int i = 0; + for ( it = m_desktops.begin(); it != itEnd; ++it) + { + old_shouldPaintWindow[i++] = (*it)->shouldPaintWindow(inf); + } + + m_windows.remove(win); + inf = info(win); + + if (inf->hasState(NET::SkipPager) || skipPager) + { + return; + } + + for ( i = 0, it = m_desktops.begin(); it != itEnd; ++it) + { + if ( old_shouldPaintWindow[i++] || (*it)->shouldPaintWindow(inf)) + { + (*it)->windowsChanged(); + } + } + } + else + { + m_windows.remove(win); + return; + } +} + +KWin::WindowInfo* KMiniPager::info( WId win ) +{ + if (!m_windows[win]) + { + KWin::WindowInfo* info = new KWin::WindowInfo( win, + NET::WMWindowType | NET::WMState | NET::XAWMState | NET::WMDesktop | NET::WMGeometry | NET::WMKDEFrameStrut, 0 ); + + m_windows.insert(win, info); + return info; + } + + return m_windows[win]; +} + +KTextShadowEngine* KMiniPager::shadowEngine() +{ + if (!m_shadowEngine) + m_shadowEngine = new KTextShadowEngine(); + + return m_shadowEngine; +} + +void KMiniPager::refresh() +{ + QValueList<KMiniPagerButton*>::ConstIterator it; + QValueList<KMiniPagerButton*>::ConstIterator itEnd = m_desktops.end(); + for ( it = m_desktops.begin(); it != itEnd; ++it) + { + (*it)->update(); + } +} + +void KMiniPager::aboutToShowContextMenu() +{ + m_contextMenu->clear(); + + m_contextMenu->insertItem(SmallIcon("kpager"), i18n("&Launch Pager"), LaunchExtPager); + m_contextMenu->insertSeparator(); + + m_contextMenu->insertItem(i18n("&Rename Desktop \"%1\"") + .arg(kwin()->desktopName(m_rmbDesk)), RenameDesktop); + m_contextMenu->insertSeparator(); + + KPopupMenu* showMenu = new KPopupMenu(m_contextMenu); + showMenu->setCheckable(true); + showMenu->insertTitle(i18n("Pager Layout")); + + QPopupMenu* rowMenu = new QPopupMenu(showMenu); + rowMenu->setCheckable(true); + rowMenu->insertItem(i18n("&Automatic"), 0 + rowOffset); + rowMenu->insertItem(i18n("one row or column", "&1"), 1 + rowOffset); + rowMenu->insertItem(i18n("two rows or columns", "&2"), 2 + rowOffset); + rowMenu->insertItem( i18n("three rows or columns", "&3"), 3 + rowOffset); + connect(rowMenu, SIGNAL(activated(int)), SLOT(contextMenuActivated(int))); + showMenu->insertItem((orientation()==Horizontal) ? i18n("&Rows"): + i18n("&Columns"), + rowMenu); + + showMenu->insertItem(i18n("&Window Thumbnails"), WindowThumbnails); + showMenu->insertItem(i18n("&Window Icons"), WindowIcons); + + showMenu->insertTitle(i18n("Text Label")); + showMenu->insertItem(i18n("Desktop N&umber"), + PagerSettings::EnumLabelType::LabelNumber + labelOffset); + showMenu->insertItem(i18n("Desktop N&ame"), + PagerSettings::EnumLabelType::LabelName + labelOffset); + showMenu->insertItem(i18n("N&o Label"), + PagerSettings::EnumLabelType::LabelNone + labelOffset); + + showMenu->insertTitle(i18n("Background")); + showMenu->insertItem(i18n("&Elegant"), + PagerSettings::EnumBackgroundType::BgPlain + bgOffset); + showMenu->insertItem(i18n("&Transparent"), + PagerSettings::EnumBackgroundType::BgTransparent + bgOffset); + showMenu->insertItem(i18n("&Desktop Wallpaper"), + PagerSettings::EnumBackgroundType::BgLive + bgOffset); + connect(showMenu, SIGNAL(activated(int)), SLOT(contextMenuActivated(int))); + m_contextMenu->insertItem(i18n("&Pager Options"),showMenu); + + m_contextMenu->insertItem(SmallIcon("configure"), + i18n("&Configure Desktops..."), + ConfigureDesktops); + + rowMenu->setItemChecked(m_settings->numberOfRows() + rowOffset, true); + m_contextMenu->setItemChecked(m_settings->labelType() + labelOffset, showMenu); + m_contextMenu->setItemChecked(m_settings->backgroundType() + bgOffset, showMenu); + + m_contextMenu->setItemChecked(WindowThumbnails, m_settings->preview()); + m_contextMenu->setItemChecked(WindowIcons, m_settings->icons()); + m_contextMenu->setItemEnabled(WindowIcons, m_settings->preview()); + m_contextMenu->setItemEnabled(RenameDesktop, + m_settings->labelType() == + PagerSettings::EnumLabelType::LabelName); +} + +void KMiniPager::slotShowMenu(const QPoint& pos, int desktop) +{ + if (!m_contextMenu) + { + return; + } + + m_rmbDesk = desktop; + m_contextMenu->exec(pos); + m_rmbDesk = -1; +} + +void KMiniPager::contextMenuActivated(int result) +{ + if (result < 1) + { + return; + } + + switch (result) + { + case LaunchExtPager: + showPager(); + return; + + case ConfigureDesktops: + kapp->startServiceByDesktopName("desktop"); + return; + + case RenameDesktop: + m_desktops[(m_rmbDesk == -1) ? m_curDesk - 1 : m_rmbDesk - 1]->rename(); + return; + } + + if (result >= rowOffset) + { + m_settings->setNumberOfRows(result - rowOffset); + resizeEvent(0); + } + + switch (result) + { + case WindowThumbnails: + m_settings->setPreview(!m_settings->preview()); + TaskManager::the()->trackGeometry(); + break; + + case WindowIcons: + m_settings->setIcons(!m_settings->icons()); + break; + + case PagerSettings::EnumBackgroundType::BgPlain + bgOffset: + m_settings->setBackgroundType(PagerSettings::EnumBackgroundType::BgPlain); + break; + case PagerSettings::EnumBackgroundType::BgTransparent + bgOffset: + m_settings->setBackgroundType(PagerSettings::EnumBackgroundType::BgTransparent); + break; + case PagerSettings::EnumBackgroundType::BgLive + bgOffset: + { + m_settings->setBackgroundType(PagerSettings::EnumBackgroundType::BgLive); + QValueList<KMiniPagerButton*>::ConstIterator it; + QValueList<KMiniPagerButton*>::ConstIterator itEnd = m_desktops.end(); + for( it = m_desktops.begin(); it != itEnd; ++it ) + { + (*it)->backgroundChanged(); + } + break; + } + + case PagerSettings::EnumLabelType::LabelNone + labelOffset: + m_settings->setLabelType(PagerSettings::EnumLabelType::LabelNone); + break; + case PagerSettings::EnumLabelType::LabelNumber + labelOffset: + m_settings->setLabelType(PagerSettings::EnumLabelType::LabelNumber); + break; + case PagerSettings::EnumLabelType::LabelName + labelOffset: + m_settings->setLabelType(PagerSettings::EnumLabelType::LabelName); + break; + } + + m_settings->writeConfig(); + updateGeometry(); + refresh(); +} + +void KMiniPager::slotDesktopNamesChanged() +{ + QValueList<KMiniPagerButton*>::ConstIterator it = m_desktops.begin(); + QValueList<KMiniPagerButton*>::ConstIterator itEnd = m_desktops.end(); + + for (int i = 1; it != itEnd; ++it, ++i) + { + QString name = m_kwin->desktopName(i); + (*it)->setDesktopName(name); + (*it)->repaint(); + QToolTip::remove((*it)); + QToolTip::add((*it), name); + } + + updateLayout(); +} + +void KMiniPager::showPager() +{ + DCOPClient *dcop=kapp->dcopClient(); + + if (dcop->isApplicationRegistered("kpager")) + { + showKPager(true); + } + else + { + // Let's run kpager if it isn't running + connect( dcop, SIGNAL( applicationRegistered(const QCString &) ), this, SLOT(applicationRegistered(const QCString &)) ); + dcop->setNotifications(true); + QString strAppPath(locate("exe", "kpager")); + if (!strAppPath.isEmpty()) + { + KProcess process; + process << strAppPath; + process << "--hidden"; + process.start(KProcess::DontCare); + } + } +} + +void KMiniPager::showKPager(bool toggleShow) +{ + QPoint pt; + switch ( position() ) + { + case pTop: + pt = mapToGlobal( QPoint(x(), y() + height()) ); + break; + case pLeft: + pt = mapToGlobal( QPoint(x() + width(), y()) ); + break; + case pRight: + case pBottom: + default: + pt=mapToGlobal( QPoint(x(), y()) ); + } + + DCOPClient *dcop=kapp->dcopClient(); + + QByteArray data; + QDataStream arg(data, IO_WriteOnly); + arg << pt.x() << pt.y() ; + if (toggleShow) + { + dcop->send("kpager", "KPagerIface", "toggleShow(int,int)", data); + } + else + { + dcop->send("kpager", "KPagerIface", "showAt(int,int)", data); + } +} + +void KMiniPager::applicationRegistered( const QCString & appName ) +{ + if (appName == "kpager") + { + disconnect( kapp->dcopClient(), SIGNAL( applicationRegistered(const QCString &) ), + this, SLOT(applicationRegistered(const QCString &)) ); + showKPager(false); + } +} + diff --git a/kicker/applets/minipager/pagerapplet.h b/kicker/applets/minipager/pagerapplet.h new file mode 100644 index 000000000..f47b0411d --- /dev/null +++ b/kicker/applets/minipager/pagerapplet.h @@ -0,0 +1,138 @@ +/***************************************************************** + +Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __MINIPAGER_H +#define __MINIPAGER_H + +#include <qvaluelist.h> +#include <qintdict.h> + +#include <kpanelapplet.h> +#include <kwin.h> + +#include "pagerbutton.h" +#include "pagersettings.h" + +class QButtonGroup; +class QGridLayout; +class QTimer; + +class KProcess; +class KWinModule; +class KTextShadowEngine; +class KSelectionOwner; + +class PagerSettings; + +class KMiniPager : public KPanelApplet +{ + Q_OBJECT + +public: + KMiniPager(const QString& configFile, Type t = Normal, int actions = 0, + QWidget *parent = 0, const char *name = 0); + + virtual ~KMiniPager(); + + int widthForHeight(int height) const; + int heightForWidth(int width) const; + + KWin::WindowInfo* info( WId win ); + KWinModule* kwin() { return m_kwin; } + KTextShadowEngine* shadowEngine(); + + void setActive( WId active ) { m_activeWindow = active; } + WId activeWindow() { return m_activeWindow; } + + enum ConfigOptions { LaunchExtPager = 96, WindowThumbnails, + WindowIcons, ConfigureDesktops, RenameDesktop }; + int labelType() const { return m_settings->labelType(); } + + int bgType() const { return m_settings->backgroundType(); } + + bool desktopPreview() const { return m_settings->preview(); } + bool windowIcons() const { return m_settings->icons(); } + + Orientation orientation() const { return KPanelApplet::orientation(); } + Direction popupDirection() { return KPanelApplet::popupDirection(); } + + void emitRequestFocus() { emit requestFocus(); } + + QPoint clickPos; + +public slots: + void slotSetDesktop(int desktop); + void slotSetDesktopViewport(int desktop, const QPoint& viewport); + void slotSetDesktopCount(int count); + void slotButtonSelected(int desk ); + void slotActiveWindowChanged( WId win ); + void slotWindowAdded( WId ); + void slotWindowRemoved( WId ); + void slotWindowChanged( WId, unsigned int ); + void slotShowMenu( const QPoint&, int ); + void slotDesktopNamesChanged(); + void slotBackgroundChanged( int ); + + void refresh(); + +protected: + void drawButtons(); + void startDrag( const QPoint &point ); + + void updateDesktopLayout(int,int,int); + void resizeEvent(QResizeEvent*); + void wheelEvent( QWheelEvent* e ); + void showKPager(bool toggleShow); + +protected slots: + void showPager(); + void applicationRegistered(const QCString &appName); + void aboutToShowContextMenu(); + void contextMenuActivated(int); + +private: + QValueList<KMiniPagerButton*> m_desktops; + int m_curDesk; + int m_rmbDesk; + + QIntDict<KWin::WindowInfo> m_windows; + WId m_activeWindow; + + QButtonGroup *m_group; + + QGridLayout *m_layout; + bool m_useViewports; + int desktopLayoutOrientation; + int desktopLayoutX; + int desktopLayoutY; + KSelectionOwner* m_desktopLayoutOwner; + + KWinModule *m_kwin; + KTextShadowEngine* m_shadowEngine; + + QPopupMenu *m_contextMenu; + PagerSettings *m_settings; +}; + +#endif + diff --git a/kicker/applets/minipager/pagerbutton.cpp b/kicker/applets/minipager/pagerbutton.cpp new file mode 100644 index 000000000..b4e268b8e --- /dev/null +++ b/kicker/applets/minipager/pagerbutton.cpp @@ -0,0 +1,824 @@ +/***************************************************************** + +Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <stdlib.h> + +#include <qcursor.h> +#include <qdrawutil.h> +#include <qlineedit.h> +#include <qpainter.h> +#include <qpopupmenu.h> +#include <qstylesheet.h> + +#include <netwm.h> +#include <dcopclient.h> + +#include <kwinmodule.h> +#include <ksharedpixmap.h> +#include <kpixmapio.h> +#include <kpixmapeffect.h> +#include <kstringhandler.h> +#include <kiconloader.h> + +#include "global.h" +#include "kickertip.h" +#include "kickerSettings.h" +#include "kshadowengine.h" +#include "paneldrag.h" + +#include "pagerapplet.h" +#include "pagerbutton.h" +#include "pagerbutton.moc" +#include "pagersettings.h" + +#ifdef FocusOut +#undef FocusOut +#endif + +KSharedPixmap* KMiniPagerButton::s_commonSharedPixmap; +KPixmap* KMiniPagerButton::s_commonBgPixmap; + +KMiniPagerButton::KMiniPagerButton(int desk, bool useViewPorts, const QPoint& viewport, + KMiniPager *parent, const char *name) + : QButton(parent, name), + m_pager(parent), + m_desktop(desk), + m_useViewports(useViewPorts), + m_viewport(viewport), + m_lineEdit(0), + m_sharedPixmap(0), + m_bgPixmap(0), + m_isCommon(false), + m_currentWindow(0), + m_inside(false) +{ + setToggleButton(true); + setAcceptDrops(true); + setWFlags(WNoAutoErase); + + setBackgroundOrigin(AncestorOrigin); + installEventFilter(KickerTip::the()); + + m_desktopName = m_pager->kwin()->desktopName(m_desktop); + + connect(this, SIGNAL(clicked()), SLOT(slotClicked())); + connect(this, SIGNAL(toggled(bool)), SLOT(slotToggled(bool))); + connect(&m_dragSwitchTimer, SIGNAL(timeout()), this, SLOT(slotDragSwitch())); + connect(&m_updateCompressor, SIGNAL(timeout()), this, SLOT(update())); + + if (m_pager->desktopPreview()) + { + setMouseTracking(true); + } + loadBgPixmap(); +} + +KMiniPagerButton::~KMiniPagerButton() +{ + delete m_sharedPixmap; + delete m_bgPixmap; +} + +QRect KMiniPagerButton::mapGeometryToViewport(const KWin::WindowInfo& info) const +{ + if (!m_useViewports) + return info.frameGeometry(); + + // ### fix vertically layouted viewports + QRect _r(info.frameGeometry()); + QPoint vx(m_pager->kwin()->currentViewport(m_pager->kwin()->currentDesktop())); + + _r.moveBy( - (m_desktop - vx.x()) * QApplication::desktop()->width(), + 0); + + if ((info.state() & NET::Sticky)) + { + _r.moveTopLeft(QPoint(_r.x() % QApplication::desktop()->width(), + _r.y() % QApplication::desktop()->height())); + + } + + return _r; +} + +QPoint KMiniPagerButton::mapPointToViewport(const QPoint& _p) const +{ + if (!m_useViewports) return _p; + + QPoint vx(m_pager->kwin()->currentViewport(m_pager->kwin()->currentDesktop())); + + // ### fix vertically layouted viewports + QPoint p(_p); + p.setX(p.x() + (m_desktop - vx.x()) * QApplication::desktop()->width()); + return p; +} + +bool KMiniPagerButton::shouldPaintWindow( KWin::WindowInfo *info ) const +{ + if (!info) + return false; + +// if (info->mappingState != NET::Visible) +// return false; + + NET::WindowType type = info->windowType( NET::NormalMask | NET::DesktopMask + | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask + | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask ); + + if (type == NET::Desktop || type == NET::Dock || type == NET::TopMenu) + return false; + + if (!m_useViewports && !info->isOnDesktop(m_desktop)) + return false; + + if (m_useViewports) { + QRect r = mapGeometryToViewport(*info); + + if (!info->hasState(NET::Sticky) && + !QApplication::desktop()->geometry().contains(r.topLeft()) && + !QApplication::desktop()->geometry().contains(r.topRight())) + return false; + } + + if (info->state() & NET::SkipPager || info->state() & NET::Shaded ) + return false; + + if (info->win() == m_pager->winId()) + return false; + + if ( info->isMinimized() ) + return false; + + return true; +} + +void KMiniPagerButton::resizeEvent(QResizeEvent *ev) +{ + if (m_lineEdit) + { + m_lineEdit->setGeometry(rect()); + } + + delete m_bgPixmap; + m_bgPixmap = 0; + + QButton::resizeEvent(ev); +} + +void KMiniPagerButton::windowsChanged() +{ + m_currentWindow = 0; + + if (!m_updateCompressor.isActive()) + { + m_updateCompressor.start(50, true); + } +} + +void KMiniPagerButton::backgroundChanged() +{ + delete s_commonSharedPixmap; + s_commonSharedPixmap = 0; + delete s_commonBgPixmap; + s_commonBgPixmap = 0; + loadBgPixmap(); +} + +void KMiniPagerButton::loadBgPixmap() +{ + if (m_pager->bgType() != PagerSettings::EnumBackgroundType::BgLive) + return; // not needed + + DCOPClient *client = kapp->dcopClient(); + if (!client->isAttached()) + { + client->attach(); + } + + QCString kdesktop_name; + int screen_number = DefaultScreen(qt_xdisplay()); + if (screen_number == 0) + kdesktop_name = "kdesktop"; + else + kdesktop_name.sprintf("kdesktop-screen-%d", screen_number); + + QByteArray data, replyData; + QCString replyType; + if (client->call(kdesktop_name, "KBackgroundIface", "isCommon()", + data, replyType, replyData)) + { + if (replyType == "bool") + { + QDataStream reply(replyData, IO_ReadOnly); + reply >> m_isCommon; + } + } + + if (m_isCommon) + { + if (s_commonBgPixmap) + { // pixmap is already ready, just use it + backgroundLoaded( true ); + return; + } + else if (s_commonSharedPixmap) + { // other button is already fetching the pixmap + connect(s_commonSharedPixmap, SIGNAL(done(bool)), + SLOT(backgroundLoaded(bool))); + return; + } + } + + QDataStream args( data, IO_WriteOnly ); + args << 1; + client->send(kdesktop_name, "KBackgroundIface", "setExport(int)", data); + + if (m_isCommon) + { + if (!s_commonSharedPixmap) + { + s_commonSharedPixmap = new KSharedPixmap; + connect(s_commonSharedPixmap, SIGNAL(done(bool)), + SLOT(backgroundLoaded(bool))); + } + s_commonSharedPixmap->loadFromShared(QString("DESKTOP1")); + } + else + { + if (!m_sharedPixmap) + { + m_sharedPixmap = new KSharedPixmap; + connect(m_sharedPixmap, SIGNAL(done(bool)), + SLOT(backgroundLoaded(bool))); + } + m_sharedPixmap->loadFromShared(QString("DESKTOP%1").arg(m_desktop)); + } +} + +static QPixmap scalePixmap(const QPixmap &pixmap, int width, int height) +{ + if (pixmap.width()>100) + { + KPixmapIO io; + QImage img( io.convertToImage( pixmap ) ); + return io.convertToPixmap( img.smoothScale( width, height ) ); + } + + QImage img( pixmap.convertToImage().smoothScale( width, height ) ); + QPixmap pix; + pix.convertFromImage( img ); + + return pix; +} + +void KMiniPagerButton::backgroundLoaded( bool loaded ) +{ + if (loaded) + { + if (!m_bgPixmap) + { + m_bgPixmap = new KPixmap; + } + if (m_isCommon) + { + if (!s_commonBgPixmap) + { + s_commonBgPixmap = new KPixmap; + *s_commonBgPixmap = scalePixmap(*s_commonSharedPixmap, width(), height()); + s_commonSharedPixmap->deleteLater(); // let others get the signal too + s_commonSharedPixmap = 0; + } + *m_bgPixmap = *s_commonBgPixmap; + } + else + { + *m_bgPixmap = scalePixmap(*m_sharedPixmap, width(), height()); + delete m_sharedPixmap; + m_sharedPixmap = 0L; + } + + update(); + } + else + { + kdWarning() << "Error getting the background\n"; + } +} + +void KMiniPagerButton::enterEvent(QEvent *) +{ + m_inside = true; + update(); +} + +void KMiniPagerButton::leaveEvent(QEvent *) +{ + m_inside = false; + update(); +} + +void KMiniPagerButton::drawButton(QPainter *bp) +{ + int w = width(); + int h = height(); + bool on = isOn(); + bool down = isDown(); + + QBrush background; + + bool liveBkgnd = m_pager->bgType() == PagerSettings::EnumBackgroundType::BgLive; + bool transparent = m_pager->bgType() == PagerSettings::EnumBackgroundType::BgTransparent; + + // background + + if (backgroundPixmap()) + { + QPoint pt = backgroundOffset(); + bp->drawTiledPixmap(0, 0, width(), height(), *backgroundPixmap(), pt.x(), pt.y()); + } + else + { + bp->fillRect(0, 0, width(), height(), paletteBackgroundColor()); + } + + + // desktop background + + if (liveBkgnd) + { + if (m_bgPixmap && !m_bgPixmap->isNull()) + { + if (on) + { + KPixmap tmp = *m_bgPixmap; + KPixmapEffect::intensity(tmp, 0.33); + bp->drawPixmap(0, 0, tmp); + } + else + { + bp->drawPixmap(0, 0, *m_bgPixmap); + } + } + else + { + liveBkgnd = false; + } + } + + if (!liveBkgnd) + { + if (transparent) + { + // transparent windows get an 1 pixel frame... + if (on) + { + bp->setPen(colorGroup().midlight()); + } + else if (down) + { + bp->setPen(KickerLib::blendColors(colorGroup().mid(), + colorGroup().midlight())); + } + else + { + bp->setPen(colorGroup().dark()); + } + + bp->drawRect(0, 0, w, h); + } + else + { + QBrush background; + + if (on) + { + background = colorGroup().brush(QColorGroup::Midlight); + } + else if (down) + { + background = KickerLib::blendColors(colorGroup().mid(), + colorGroup().midlight()); + } + else + { + background = colorGroup().brush(QColorGroup::Mid); + } + + bp->fillRect(0, 0, w, h, background); + } + } + + // window preview... + if (m_pager->desktopPreview()) + { + KWinModule* kwin = m_pager->kwin(); + KWin::WindowInfo *info = 0; + int dw = QApplication::desktop()->width(); + int dh = QApplication::desktop()->height(); + + QValueList<WId> windows = kwin->stackingOrder(); + QValueList<WId>::const_iterator itEnd = windows.constEnd(); + for (QValueList<WId>::ConstIterator it = windows.constBegin(); it != itEnd; ++it) + { + info = m_pager->info(*it); + + if (shouldPaintWindow(info)) + { + QRect r = mapGeometryToViewport(*info); + r = QRect(r.x() * width() / dw, 2 + r.y() * height() / dh, + r.width() * width() / dw, r.height() * height() / dh); + + if (kwin->activeWindow() == info->win()) + { + QBrush brush = colorGroup().brush(QColorGroup::Highlight); + qDrawShadeRect(bp, r, colorGroup(), false, 1, 0, &brush); + } + else + { + QBrush brush = colorGroup().brush(QColorGroup::Button); + + if (on) + { + brush.setColor(brush.color().light(120)); + } + + bp->fillRect(r, brush); + qDrawShadeRect(bp, r, colorGroup(), true, 1, 0); + } + + if (m_pager->windowIcons() && r.width() > 15 && r.height() > 15) + { + QPixmap icon = KWin::icon(*it, 16, 16, true); + if (!icon.isNull()) + { + bp->drawPixmap(r.left() + ((r.width() - 16) / 2), + r.top() + ((r.height() - 16) / 2), + icon); + } + } + } + } + } + + if (liveBkgnd) + { + // draw a little border around the individual buttons + // makes it look a bit more finished. + if (on) + { + bp->setPen(colorGroup().midlight()); + } + else + { + bp->setPen(colorGroup().mid()); + } + + bp->drawRect(0, 0, w, h); + } + + if (m_pager->labelType() != PagerSettings::EnumLabelType::LabelNone) + { + QString label = (m_pager->labelType() == PagerSettings::EnumLabelType::LabelNumber) ? + QString::number(m_desktop) : m_desktopName; + + if (transparent || liveBkgnd) + { + bp->setPen(on ? colorGroup().midlight() : colorGroup().buttonText()); + m_pager->shadowEngine()->drawText(*bp, QRect(0, 0, w, h), AlignCenter, label, size()); + } + else + bp->drawText(0, 0, w, h, AlignCenter, label); + } + + if (m_inside) + KickerLib::drawBlendedRect(bp, QRect(1, 1, width() - 2, height() - 2), colorGroup().foreground()); +} + +void KMiniPagerButton::mousePressEvent(QMouseEvent * e) +{ + if (e->button() == RightButton) + { + // prevent LMB down -> RMB down -> LMB up sequence + if ((e->state() & MouseButtonMask ) == NoButton) + { + emit showMenu(e->globalPos(), m_desktop); + return; + } + } + + if (m_pager->desktopPreview()) + { + m_pager->clickPos = e->pos(); + } + + QButton::mousePressEvent(e); +} + +void KMiniPagerButton::mouseReleaseEvent(QMouseEvent* e) +{ + m_pager->clickPos = QPoint(); + QButton::mouseReleaseEvent(e); +} + +void KMiniPagerButton::mouseMoveEvent(QMouseEvent* e) +{ + if (!m_pager->desktopPreview()) + { + return; + } + + int dw = QApplication::desktop()->width(); + int dh = QApplication::desktop()->height(); + int w = width(); + int h = height(); + + QPoint pos(m_pager->clickPos.isNull() ? mapFromGlobal(QCursor::pos()) : m_pager->clickPos); + QPoint p = mapPointToViewport(QPoint(pos.x() * dw / w, pos.y() * dh / h)); + + Task::Ptr wasWindow = m_currentWindow; + m_currentWindow = TaskManager::the()->findTask(m_useViewports ? 1 : m_desktop, p); + + if (wasWindow != m_currentWindow) + { + KickerTip::Client::updateKickerTip(); + } + + if (m_currentWindow && !m_pager->clickPos.isNull() && + (m_pager->clickPos - e->pos()).manhattanLength() > KGlobalSettings::dndEventDelay()) + { + QRect r = m_currentWindow->geometry(); + + // preview window height, window width + int ww = r.width() * w / dw; + int wh = r.height() * h / dh; + QPixmap windowImage(ww, wh); + QPainter bp(&windowImage, this); + + bp.setPen(colorGroup().foreground()); + bp.drawRect(0, 0, ww, wh); + bp.fillRect(1, 1, ww - 2, wh - 2, colorGroup().background()); + + Task::List tasklist; + tasklist.append(m_currentWindow); + TaskDrag* drag = new TaskDrag(tasklist, this); + QPoint offset(m_pager->clickPos.x() - (r.x() * w / dw), + m_pager->clickPos.y() - (r.y() * h / dh)); + drag->setPixmap(windowImage, offset); + drag->dragMove(); + + if (isDown()) + { + setDown(false); + } + + m_pager->clickPos = QPoint(); + } +} + +void KMiniPagerButton::dragEnterEvent(QDragEnterEvent* e) +{ + if (PanelDrag::canDecode(e)) + { + // ignore container drags + return; + } + else if (TaskDrag::canDecode(e)) + { + // if it's a task drag don't switch the desktop, just accept it + e->accept(); + setDown(true); + } + else + { + // if a dragitem is held for over a pager button for two seconds, + // activate corresponding desktop + m_dragSwitchTimer.start(1000, true); + QButton::dragEnterEvent(e); + } +} + +void KMiniPagerButton::dropEvent(QDropEvent* e) +{ + if (TaskDrag::canDecode(e)) + { + e->accept(); + Task::List tasks(TaskDrag::decode(e)); + + if ((m_useViewports || e->source() == this) && tasks.count() == 1) + { + Task::Ptr task = tasks[0]; + int dw = QApplication::desktop()->width(); + int dh = QApplication::desktop()->height(); + int w = width(); + int h = height(); + QRect location = mapGeometryToViewport(task->info()); + QPoint pos = mapPointToViewport(e->pos()); + int deltaX = pos.x() - m_pager->clickPos.x(); + int deltaY = pos.y() - m_pager->clickPos.y(); + + if (abs(deltaX) < 3) + { + deltaX = 0; + } + else + { + deltaX = deltaX * dw / w; + } + + if (abs(deltaY) < 3) + { + deltaY = 0; + } + else + { + deltaY = deltaY * dh / h; + } + + location.moveBy(deltaX, deltaY); + + XMoveWindow(x11Display(), task->window(), location.x(), location.y()); + if ((e->source() != this || !task->isOnAllDesktops()) && + task->desktop() != m_desktop) + { + task->toDesktop(m_desktop); + } + } + else + { + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + (*it)->toDesktop(m_desktop); + } + } + + setDown(false); + } + + QButton::dropEvent( e ); +} + +void KMiniPagerButton::enabledChange( bool oldEnabled ) +{ + if (m_pager->bgType() == PagerSettings::EnumBackgroundType::BgLive) + { + m_pager->refresh(); + } + + QButton::enabledChange(oldEnabled); +} + +void KMiniPagerButton::dragLeaveEvent( QDragLeaveEvent* e ) +{ + m_dragSwitchTimer.stop(); + + if (m_pager->kwin()->currentDesktop() != m_desktop) + { + setDown(false); + } + + QButton::dragLeaveEvent( e ); +} + +void KMiniPagerButton::slotDragSwitch() +{ + emit buttonSelected(m_desktop); +} + +void KMiniPagerButton::slotClicked() +{ + emit buttonSelected(m_desktop); +} + +void KMiniPagerButton::rename() +{ + if ( !m_lineEdit ) { + m_lineEdit = new QLineEdit( this ); + connect( m_lineEdit, SIGNAL( returnPressed() ), m_lineEdit, SLOT( hide() ) ); + m_lineEdit->installEventFilter( this ); + } + m_lineEdit->setGeometry( rect() ); + m_lineEdit->setText(m_desktopName); + m_lineEdit->show(); + m_lineEdit->setFocus(); + m_lineEdit->selectAll(); + m_pager->emitRequestFocus(); +} + +void KMiniPagerButton::slotToggled( bool b ) +{ + if ( !b && m_lineEdit ) + { + m_lineEdit->hide(); + } +} + +bool KMiniPagerButton::eventFilter( QObject *o, QEvent * e) +{ + if (o && o == m_lineEdit && + (e->type() == QEvent::FocusOut || e->type() == QEvent::Hide)) + { + m_pager->kwin()->setDesktopName( m_desktop, m_lineEdit->text() ); + m_desktopName = m_lineEdit->text(); + QTimer::singleShot( 0, m_lineEdit, SLOT( deleteLater() ) ); + m_lineEdit = 0; + return true; + } + + return QButton::eventFilter(o, e); +} + +void KMiniPagerButton::updateKickerTip(KickerTip::Data &data) +{ + Task::Dict tasks = TaskManager::the()->tasks(); + Task::Dict::iterator taskEnd = tasks.end(); + uint taskCounter = 0; + uint taskLimiter = 4; + QString lastWindow; + + for (Task::Dict::iterator it = tasks.begin(); it != taskEnd; ++it) + { + if (it.data()->desktop() == m_desktop || it.data()->isOnAllDesktops()) + { + taskCounter++; + if (taskCounter > taskLimiter) + { + lastWindow = it.data()->visibleName(); + continue; + } + + QPixmap winIcon = it.data()->pixmap(); + QString bullet; + + if (winIcon.isNull()) + { + bullet = "•"; + } + else + { + data.mimeFactory->setPixmap(QString::number(taskCounter), winIcon); + bullet = QString("<img src=\"%1\" width=\"%2\" height=\"%3\">").arg(taskCounter).arg(16).arg(16); + } + + QString name = KStringHandler::cPixelSqueeze(it.data()->visibleName(), fontMetrics(), 400); + name = QStyleSheet::escape(name); + if (it.data() == m_currentWindow) + { + data.subtext.append(QString("<br>%1 <u>").arg(bullet)); + data.subtext.append(name).append("</u>"); + } + else + { + data.subtext.append(QString("<br>%1 ").arg(bullet)); + data.subtext.append(name); + } + } + } + + if (taskCounter > taskLimiter) + { + if (taskCounter - taskLimiter == 1) + { + data.subtext.append("<br>• ").append(lastWindow); + } + else + { + data.subtext.append("<br>• <i>") + .append(i18n("and 1 other", "and %n others", taskCounter - taskLimiter)) + .append("</i>"); + } + } + + if (taskCounter > 0) + { + data.subtext.prepend(i18n("One window:", + "%n windows:", + taskCounter)); + } + + data.duration = 4000; + data.icon = DesktopIcon("window_list", KIcon::SizeMedium); + data.message = QStyleSheet::escape(m_desktopName); + data.direction = m_pager->popupDirection(); +} + diff --git a/kicker/applets/minipager/pagerbutton.h b/kicker/applets/minipager/pagerbutton.h new file mode 100644 index 000000000..1547201e0 --- /dev/null +++ b/kicker/applets/minipager/pagerbutton.h @@ -0,0 +1,111 @@ +/***************************************************************** + +Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __MINIPAGERBUTTON_H +#define __MINIPAGERBUTTON_H + +#include <qbutton.h> + +#include "taskmanager.h" +#include "kickertip.h" + +class KPixmap; +class KWinModule; +class KMiniPager; +class KSharedPixmap; +class QLineEdit; + +class KMiniPagerButton : public QButton, public KickerTip::Client +{ + Q_OBJECT +public: + KMiniPagerButton(int desk, bool useViewports, const QPoint& viewport, + KMiniPager *parent=0, const char *name=0); + ~KMiniPagerButton(); + + int desktop() { return m_desktop; } + + QString desktopName() { return m_desktopName; } + void setDesktopName( QString name ) { m_desktopName = name; } + + void rename(); + void backgroundChanged(); + void windowsChanged(); + + bool shouldPaintWindow( KWin::WindowInfo *info ) const; + +signals: + void buttonSelected( int desk ); + void showMenu( const QPoint&, int ); + +protected: + void drawButton(QPainter *); + void enterEvent(QEvent*); + void leaveEvent(QEvent*); + void resizeEvent(QResizeEvent *ev); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + void dragEnterEvent(QDragEnterEvent* e); + void dragLeaveEvent(QDragLeaveEvent* e); + void enabledChange( bool oldEnabled ); + void dropEvent(QDropEvent* e); + + bool eventFilter(QObject*, QEvent*); + void updateKickerTip(KickerTip::Data &data); + +private slots: + void slotToggled(bool); + void slotClicked(); + void slotDragSwitch(); + + void backgroundLoaded( bool loaded ); + +private: + void loadBgPixmap(); + QRect mapGeometryToViewport(const KWin::WindowInfo&) const; + QPoint mapPointToViewport(const QPoint&) const; + + KMiniPager* m_pager; + int m_desktop; + bool m_useViewports; + QString m_desktopName; + QPoint m_viewport; + + QTimer m_updateCompressor; + QTimer m_dragSwitchTimer; + Task::Ptr m_dragging; + + QLineEdit* m_lineEdit; + + KSharedPixmap *m_sharedPixmap; + KPixmap *m_bgPixmap; + static KSharedPixmap *s_commonSharedPixmap; + static KPixmap *s_commonBgPixmap; + bool m_isCommon; + + Task::Ptr m_currentWindow; + bool m_inside; +}; + +#endif diff --git a/kicker/applets/minipager/pagersettings.kcfg b/kicker/applets/minipager/pagersettings.kcfg new file mode 100644 index 000000000..8a26bdc86 --- /dev/null +++ b/kicker/applets/minipager/pagersettings.kcfg @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + + <kcfgfile arg="true"/> + <group name="General"> + <entry key="LabelType" type="Enum"> + <choices> + <choice name="LabelNone"> + <label>None</label> + </choice> + <choice name="LabelName"> + <label>Name</label> + </choice> + <choice name="LabelNumber"> + <label>Number</label> + </choice> + </choices> + <default>LabelNumber</default> + <label>Virtual desktop label type</label> + </entry> + + <entry key="BackgroundType" type="Enum"> + <choices> + <choice name="BgPlain"> + <label>Plain</label> + </choice> + <choice name="BgTransparent"> + <label>Transparent</label> + </choice> + <choice name="BgLive"> + <label>Live</label> + </choice> + </choices> + <default>BgPlain</default> + <label>Virtual desktop background type</label> + </entry> + + <entry name="NumberOfRows" type="Int"> + <label>Number of rows to arrange the desktop previews into</label> + <default>0</default> + <min>0</min> + <max>4</max> + </entry> + + <entry name="Preview" type="Bool"> + <label>Show desktop preview?</label> + <default>true</default> + </entry> + + <entry name="Icons" type="Bool"> + <label>Show window icons in previews?</label> + <default>true</default> + </entry> + </group> +</kcfg> diff --git a/kicker/applets/minipager/pagersettings.kcfgc b/kicker/applets/minipager/pagersettings.kcfgc new file mode 100644 index 000000000..47a1915f4 --- /dev/null +++ b/kicker/applets/minipager/pagersettings.kcfgc @@ -0,0 +1,4 @@ +File=pagersettings.kcfg +Singleton=false +ClassName=PagerSettings +Mutators=true diff --git a/kicker/applets/naughty/Makefile.am b/kicker/applets/naughty/Makefile.am new file mode 100644 index 000000000..533df19c3 --- /dev/null +++ b/kicker/applets/naughty/Makefile.am @@ -0,0 +1,30 @@ +pic_DATA = naughty-happy.png naughty-sad.png +picdir = $(kde_datadir)/naughtyapplet/pics + +INCLUDES = -I$(top_srcdir)/kicker/libkicker $(all_includes) + +kde_module_LTLIBRARIES = naughty_panelapplet.la + +naughty_panelapplet_la_SOURCES = \ + NaughtyProcessMonitor.cpp \ + NaughtyConfigDialog.cpp \ + NaughtyApplet.cpp + +METASOURCES = AUTO + +noinst_HEADERS = \ + NaughtyProcessMonitor.h \ + NaughtyConfigDialog.h \ + NaughtyApplet.h + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = naughtyapplet.desktop + +EXTRA_DIST = $(lnk_DATA) + +naughty_panelapplet_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +naughty_panelapplet_la_LIBADD = ../../libkicker/libkickermain.la $(LIB_KDEUI) $(LIB_KVM) + +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/naughtyapplet.pot + diff --git a/kicker/applets/naughty/NaughtyApplet.cpp b/kicker/applets/naughty/NaughtyApplet.cpp new file mode 100644 index 000000000..c256aa36f --- /dev/null +++ b/kicker/applets/naughty/NaughtyApplet.cpp @@ -0,0 +1,223 @@ +/* + Naughty applet - Runaway process monitor for the KDE panel + + Copyright 2000 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "NaughtyApplet.h" +#include "NaughtyProcessMonitor.h" +#include "NaughtyConfigDialog.h" + +#include <qmessagebox.h> +#include <qtoolbutton.h> +#include <qlayout.h> + +#include <kiconloader.h> +#include <kglobal.h> +#include <kconfig.h> +#include <kaboutapplication.h> +#include <kaboutdata.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kmessagebox.h> +#include <qpushbutton.h> + +extern "C" +{ + KDE_EXPORT KPanelApplet* init(QWidget * parent, const QString & configFile) + { + KGlobal::locale()->insertCatalogue("naughtyapplet"); + + return new NaughtyApplet + ( + configFile, + KPanelApplet::Normal, + KPanelApplet::About | KPanelApplet::Preferences, + parent, + "naughtyapplet" + ); + } +} + +NaughtyApplet::NaughtyApplet +( + const QString & configFile, + Type t, + int actions, + QWidget * parent, + const char * name +) + : KPanelApplet(configFile, t, actions, parent, name) +{ + KGlobal::iconLoader()->addAppDir("naughtyapplet"); + setBackgroundOrigin( AncestorOrigin ); + + button_ = new SimpleButton(this); + button_->setFixedSize(20, 20); + + QVBoxLayout * layout = new QVBoxLayout(this); + layout->addWidget(button_); + + monitor_ = new NaughtyProcessMonitor(2, 20, this); + + connect + ( + button_, SIGNAL(clicked()), + this, SLOT(slotPreferences()) + ); + + connect + ( + monitor_, SIGNAL(runawayProcess(ulong, const QString &)), + this, SLOT(slotWarn(ulong, const QString &)) + ); + + connect + ( + monitor_, SIGNAL(load(uint)), + this, SLOT(slotLoad(uint)) + ); + + loadSettings(); + + monitor_->start(); +} + +NaughtyApplet::~NaughtyApplet() +{ + KGlobal::locale()->removeCatalogue("naughtyapplet"); +} + + void +NaughtyApplet::slotWarn(ulong pid, const QString & name) +{ + if (ignoreList_.contains(name)) + return; + + QString s = i18n("A program called '%1' is slowing down the others " + "on your machine. It may have a bug that is causing " + "this, or it may just be busy.\n" + "Would you like to try to stop the program?"); + + int retval = KMessageBox::warningYesNo(this, s.arg(name), QString::null, i18n("Stop"), i18n("Keep Running")); + + if (KMessageBox::Yes == retval) + monitor_->kill(pid); + else + { + s = i18n("In future, should busy programs called '%1' be ignored?"); + + retval = KMessageBox::questionYesNo(this, s.arg(name), QString::null, i18n("Ignore"), i18n("Do Not Ignore")); + + if (KMessageBox::Yes == retval) + { + ignoreList_.append(name); + config()->writeEntry("IgnoreList", ignoreList_); + config()->sync(); + } + } +} + + int +NaughtyApplet::widthForHeight(int) const +{ + return 20; +} + + int +NaughtyApplet::heightForWidth(int) const +{ + return 20; +} + + void +NaughtyApplet::slotLoad(uint l) +{ + if (l > monitor_->triggerLevel()) + button_->setPixmap(BarIcon("naughty-sad")); + else + button_->setPixmap(BarIcon("naughty-happy")); +} + + void +NaughtyApplet::about() +{ + KAboutData about + ( + "naughtyapplet", + I18N_NOOP("Naughty applet"), + "1.0", + I18N_NOOP("Runaway process catcher"), + KAboutData::License_GPL_V2, + "(C) 2000 Rik Hemsley (rikkus) <rik@kde.org>" + ); + + KAboutApplication a(&about, this); + a.exec(); +} + + void +NaughtyApplet::slotPreferences() +{ + preferences(); +} + + void +NaughtyApplet::preferences() +{ + NaughtyConfigDialog d + ( + ignoreList_, + monitor_->interval(), + monitor_->triggerLevel(), + this + ); + + QDialog::DialogCode retval = QDialog::DialogCode(d.exec()); + + if (QDialog::Accepted == retval) + { + ignoreList_ = d.ignoreList(); + monitor_->setInterval(d.updateInterval()); + monitor_->setTriggerLevel(d.threshold()); + saveSettings(); + } +} + + void +NaughtyApplet::loadSettings() +{ + ignoreList_ = config()->readListEntry("IgnoreList"); + monitor_->setInterval(config()->readUnsignedNumEntry("UpdateInterval", 2)); + monitor_->setTriggerLevel(config()->readUnsignedNumEntry("Threshold", 20)); + + // Add 'X' as a default. + if (ignoreList_.isEmpty() && !config()->hasKey("IgnoreList")) + ignoreList_.append("X"); +} + + void +NaughtyApplet::saveSettings() +{ + config()->writeEntry("IgnoreList", ignoreList_); + config()->writeEntry("UpdateInterval", monitor_->interval()); + config()->writeEntry("Threshold", monitor_->triggerLevel()); + config()->sync(); +} + +#include "NaughtyApplet.moc" + diff --git a/kicker/applets/naughty/NaughtyApplet.h b/kicker/applets/naughty/NaughtyApplet.h new file mode 100644 index 000000000..00df51ec4 --- /dev/null +++ b/kicker/applets/naughty/NaughtyApplet.h @@ -0,0 +1,76 @@ +/* + Naughty applet - Runaway process monitor for the KDE panel + + Copyright 2000 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef NAUGHTY_H +#define NAUGHTY_H + +#include <kpanelapplet.h> +#include <qstringlist.h> + +#include "simplebutton.h" + +class NaughtyProcessMonitor; +class QPushButton; + +class NaughtyApplet : public KPanelApplet +{ + Q_OBJECT + + public: + + NaughtyApplet + ( + const QString & configFile, + Type t = Normal, + int actions = 0, + QWidget * parent = 0, + const char * name = 0 + ); + + ~NaughtyApplet(); + + virtual int widthForHeight(int h) const; + virtual int heightForWidth(int w) const; + + signals: + + void layoutChanged(); + + protected slots: + + void slotWarn(ulong pid, const QString & name); + void slotLoad(uint); + void slotPreferences(); + + protected: + + virtual void about(); + virtual void preferences(); + virtual void loadSettings(); + virtual void saveSettings(); + + private: + + NaughtyProcessMonitor * monitor_; + SimpleButton * button_; + QStringList ignoreList_; +}; + +#endif diff --git a/kicker/applets/naughty/NaughtyConfigDialog.cpp b/kicker/applets/naughty/NaughtyConfigDialog.cpp new file mode 100644 index 000000000..e03a955cc --- /dev/null +++ b/kicker/applets/naughty/NaughtyConfigDialog.cpp @@ -0,0 +1,98 @@ +/* + Naughty applet - Runaway process monitor for the KDE panel + + Copyright 2000 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <keditlistbox.h> +#include <knuminput.h> +#include <klocale.h> +#include <qvbox.h> + +#include "NaughtyConfigDialog.h" +#include "NaughtyConfigDialog.moc" + +NaughtyConfigDialog::NaughtyConfigDialog +( + const QStringList & items, + uint updateInterval, + uint threshold, + QWidget * parent, + const char * name +) + : + KDialogBase + ( + parent, + name, + true, + i18n("Configuration"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, + true + ) +{ + QVBox * v = new QVBox(this); + setMainWidget(v); + + kini_updateInterval_ = new KIntNumInput(updateInterval, v); + kini_threshold_ = new KIntNumInput(kini_updateInterval_, threshold, v); + + kini_updateInterval_ ->setLabel(i18n("&Update interval:")); + kini_threshold_ ->setLabel(i18n("CPU &load threshold:")); + + kini_updateInterval_ ->setRange(1, 20); + kini_threshold_ ->setRange(10, 1000); + + listBox_ = new KEditListBox + (i18n("&Programs to Ignore"), + v, + "naughty config dialog ignore listbox", + false, + KEditListBox::Add | KEditListBox::Remove + ); + + listBox_->insertStringList(items); +} + +NaughtyConfigDialog::~NaughtyConfigDialog() +{ +} + + uint +NaughtyConfigDialog::updateInterval() const +{ + return uint(kini_updateInterval_->value()); +} + + uint +NaughtyConfigDialog::threshold() const +{ + return uint(kini_threshold_->value()); +} + + QStringList +NaughtyConfigDialog::ignoreList() const +{ + QStringList retval; + + for (int i = 0; i < listBox_->count(); i++) + retval << listBox_->text(i); + + return retval; +} + diff --git a/kicker/applets/naughty/NaughtyConfigDialog.h b/kicker/applets/naughty/NaughtyConfigDialog.h new file mode 100644 index 000000000..485cbf14f --- /dev/null +++ b/kicker/applets/naughty/NaughtyConfigDialog.h @@ -0,0 +1,58 @@ +/* + Naughty applet - Runaway process monitor for the KDE panel + + Copyright 2000 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef NAUGHTY_CONFIG_DIALOG_H +#define NAUGHTY_CONFIG_DIALOG_H + +#include <kdialogbase.h> + +class KEditListBox; +class KIntNumInput; + +class NaughtyConfigDialog : public KDialogBase +{ + Q_OBJECT + + public: + + NaughtyConfigDialog + ( + const QStringList & items, + uint interval, + uint threshold, + QWidget * parent = 0, + const char * name = 0 + ); + + ~NaughtyConfigDialog(); + + QStringList ignoreList() const; + uint updateInterval() const; + uint threshold() const; + + private: + + KEditListBox * listBox_; + + KIntNumInput * kini_updateInterval_; + KIntNumInput * kini_threshold_; +}; + +#endif diff --git a/kicker/applets/naughty/NaughtyProcessMonitor.cpp b/kicker/applets/naughty/NaughtyProcessMonitor.cpp new file mode 100644 index 000000000..f9d352902 --- /dev/null +++ b/kicker/applets/naughty/NaughtyProcessMonitor.cpp @@ -0,0 +1,475 @@ +/* + Naughty applet - Runaway process monitor for the KDE panel + + Copyright 2000 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* OpenBSD support by Jean-Yves Burlett <jean-yves@burlett.org> */ + +#ifdef __OpenBSD__ +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/sysctl.h> +#include <sys/ucred.h> +#include <sys/dkstat.h> +#include <stdlib.h> +#endif + +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> + +#include <qfile.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qtextstream.h> +#include <qdir.h> +#include <qtimer.h> +#include <qmap.h> +#include <qdatetime.h> + +#include <klocale.h> + +#include "NaughtyProcessMonitor.h" + +class NaughtyProcessMonitorPrivate +{ + public: + + NaughtyProcessMonitorPrivate() + : interval_(0), + timer_(0), + oldLoad_(0), + triggerLevel_(0) + { + } + + ~NaughtyProcessMonitorPrivate() + { + // Empty. + } + + uint interval_; + QTimer * timer_; + QMap<ulong, uint> loadMap_; + QMap<ulong, uint> scoreMap_; +#ifdef __OpenBSD__ + QMap<ulong, uint> cacheLoadMap_; + QMap<ulong, uid_t> uidMap_; +#endif + uint oldLoad_; + uint triggerLevel_; + + private: + + NaughtyProcessMonitorPrivate(const NaughtyProcessMonitorPrivate &); + + NaughtyProcessMonitorPrivate & operator = + (const NaughtyProcessMonitorPrivate &); +}; + +NaughtyProcessMonitor::NaughtyProcessMonitor + ( + uint interval, + uint triggerLevel, + QObject * parent, + const char * name + ) + : QObject(parent, name) +{ + d = new NaughtyProcessMonitorPrivate; + d->interval_ = interval * 1000; + d->triggerLevel_ = triggerLevel; + d->timer_ = new QTimer(this); + connect(d->timer_, SIGNAL(timeout()), this, SLOT(slotTimeout())); +} + +NaughtyProcessMonitor::~NaughtyProcessMonitor() +{ + delete d; +} + + void +NaughtyProcessMonitor::start() +{ + d->timer_->start(d->interval_, true); +} + + void +NaughtyProcessMonitor::stop() +{ + d->timer_->stop(); +} + + uint +NaughtyProcessMonitor::interval() const +{ + return d->interval_ / 1000; +} + + void +NaughtyProcessMonitor::setInterval(uint i) +{ + stop(); + d->interval_ = i * 1000; + start(); +} + + uint +NaughtyProcessMonitor::triggerLevel() const +{ + return d->triggerLevel_; +} + + void +NaughtyProcessMonitor::setTriggerLevel(uint i) +{ + d->triggerLevel_ = i; +} + + void +NaughtyProcessMonitor::slotTimeout() +{ + uint cpu = cpuLoad(); + + emit(load(cpu)); + + if (cpu > d->triggerLevel_ * (d->interval_ / 1000)) + { + uint load; + QValueList<ulong> l(pidList()); + + for (QValueList<ulong>::ConstIterator it(l.begin()); it != l.end(); ++it) + if (getLoad(*it, load)) + _process(*it, load); + } + + d->timer_->start(d->interval_, true); +} + + void +NaughtyProcessMonitor::_process(ulong pid, uint load) +{ + if (!d->loadMap_.contains(pid)) + { + d->loadMap_.insert(pid, load); + return; + } + + uint oldLoad = d->loadMap_[pid]; + bool misbehaving = (load - oldLoad) > 40 * (d->interval_ / 1000); + bool wasMisbehaving = d->scoreMap_.contains(pid); + + if (misbehaving) + if (wasMisbehaving) + { + d->scoreMap_.replace(pid, d->scoreMap_[pid] + 1); + if (canKill(pid)) + emit(runawayProcess(pid, processName(pid))); + } + else + d->scoreMap_.insert(pid, 1); + else + if (wasMisbehaving) + d->scoreMap_.remove(pid); + + d->loadMap_.replace(pid, load); +} + +// Here begins the set of system-specific methods. + + bool +NaughtyProcessMonitor::canKill(ulong pid) const +{ +#ifdef __linux__ + QFile f("/proc/" + QString::number(pid) + "/status"); + + if (!f.open(IO_ReadOnly)) + return false; + + QTextStream t(&f); + + QString s; + + while (!t.atEnd() && s.left(4) != "Uid:") + s = t.readLine(); + + QStringList l(QStringList::split('\t', s)); + + uint a(l[1].toUInt()); + +// What are these 3 fields for ? Would be nice if the Linux kernel docs +// were complete, eh ? +// uint b(l[2].toUInt()); +// uint c(l[3].toUInt()); +// uint d(l[4].toUInt()); + + return geteuid() == a; +#elif defined(__OpenBSD__) + // simply check if entry exists in the uid map and use it + if (!d->uidMap_.contains(pid)) + return false ; + + return geteuid () == d->uidMap_[pid] ; +#else + Q_UNUSED( pid ); + return false; +#endif +} + + QString +NaughtyProcessMonitor::processName(ulong pid) const +{ +#if defined(__linux__) || defined(__OpenBSD__) +#ifdef __linux__ + QFile f("/proc/" + QString::number(pid) + "/cmdline"); + + if (!f.open(IO_ReadOnly)) + return i18n("Unknown"); + + QCString s; + + while (true) + { + int c = f.getch(); + + // Stop at NUL + if (c == -1 || char(c) == '\0') + break; + else + s += char(c); + } + + // Now strip 'kdeinit:' prefix. + QString unicode(QString::fromLocal8Bit(s)); + +#elif defined(__OpenBSD__) + int mib[4] ; + size_t size ; + char **argv ; + + // fetch argv for the process `pid' + + mib[0] = CTL_KERN ; + mib[1] = KERN_PROC_ARGS ; + mib[2] = pid ; + mib[3] = KERN_PROC_ARGV ; + + // we assume argv[0]'s size will be less than one page + + size = getpagesize () ; + argv = (char **)calloc (size, sizeof (char)) ; + size-- ; // ensure argv is ended by 0 + if (-1 == sysctl (mib, 4, argv, &size, NULL, 0)) { + free (argv) ; + return i18n("Unknown") ; + } + + // Now strip 'kdeinit:' prefix. + QString unicode(QString::fromLocal8Bit(argv[0])); + + free (argv) ; +#endif + + QStringList parts(QStringList::split(' ', unicode)); + + QString processName = parts[0] == "kdeinit:" ? parts[1] : parts[0]; + + int lastSlash = processName.findRev('/'); + + // Get basename, if there's a path. + if (-1 != lastSlash) + processName = processName.mid(lastSlash + 1); + + return processName; + +#else + Q_UNUSED( pid ); + return QString::null; +#endif +} + + uint +NaughtyProcessMonitor::cpuLoad() const +{ +#ifdef __linux__ + QFile f("/proc/stat"); + + if (!f.open(IO_ReadOnly)) + return 0; + + bool forgetThisOne = 0 == d->oldLoad_; + + QTextStream t(&f); + + QString s = t.readLine(); + + QStringList l(QStringList::split(' ', s)); + + uint user = l[1].toUInt(); + uint sys = l[3].toUInt(); + + uint load = user + sys; + uint diff = load - d->oldLoad_; + d->oldLoad_ = load; + + return (forgetThisOne ? 0 : diff); +#elif defined(__OpenBSD__) + int mib[2] ; + long cp_time[CPUSTATES] ; + size_t size ; + uint load, diff ; + bool forgetThisOne = 0 == d->oldLoad_; + + // fetch CPU time statistics + + mib[0] = CTL_KERN ; + mib[1] = KERN_CPTIME ; + + size = CPUSTATES * sizeof(long) ; + + if (-1 == sysctl (mib, 2, cp_time, &size, NULL, 0)) + return 0 ; + + load = cp_time[CP_USER] + cp_time[CP_SYS] ; + diff = load - d->oldLoad_ ; + d->oldLoad_ = load ; + + return (forgetThisOne ? 0 : diff); +#else + return 0; +#endif +} + + QValueList<ulong> +NaughtyProcessMonitor::pidList() const +{ +#ifdef __linux__ + QStringList dl(QDir("/proc").entryList()); + + QValueList<ulong> pl; + + for (QStringList::ConstIterator it(dl.begin()); it != dl.end(); ++it) + if (((*it)[0].isDigit())) + pl << (*it).toUInt(); + + return pl; +#elif defined(__OpenBSD__) + int mib[3] ; + int nprocs = 0, nentries ; + size_t size ; + struct kinfo_proc *kp ; + int i ; + QValueList<ulong> l; + + // fetch number of processes + + mib[0] = CTL_KERN ; + mib[1] = KERN_NPROCS ; + + if (-1 == sysctl (mib, 2, &nprocs, &size, NULL, 0)) + return l ; + + // magic size evaluation ripped from ps + + size = (5 * nprocs * sizeof(struct kinfo_proc)) / 4 ; + kp = (struct kinfo_proc *)calloc (size, sizeof (char)) ; + + // fetch process info + + mib[0] = CTL_KERN ; + mib[1] = KERN_PROC ; + mib[2] = KERN_PROC_ALL ; + + if (-1 == sysctl (mib, 3, kp, &size, NULL, 0)) { + free (kp) ; + return l ; + } + + nentries = size / sizeof (struct kinfo_proc) ; + + // time statistics and euid data are fetched only for processes in + // the pidList, so, instead of doing one sysctl per process for + // getLoad and canKill calls, simply cache the data we already have. + + d->cacheLoadMap_.clear () ; + d->uidMap_.clear () ; + for (i = 0; i < nentries; i++) { + l << (unsigned long) kp[i].kp_proc.p_pid ; + d->cacheLoadMap_.insert (kp[i].kp_proc.p_pid, + (kp[i].kp_proc.p_uticks + + kp[i].kp_proc.p_sticks)) ; + d->uidMap_.insert (kp[i].kp_proc.p_pid, + kp[i].kp_eproc.e_ucred.cr_uid) ; + } + + free (kp) ; + + return l ; +#else + QValueList<ulong> l; + return l; +#endif +} + + bool +NaughtyProcessMonitor::getLoad(ulong pid, uint & load) const +{ +#ifdef __linux__ + QFile f("/proc/" + QString::number(pid) + "/stat"); + + if (!f.open(IO_ReadOnly)) + return false; + + QTextStream t(&f); + + QString line(t.readLine()); + + QStringList fields(QStringList::split(' ', line)); + + uint userTime (fields[13].toUInt()); + uint sysTime (fields[14].toUInt()); + + load = userTime + sysTime; + + return true; +#elif defined(__OpenBSD__) + // use cache + if (!d->cacheLoadMap_.contains(pid)) + return false ; + + load = d->cacheLoadMap_[pid] ; + return true ; +#else + Q_UNUSED( pid ); + Q_UNUSED( load ); + return false; +#endif +} + + bool +NaughtyProcessMonitor::kill(ulong pid) const +{ +#if defined(__linux__) || defined(__OpenBSD__) + return 0 == ::kill(pid, SIGKILL); +#else + Q_UNUSED( pid ); + return false; +#endif +} + +#include "NaughtyProcessMonitor.moc" diff --git a/kicker/applets/naughty/NaughtyProcessMonitor.h b/kicker/applets/naughty/NaughtyProcessMonitor.h new file mode 100644 index 000000000..d7023dbd7 --- /dev/null +++ b/kicker/applets/naughty/NaughtyProcessMonitor.h @@ -0,0 +1,76 @@ +/* + Naughty applet - Runaway process monitor for the KDE panel + + Copyright 2000 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef NAUGHTY_PROCESS_MONITOR_H +#define NAUGHTY_PROCESS_MONITOR_H + +#include <qobject.h> + +class NaughtyProcessMonitorPrivate; + +class NaughtyProcessMonitor : public QObject +{ + Q_OBJECT + + public: + + NaughtyProcessMonitor + ( + uint interval, + uint triggerLevel, + QObject * parent = 0, + const char * name = 0 + ); + + virtual ~NaughtyProcessMonitor(); + + void start(); + void stop(); + + uint triggerLevel() const; + void setTriggerLevel(uint); + uint interval() const; + void setInterval(uint); + + virtual uint cpuLoad() const; + virtual QValueList<ulong> pidList() const; + virtual bool getLoad(ulong pid, uint & load) const; + virtual QString processName(ulong pid) const; + virtual bool canKill(ulong pid) const; + virtual bool kill(ulong pid) const; + + protected slots: + + void slotTimeout(); + + signals: + + void load(uint); + void runawayProcess(ulong pid, const QString & name); + + private: + + void _process(ulong pid, uint load); + + NaughtyProcessMonitorPrivate * d; +}; + +#endif + diff --git a/kicker/applets/naughty/configure.in.in b/kicker/applets/naughty/configure.in.in new file mode 100644 index 000000000..5847780f0 --- /dev/null +++ b/kicker/applets/naughty/configure.in.in @@ -0,0 +1,5 @@ +case "$host" in + *-*-freebsd*) LIB_KVM="-lkvm" ;; + *) LIB_KVM="" ;; +esac +AC_SUBST(LIB_KVM) diff --git a/kicker/applets/naughty/naughty-happy.png b/kicker/applets/naughty/naughty-happy.png Binary files differnew file mode 100644 index 000000000..3200b5270 --- /dev/null +++ b/kicker/applets/naughty/naughty-happy.png diff --git a/kicker/applets/naughty/naughty-sad.png b/kicker/applets/naughty/naughty-sad.png Binary files differnew file mode 100644 index 000000000..9b6541907 --- /dev/null +++ b/kicker/applets/naughty/naughty-sad.png diff --git a/kicker/applets/naughty/naughtyapplet.desktop b/kicker/applets/naughty/naughtyapplet.desktop new file mode 100644 index 000000000..f7cb6a35f --- /dev/null +++ b/kicker/applets/naughty/naughtyapplet.desktop @@ -0,0 +1,131 @@ +[Desktop Entry] +Type=Plugin +Name=Runaway Process Catcher +Name[af]=Weghardloop Proses Vanger +Name[ar]=لاقط الإجرائات الهاربة +Name[az]=İşıək Gedişat Yaxalayıcı +Name[be]=Захоп завіснуўшых працэсаў +Name[bg]=Неуправляеми процеси +Name[bn]=অনিয়ন্ত্রিত প্রসেস প্রহরী +Name[bs]=Hvatač odbjeglih procesa +Name[ca]=Capturador de processos descontrolats +Name[cs]=Odchytávač chybných procesů +Name[csb]=Jachtôrz zagùbionëch procesów +Name[cy]=Arhosydd Prosesau Di-derfyn +Name[da]=Indfanger af løbsk-kørte processer +Name[de]=Beenden unkontrollierter Prozesse +Name[el]=Runaway Έλεγχος Διεργασιών +Name[eo]=Kaptilo por eskapitaj procezoj +Name[es]=Capturador de procesos desbocados +Name[et]=Hanguvate protsesside püüdja +Name[eu]=Ataza eroen harrapatzailea +Name[fa]=گیرندۀ فرآیند فراری +Name[fi]=Karanneiden prosessien kiinniottaja +Name[fr]=Détecteur de processus fous +Name[fy]=Processenmonitor +Name[ga]=Sriantóir na bPróiseas Éalaitheach +Name[gl]=Detector de Procesos Estragados +Name[he]=תופס תהליכים נמלטים +Name[hi]=रनअवे प्रॉसेस कैचर +Name[hr]=Hvatač odbjeglih procesa +Name[hu]=Folyamatszabályozó +Name[is]=Ferlafangari +Name[it]=Rilevatore di processi impazziti +Name[ja]=手に負えないプロセスのキャッチャー +Name[kk]=Жаңылыс процесстерді байқаушы +Name[km]=ឧបករណ៍ចាប់យកដំណើរការដែលមិនអាចបញ្ជាបាន +Name[lo]=ດັກຈັບໂປຣເສດ +Name[lt]=Pabėgusių procesų gaudyklė +Name[lv]=Nevadāmu Procesu Savācējs +Name[mk]=Фаќач на процеси бегалци +Name[mn]=Удирдлагагүй процессуудыг төгсгөх +Name[ms]=Penangkap Proses Luar Kawalan +Name[mt]=Programm biex Jaqbad Proċessi Maħruba +Name[nb]=Fanger løpske prosesser +Name[nds]=Dörgahn Perzessen infangen +Name[ne]=रन वे प्रोसेस क्याचर +Name[nl]=Processenmonitor +Name[nn]=Løpsk prosess-fangar +Name[nso]=Moswari wa Tiragalo yeo e Tshabago +Name[pa]=ਬੇਕਾਬੂ ਕਾਰਜ ਸ਼ਿਕਾਰੀ +Name[pl]=Łowca zagubionych procesów +Name[pt]=Colector de Processos em Fuga +Name[pt_BR]=Captura de processos +Name[ro]=Monitor de procese +Name[ru]=Сторож сбойных процессов +Name[rw]=Mufata Igikorwa Ntagenzura +Name[se]=Báhtaran proseassaid dustejeaddji +Name[sk]=Zachytenie chybných procesov +Name[sl]=Prestrezovalnik pobeglih procesov +Name[sr]=Хватач одбеглих процеса +Name[sr@Latn]=Hvatač odbeglih procesa +Name[sv]=Fånga bortsprungna processer +Name[ta]=ஓடுபாதை செயல் பிடிப்பான் +Name[tg]=Дастгиркунандаи протсессҳои қарорӣ +Name[th]=ดักการจบโปรเซส +Name[tr]=Sorunlu Süreç Yakalayıcı +Name[tt]=Içqınğan Eşlänü Totqıç +Name[uk]=Захоплювач процесів-дезертирів +Name[ven]=TShitenwa tsha Catcher +Name[vi]=Bắt Tiến trình Chạy trốn +Name[wa]=Troûleu d' sot processus +Name[zh_CN]=落跑进程捕捉器 +Name[zh_TW]=失控程式捕捉器 +Name[zu]=Umbambi wenqubo ebalekayo +Comment=Detect and end broken processes which consume too much CPU time +Comment[af]=Spoor stukkende prosesse op wat te veel CPU tyd opneem en stop hulle +Comment[ar]=إكتشف و أنهي الإجرائات المقطوعة اللتي تستهلك الكثير من وقت تشغيل وحدة المعالجة المركزية +Comment[be]=Вызначае і забівае зламаныя працэсы, якія выкарыстоўваюць працэсар надта моцна +Comment[bg]=Намиране и прекратяване на процеси, които консумират твърде много ресурси +Comment[bs]=Otkrij i završi neispravne procese koji zauzimaju previše CPU vremena +Comment[ca]=Detecta i finalitza processos espatllats que consumeixen massa temps de CPU +Comment[cs]=Zjištění a ukončení poškozených procesů ubírajících výkon +Comment[csb]=Òdnajdiwô ë kùńczi niesprôwné procesë, jaczé brëkùją za wiele procesora +Comment[da]=Detekterer og afslutter fejlagtige processer som bruger for meget processortid +Comment[de]=Erkennen und Beenden fehlerhafter Prozesse, die zu viel Rechenzeit verbrauchen +Comment[el]=Ανίχνευση και τερματισμός διεργασιών που καταναλώνουν μεγάλο χρόνο του επεξεργαστή +Comment[eo]=Detekti kaj mortigi difektitajn procezojn konsumante tro da procezilo-tempon +Comment[es]=Detectar procesos rotos que consumen demasiado tiempo del procesador +Comment[et]=Liialt protsessoriaega kulutavate katkiste rakenduste avastamine ja nende töö lõpetamine +Comment[eu]=Detektatu eta amaitu CPU gehiegi erabiltzen ari diren prozesuak +Comment[fa]=آشکارسازی و پایان فرآیندهای قطعشده، که زمان خیلی زیاد واحد پردازش مرکزی را مصرف میکند. +Comment[fi]=Tunnista ja lopeta rikkinäiset prosessit, jotka kuluttavat liikaa laskentatehoa. +Comment[fr]=Détection et arrêt des programmes consommant trop de ressources du processeur +Comment[fy]=Untdekke en stopje alle brutsen prosessen dy tefolle prosessortiid konsumearje +Comment[gl]=Detecta e mata procesos estragados que consumen tempo de CPU +Comment[he]=זהה וסגור תהליכים שצורכים יותר מדי זמן מעבד +Comment[hr]=Otkrivanje i završavanje nedovršenih procesa koji troše previše procesorskog vremena +Comment[hu]=A túl sok processzoridőt lefoglaló folyamatok meghatározása és bezárása +Comment[is]=Uppgötvaðu og slökktu á rofnum ferlum sem taka of mikinn örgjörvatíma +Comment[it]=Trova e termina i processi impazziti che consuma troppo processore +Comment[ja]=CPU 時間を無駄に消費する壊れたプロセスを見つけて終了させる +Comment[kk]=Проңессорды көп жұмсайтын процессарды табу және жою +Comment[km]=រក និងបញ្ចប់ដំណើរការខូចដែលប្រើពេលវេលា CPU ច្រើនពេក +Comment[lt]=Aptikti ir užbaigti sugadintus procesus, kurie suryja per daug CPU laiko +Comment[mk]=Откривање и прекинување на нефункционални процеси што го трошат времето на процесорот +Comment[nb]=Finn og avslutt løpske prosesser som tar for mye prosessorkraft +Comment[nds]=Schaadhaftig Perzessen, de to veel Rekentiet bruukt, opdecken un beennen +Comment[ne]=प्रसस्त CPU समय खपत गर्ने कमजोर प्रक्रिया पत्ता लगाउनुहोस् र अन्त्य गर्नुहोस् +Comment[nl]=Detecteer en stop gebroken processen die teveel processortijd consumeren +Comment[nn]=Finn og avslutt løpske prosessar som tek for myjke prosessorkraft. +Comment[pl]=Wykrywa i kończy niesprawne procesy, które zużywają za dużo procesora +Comment[pt]=Detectar e terminar os processos com problemas que estejam a consumir demasiado CPU +Comment[pt_BR]=Detecta e finaliza processos quebrados que consomem muito tempo de CPU +Comment[ro]=Detectează și termină procese defecte care consumă prea mult CPU +Comment[ru]=Обнаружение и завершение процессов, требующим слишком много времени процессора +Comment[se]=Gávnna jea heaittit reakčanan proseassaid mat geavahit menddo olu CPU-áiggi +Comment[sk]=Zistenie a ukončenie procesov, ktoré spotrebúvajú priveľa času CPU +Comment[sl]=Zaznavanje in pobijanje procesov, ki porabljajo preveč procesorskega časa +Comment[sr]=Детектује и окончава покварене процесе који одузимају превише процесорског времена +Comment[sr@Latn]=Detektuje i okončava pokvarene procese koji oduzimaju previše procesorskog vremena +Comment[sv]=Detekterar och avslutar felaktiga processer som använder för mycket processortid +Comment[th]=ตรวจจับและจบโปรเซสที่เสียหาย ซึ่งใช้เวลาของหน่วยประมวลผลมากเกินไป +Comment[tr]=Sorunlu ve fazla işlemci gücü harcayan programları bulup yokeder +Comment[uk]=Виявлення і припинення процесів, які споживають забагато часу процесора +Comment[vi]=Phát hiện và ngừng các tiến trình gây lãng phí bộ vi xử lý +Comment[wa]=Trove et arestêye les schetés processus k' eployèt trop di tins CPU +Comment[zh_CN]=检测并结束占用太多 CPU 时间的进程 +Comment[zh_TW]=偵測並終結浪費多數 CPU 時間的破損程序 +Icon=runprocesscatcher +X-KDE-Library=naughty_panelapplet +X-KDE-UniqueApplet=true diff --git a/kicker/applets/run/Makefile.am b/kicker/applets/run/Makefile.am new file mode 100644 index 000000000..ec4de4984 --- /dev/null +++ b/kicker/applets/run/Makefile.am @@ -0,0 +1,19 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = run_panelapplet.la + +run_panelapplet_la_SOURCES = runapplet.cpp + +METASOURCES = runapplet.moc +noinst_HEADERS = runapplet.h + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = runapplet.desktop + +EXTRA_DIST = $(lnk_DATA) + +run_panelapplet_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +run_panelapplet_la_LIBADD = $(LIB_KSYCOCA) $(LIB_KDEUI) + +messages: + $(XGETTEXT) *.cpp *.h -o $(podir)/krunapplet.pot diff --git a/kicker/applets/run/runapplet.cpp b/kicker/applets/run/runapplet.cpp new file mode 100644 index 000000000..93bb5d7ad --- /dev/null +++ b/kicker/applets/run/runapplet.cpp @@ -0,0 +1,294 @@ +/***************************************************************** + +Copyright (c) 2000 Matthias Elter <elter@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <qlabel.h> +#include <qfont.h> +#include <qstringlist.h> +#include <qpushbutton.h> +#include <qhbox.h> + +#include <kapplication.h> +#include <kglobal.h> +#include <klocale.h> +#include <kconfig.h> +#include <kcombobox.h> +#include <kurifilter.h> +#include <kdialog.h> +#include <krun.h> +#include <kmessagebox.h> + +#include "runapplet.h" +#include "runapplet.moc" + +extern "C" +{ + KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile) + { + KGlobal::locale()->insertCatalogue("krunapplet"); + return new RunApplet(configFile, KPanelApplet::Stretch, 0, parent, "krunapplet"); + } +} + +RunApplet::RunApplet(const QString& configFile, Type type, int actions, + QWidget *parent, const char *name) + : KPanelApplet(configFile, type, actions, parent, name) +{ + // setBackgroundMode(X11ParentRelative); + setBackgroundOrigin( AncestorOrigin ); + // setup label + _label = new QLabel(i18n("Run command:"), this); + QFont f(_label->font()); + f.setPixelSize(12); +// _label->setBackgroundMode(X11ParentRelative); + _label->setBackgroundOrigin( AncestorOrigin ); + _label->setFixedHeight(14); + _label->setFont(f); + + // setup popup button + _btn = new QPushButton(this); + f = _btn->font(); + f.setPixelSize(12); + _btn->setFont(f); + connect(_btn, SIGNAL(clicked()), SLOT(popup_combo())); + + // setup history combo + _input = new KHistoryCombo(this); + _input->setFocus(); + _input->clearEdit(); + watchForFocus(_input->lineEdit()); + connect(_input, SIGNAL(activated(const QString&)), + SLOT(run_command(const QString&))); + + KConfig *c = config(); + c->setGroup("General"); + + // restore history and completion list + QStringList list = c->readListEntry("Completion list"); + _input->completionObject()->setItems(list); + list = c->readListEntry("History list"); + _input->setHistoryItems(list); + int mode = c->readNumEntry( "CompletionMode", KGlobalSettings::completionMode() ); + _input->setCompletionMode( (KGlobalSettings::Completion) mode ); + + _filterData = new KURIFilterData(); + + _hbox = new QHBox( 0, 0, WStyle_Customize | WType_Popup ); + _hbox->setFixedSize(120, 22); +} + +RunApplet::~RunApplet() +{ + KConfig *c = config(); + c->setGroup("General"); + + // save history and completion list + QStringList list = _input->completionObject()->items(); + c->writeEntry("Completion list", list); + list = _input->historyItems(); + c->writeEntry("History list", list); + c->writeEntry( "CompletionMode", (int) _input->completionMode() ); + c->sync(); + + delete _filterData; + KGlobal::locale()->removeCatalogue("krunapplet"); +} + +void RunApplet::resizeEvent(QResizeEvent*) +{ + if(orientation() == Horizontal) + { + _btn->hide(); + _input->reparent(this, QPoint(0,0), true); + _label->setGeometry(0,0, width(), _label->height()); + + if(height() >= _input->sizeHint().height() + _label->height()) + { + int inputVOffset = height() - _input->sizeHint().height() - 2; + int labelHeight = _label->sizeHint().height(); + _label->setGeometry(0, inputVOffset - labelHeight, + width(), labelHeight); + _input->setGeometry(0, inputVOffset, + width(), _input->sizeHint().height()); + _label->show(); + } + else + { + _label->hide(); + + // make it as high as the combobox naturally wants to be + // but no taller than the panel is! + // don't forget to center it vertically either. + int newHeight = _input->sizeHint().height(); + if (newHeight > height()) + newHeight = height(); + _input->setGeometry(0, (height() - newHeight) / 2, + width(), newHeight); + } + } + else + { + _btn->show(); + _btn->setFixedSize(width(), 22); + _input->reparent( _hbox, QPoint(0, 0), false); + _label->hide(); + } + setButtonText(); +} + +void RunApplet::positionChange(KPanelApplet::Position) +{ + setButtonText(); +} + +void RunApplet::setButtonText() +{ + QString t; + + if (position() == pRight) + { + if (width() >= 42) + t = i18n("< Run"); + else + t = "<"; + } + else + { + if(width() >= 42) + t = i18n("Run >"); + else + t = ">"; + } + + _btn->setText(t); +} + +int RunApplet::widthForHeight(int ) const +{ + return _label->sizeHint().width(); +} + +int RunApplet::heightForWidth(int ) const +{ + return 22; +} + +void RunApplet::popup_combo() +{ + QPoint p; + if (position() == pRight) + p = mapToGlobal(QPoint(-_input->width()-1, 0)); + else + p = mapToGlobal(QPoint(width()+1, 0)); + _hbox->move(p); + _hbox->show(); + _input->setFocus(); +} + +void RunApplet::run_command(const QString& command) +{ + QString exec; + bool focusNeeded = false; + + kapp->propagateSessionManager(); + + _filterData->setData( _input->currentText().stripWhiteSpace() ); + QStringList filters; + filters << "kurisearchfilter" << "kshorturifilter"; + KURIFilter::self()->filterURI( *(_filterData), filters ); + + _input->addToHistory(command); + _input->clearEdit(); + + QString cmd = (_filterData->uri().isLocalFile() ? _filterData->uri().path():_filterData->uri().url()); + + // Nothing interesting. Quit! + if ( cmd.isEmpty() ){ + KMessageBox::sorry(0L, i18n("You have to enter a command to execute " + "or a URL to be opened first.")); + focusNeeded = true; + goto hide; + } + else if (cmd == "logout") + { + bool shutdown = kapp->requestShutDown(); + if( !shutdown ) + { + // This i18n string is in kdesktop/desktop.cc as well. Maybe we should DCOP to kdesktop instead ? + KMessageBox::error( 0, i18n("Unable to log out properly.\nThe session manager cannot " + "be contacted. You can try to force a shutdown by pressing " + "Ctrl+Alt+Backspace. Note, however, that your current " + "session will not be saved with a forced shutdown." ) ); + focusNeeded = true; + } + goto hide; + } + else + { + switch( _filterData->uriType() ) + { + case KURIFilterData::LOCAL_FILE: + case KURIFilterData::LOCAL_DIR: + case KURIFilterData::NET_PROTOCOL: + case KURIFilterData::HELP: + { + (void) new KRun( _filterData->uri() ); + goto hide; + } + case KURIFilterData::EXECUTABLE: + case KURIFilterData::SHELL: + { + exec = cmd; + if( _filterData->hasArgsAndOptions() ) + cmd += _filterData->argsAndOptions(); + break; + } + case KURIFilterData::UNKNOWN: + case KURIFilterData::ERROR: + default: + KMessageBox::sorry( 0, i18n("<qt>The program name or command <b>%1</b>\n" + "cannot be found. Please correct the command\n" + "or URL and try again</qt>").arg( cmd ) ); + _input->removeFromHistory( _input->currentText() ); + focusNeeded = true; + goto hide; + } + } + if (KRun::runCommand( cmd, exec, "" )) + goto hide; + else + { + KMessageBox::sorry( 0, i18n("<qt>Could not run <b>%1</b>.\nPlease correct" + " the command or URL and try again.</qt>").arg( cmd ) ); + _input->removeFromHistory( _input->currentText() ); + focusNeeded = true; + goto hide; + } + + needsFocus(focusNeeded); + return; + + hide: + if (orientation() == Vertical) + _hbox->hide(); + needsFocus(focusNeeded); +} diff --git a/kicker/applets/run/runapplet.desktop b/kicker/applets/run/runapplet.desktop new file mode 100644 index 000000000..66937ccae --- /dev/null +++ b/kicker/applets/run/runapplet.desktop @@ -0,0 +1,130 @@ +[Desktop Entry] +Type=Plugin +Name=Run Command +Name[af]=Hardloop Opdrag +Name[ar]=تنفيذ الأمر +Name[be]=Выканаць праграму +Name[bg]=Изпълнение на команда +Name[bn]=কমান্ড চালাও +Name[br]=Seveniñ ur Goulev +Name[bs]=Izvrši naredbu +Name[ca]=Executa un comandament +Name[cs]=Spustit příkaz +Name[csb]=Zrëszënié pòlétu +Name[cy]=Rhedeg Gorchymyn +Name[da]=Kør kommando +Name[de]=Befehl ausführen +Name[el]=Εκτέλεση εντολής +Name[eo]=Lanĉu komandon +Name[es]=Ejecutar una orden +Name[et]=Käsu käivitamine +Name[eu]=Exekutatu komandoa +Name[fa]=اجرای فرمان +Name[fi]=Suorita komento +Name[fr]=Lancer une commande +Name[fy]=kommando útfiere +Name[ga]=Rith Ordú +Name[gl]=Executar Comando +Name[he]=הפעלת פקודה +Name[hi]=कमांड चलाएँ +Name[hr]=Pokreni naredbu +Name[hu]=Parancs végrehajtása +Name[is]=Keyra skipun +Name[it]=Esegui comando +Name[ja]=コマンドを実行 +Name[ka]=ბრძანების შესრულება +Name[kk]=Команданы орындау +Name[km]=រត់ពាក្យបញ្ជា +Name[ko]=Penguin Command +Name[lt]=Paleisti komandą +Name[lv]=Darbināt komandu +Name[mk]=Изврши команда +Name[ms]=Arahan Laksana +Name[nb]=Kjør kommando +Name[nds]=Befehl utföhren +Name[ne]=आदेश चलाउनुहोस् +Name[nl]=Commando uitvoeren +Name[nn]=Køyr kommando +Name[pa]=ਕਮਾਂਡ ਚਲਾਓ +Name[pl]=Uruchomienie polecenia +Name[pt]=Executar um Comando +Name[pt_BR]=Executar Comando +Name[ro]=Execută comanda +Name[ru]=Выполнить команду +Name[rw]=Gutangiza Ibwiriza +Name[se]=Vuoje gohččuma +Name[sk]=Vykonať príkaz +Name[sl]=Poženi ukaz +Name[sr]=Покретање наредбе +Name[sr@Latn]=Pokretanje naredbe +Name[sv]=Kör kommando +Name[ta]=இயக்க கட்டளை +Name[tg]=Иҷрои фармон +Name[th]=ใช้งานคำสั่ง +Name[tr]=Komut Çalıştır +Name[tt]=Boyırıq Eşlätü +Name[uk]=Запуск команди +Name[uz]=Buyruqni bajarish +Name[uz@cyrillic]=Буйруқни бажариш +Name[vi]=Gõ lệnh +Name[wa]=Enonder ene comande +Name[zh_CN]=运行命令 +Name[zh_TW]=執行命令 +Comment=Launch single commands without a terminal window +Comment[af]=Lanseer enkel opdragte sonder 'n terminaal venster +Comment[ar]=أطلق أوامر وحيدة بدون الحاجة إلى نافذة المطراف +Comment[be]=Запускае асобныя каманды без тэрмінальнага акна +Comment[bg]=Стартиране на команда без да има нужда от терминален прозорец +Comment[bn]=টার্মিনাল উইণ্ডো ছাড়াই একটি কমান্ড চালান +Comment[bs]=Izvršite pojedinačne naredbe bez prozora terminala +Comment[ca]=Engega ordres sense una finestra de terminal +Comment[cs]=Spouštění jednotlivých příkazů bez terminálového okna +Comment[csb]=Zrëszanié pòjedińczëch pòlétów kònsolë bez òtmëkaniô òkna terminala +Comment[da]=Start enkelte kommandoer uden et terminalvindue +Comment[de]=Ausführen einzelner Kommandos ohne Terminalfenster +Comment[el]=Εκτέλεση εντολών χωρίς ένα παράθυρο τερματικού +Comment[eo]=Lanĉi unuopajn komandojn sen terminala fenestro +Comment[es]=Lanzar órdenes individuales sin ventana de terminal +Comment[et]=Üksikute käskude käivitamine terminali abita +Comment[eu]=Abiarazi komandoak terminal leihorik gabe +Comment[fa]=راهاندازی فرمانهای تک بدون پنجرۀ پایانه +Comment[fi]=Käynnistä yksittäisiä komentoja ilman pääteikkunaa. +Comment[fr]=Lancer des commandes simples sans fenêtre de terminal +Comment[fy]=Fier losse kommando's út sûnder in terminalfinster +Comment[ga]=Rith orduithe aonair gan fhuinneog theirminéil +Comment[gl]=Executa comandos individuais sen usar unha terminal +Comment[he]=הפעל פקודות פשוטות ללא חלון מסוף +Comment[hr]=Pokretanje pojedinih naredbi bez terminalskog prozora +Comment[hu]=Parancs kiadása parancsértelmező ablak nélkül +Comment[is]=Keyrðu einstakar skipanir án skeljaglugga +Comment[it]=Lancia singoli comandi senza una finestra di terminale +Comment[ja]=ターミナルウィンドウを開かずに一つのコマンドを実行 +Comment[kk]=Болек командаларды терминал терезесінен тыс жегу +Comment[km]=បើកពាក្យបញ្ជាតែមួយ ដោយគ្មានបង្អួចស្ថានីយ +Comment[lt]=Vykdykite pavienes komandas ne terminalo lange +Comment[mk]=Стартување на единечни команди без терминалски прозорец +Comment[nb]=Kjør en enkelt kommando uten et skall +Comment[nds]=Enkel Befehlen ahn Terminalfinster starten +Comment[ne]=टर्मिनल सञ्झ्याल बिना एकल आदेश सुरुआत गर्नुहोस् +Comment[nl]=Voer losse commando's uit zonder een terminalvenster +Comment[nn]=Køyr ein enkelt kommando utan eit skal. +Comment[pl]=Uruchamianie pojedynczych poleceń konsoli bez otwierania okna terminala +Comment[pt]=Lançar comandos simples sem uma janela de terminal +Comment[pt_BR]=Abre comandos digitados sem a necessidade de uma janela de terminal +Comment[ro]=Lansează comenzi fără o fereastră de terminal +Comment[ru]=Выполнение отдельной команды без окна терминала +Comment[se]=Vuoje oktonas gohččomiid terminálaláse haga +Comment[sk]=Spustiť príkaz bez okna terminálu +Comment[sl]=Poganjanje posameznih ukazov brez okna terminala +Comment[sr]=Покрените једноструке наредбе без терминалског прозора +Comment[sr@Latn]=Pokrenite jednostruke naredbe bez terminalskog prozora +Comment[sv]=Starta enstaka kommandon utan ett terminalfönster +Comment[th]=เรียกใช้งานคำสั่งเดี่ยวๆ โดยไม่ต้องเข้าหน้าต่างเทอร์มินัล +Comment[uk]=Запуск окремих команд без вікна термінала +Comment[vi]=Chạy một lệnh đơn mà không cần mở một thiết bị đầu cuối +Comment[wa]=Lancî ene comande seule sins terminå +Comment[zh_CN]=调用单条命令而无须使用终端窗口 +Comment[zh_TW]=不使用終端機視窗而送出單行指令 +Icon=exec +X-KDE-Library=run_panelapplet +X-KDE-UniqueApplet=true diff --git a/kicker/applets/run/runapplet.h b/kicker/applets/run/runapplet.h new file mode 100644 index 000000000..ded8fc398 --- /dev/null +++ b/kicker/applets/run/runapplet.h @@ -0,0 +1,65 @@ +/***************************************************************** + +Copyright (c) 2000 Matthias Elter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __runapplet_h__ +#define __runapplet_h__ + +#include <qstring.h> +#include <kpanelapplet.h> + +class QLabel; +class QHBox; +class QPushButton; +class KHistoryCombo; +class KURIFilterData; + +class RunApplet : public KPanelApplet +{ + Q_OBJECT + +public: + RunApplet(const QString& configFile, Type t = Stretch, int actions = 0, + QWidget *parent = 0, const char *name = 0); + virtual ~RunApplet(); + + int widthForHeight(int height) const; + int heightForWidth(int width) const; + +protected: + void resizeEvent(QResizeEvent*); + void positionChange(KPanelApplet::Position); + +protected slots: + void run_command(const QString&); + void popup_combo(); + void setButtonText(); + +private: + KHistoryCombo *_input; + KURIFilterData *_filterData; + QLabel *_label; + QPushButton *_btn; + QHBox *_hbox; +}; + +#endif diff --git a/kicker/applets/swallow/Makefile.am b/kicker/applets/swallow/Makefile.am new file mode 100644 index 000000000..1b88a0c10 --- /dev/null +++ b/kicker/applets/swallow/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = swallow_panelapplet.la + +swallow_panelapplet_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +swallow_panelapplet_la_LIBADD = $(LIB_KDEUI) $(LIB_KIO) + +swallow_panelapplet_la_SOURCES = swallow.cpp prefwidgetbase.ui prefwidget.cpp + +noinst_HEADERS = swallow.h prefwidget.h prefwidgetbase.h + +swallow_panelapplet_la_METASOURCES = AUTO + +applnk_DATA = swallowapplet.desktop +applnkdir = $(kde_datadir)/kicker/applets + +#messages: rc.cpp +# $(XGETTEXT) *.cpp -o $(podir)/swallowapplet.pot diff --git a/kicker/applets/swallow/prefwidget.cpp b/kicker/applets/swallow/prefwidget.cpp new file mode 100644 index 000000000..2e443a6b2 --- /dev/null +++ b/kicker/applets/swallow/prefwidget.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2001 Daniel Molkentin <molkentin@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software + */ + +#include <keditlistbox.h> + +#include "prefwidget.h" + +PreferencesWidget::PreferencesWidget( SwallowCommandList *swc, QWidget *parent ) + : PreferencesWidgetBase(parent) +{ + + SwallowCommandListIterator it( *swc ); + SwallowCommand *currentCL; + while ( ( currentCL = it.current() ) != 0 ) + { + ++it; + klebDockApps->insertItem( currentCL->title ); + } +} +/* +PreferencesWidget::~PreferencesWidget() +{ +} +*/ +#include "prefwidget.moc" diff --git a/kicker/applets/swallow/prefwidget.h b/kicker/applets/swallow/prefwidget.h new file mode 100644 index 000000000..76ecce35e --- /dev/null +++ b/kicker/applets/swallow/prefwidget.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2001 Daniel Molkentin <molkentin@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software + */ + +#ifndef PREFWIDGET_H +#define PREFWIDGET_H + +#include "prefwidgetbase.h" +#include "swallow.h" + +class PreferencesWidget : public PreferencesWidgetBase +{ + Q_OBJECT + +public: + PreferencesWidget( SwallowCommandList* swc, QWidget* parent = 0 ); +// ~PreferencesWidget(); + +}; + +#endif diff --git a/kicker/applets/swallow/prefwidgetbase.ui b/kicker/applets/swallow/prefwidgetbase.ui new file mode 100644 index 000000000..bd2673efa --- /dev/null +++ b/kicker/applets/swallow/prefwidgetbase.ui @@ -0,0 +1,134 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>PreferencesWidgetBase</class> +<widget class="QDialog"> + <property name="name"> + <cstring>PreferencesWidgetBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>409</width> + <height>366</height> + </rect> + </property> + <property name="caption"> + <string>AppDock Preferences</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton" row="3" column="2"> + <property name="name"> + <cstring>PushButton1</cstring> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="on"> + <bool>false</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="KEditListBox" row="1" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>klebDockApps</cstring> + </property> + <property name="title"> + <string>Applications to Dock</string> + </property> + </widget> + <widget class="QGroupBox" row="2" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>GroupBox1</cstring> + </property> + <property name="title"> + <string>Entry Details</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="text"> + <string>&Name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>leName</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="text"> + <string>Command &line:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>urlCommandLine</cstring> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>leName</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Specify a short name for the application.</string> + </property> + </widget> + <widget class="KURLRequester" row="1" column="1"> + <property name="name"> + <cstring>urlCommandLine</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Specify a path to the application. This may include startup parameters. You can use the folder icon to select the application.</string> + </property> + </widget> + </grid> + </widget> + <widget class="QPushButton" row="3" column="3"> + <property name="name"> + <cstring>PushButton2</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + </widget> + <spacer row="3" column="1"> + <property name="name"> + <cstring>Spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> +</widget> +<includes> + <include location="local" impldecl="in implementation">kdialog.h</include> +</includes> +<layoutdefaults spacing="3" margin="6"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +<includehints> + <includehint>keditlistbox.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/kicker/applets/swallow/swallow.cpp b/kicker/applets/swallow/swallow.cpp new file mode 100644 index 000000000..b1224986b --- /dev/null +++ b/kicker/applets/swallow/swallow.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2000 Matthias Elter <elter@kde.org> + * 2000 Carsten Pfeiffer <pfeiffer@kde.org> + * based on keyes (C) 1999 by Jerome Tollet <tollet@magic.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software + */ + + +#include <stdlib.h> + +#include <qlayout.h> +#include <qstringlist.h> + +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kprocess.h> +#include <kshell.h> +#include <kwin.h> +#include <kwinmodule.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include "swallow.h" +#include "prefwidget.h" + +template class QPtrList<SwallowApp>; +typedef QPtrListIterator<SwallowApp> SwallowAppListIterator; +template class QPtrList<SwallowCommand>; + + +// init static variables +SwallowAppList * SwallowApplet::appList = 0L; +SwallowAppList * SwallowApplet::embeddedList = 0L; +KWinModule * SwallowApplet::wModule = 0L; +SwallowApplet * SwallowApplet::self = 0L; + +extern "C" +{ + KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile) { + return new SwallowApplet(configFile, parent, "kswallow applet"); + } +} + + +SwallowApplet::SwallowApplet( const QString& configFile, + QWidget *parent, const char *name ) + : KPanelApplet( configFile, Normal, Preferences, parent, name ) +{ + resize( 30, 30 ); + kdDebug() << "**** constructing swallow applet (" << configFile << ") ****" << endl; + self = this; + m_swcList = new SwallowCommandList; + m_swcList->setAutoDelete( true ); + wModule = new KWinModule(this); + embeddedList = new SwallowAppList; + embeddedList->setAutoDelete( false ); + appList = new SwallowAppList; + appList->setAutoDelete( true ); + + QBoxLayout::Direction d = (orientation() == Horizontal) ? + QBoxLayout::LeftToRight : QBoxLayout::TopToBottom; + m_layout = new QBoxLayout( this, d, 0, 2 ); // make stretch configurable? + m_layout->setAutoAdd( false ); + + // read the config file and start all the configured apps + createApps( readConfig() ); + + if ( appList->count() == 0 ) { + if ( KMessageBox::questionYesNo(0L, i18n("There is no swallowed application, " + "do you want to configure or quit?"), i18n("No Swallowed Application"), + KGuiItem(i18n("Configure"),"configure"), KStdGuiItem::quit()) == KMessageBox::Yes ) + preferences(); + else { + delete this; + ::exit(0); + } + } + + emit updateLayout(); +} + +SwallowApplet::~SwallowApplet() +{ + kdDebug() << "********************** DELETING ************************" << endl; + + delete m_swcList; + delete embeddedList; + delete appList; + delete wModule; + wModule = 0L; +} + + +SwallowCommandList* SwallowApplet::readConfig() +{ + m_swcList->clear(); + KConfig *kc = config(); + + kc->setGroup("General"); + int count = kc->readNumEntry("Number of apps"); + kdDebug() << "*** Registered " << count << " App(s) to be swallow'ed!" << endl; + QString group = "SwallowApp %1"; + QString title, cmd; + ushort errors = 0; + SwallowCommand *swc = 0L; + + for ( int i = 1; i <= count; i++ ) { + kc->setGroup( group.arg(i) ); + cmd = kc->readPathEntry("Commandline"); + title = kc->readEntry("Window title"); + kdDebug() << "*** Found Entry: Cmd-Line: " << cmd << " Window-Title: " << title << endl; + + if ( !cmd.isEmpty() && !title.isEmpty() ) { + swc = new SwallowCommand; + swc->cmdline = cmd; + swc->title = title; + m_swcList->append( swc ); + } + // remember items with non-null cmdline or title, + // discard items with empty cmdline and empty title + else if ( !(cmd.isEmpty() && title.isEmpty()) ) + errors++; + } + + if ( errors > 0 ) { + QString entry = (errors == 1) ? i18n("entry") : i18n("entries"); + if ( KMessageBox::questionYesNo(0L, i18n("I found %1 invalid/incomplete %2\nin the configuration file.\n\nBoth the window title and the commandline\n of the to be swallowed application\nare required.\n\n.Do you want to correct this?").arg(errors).arg(entry), i18n("Configuration Error"),i18n("Correct"),i18n("Ignore Error")) == KMessageBox::Yes) + preferences(); + } + + return m_swcList; +} + + +void SwallowApplet::createApps( SwallowCommandList* list ) +{ + SwallowApp *app = 0L; + + SwallowCommandListIterator it( *list ); + while ( (it.current()) ) { + app = new SwallowApp( it.current(), this ); + app->hide(); + connect( app, SIGNAL( embedded(SwallowApp *)), + SLOT( embedded(SwallowApp *))); + appList->append( app ); + ++it; + kapp->processEvents(); + } + + m_layout->activate(); +} + + +void SwallowApplet::embedded( SwallowApp *app ) +{ + kdDebug() << " -> embedding " << app << ", current size is: " << width() << ", " << height() << endl; + if ( orientation() == Horizontal ) + app->resize(height() * app->sizeRatio(), height() ); + else + app->resize(width(), width() * app->sizeRatio()); + + kdDebug() << "--> ratio: " << app->sizeRatio() << endl; + kdDebug() << "**** " << app << " is embedded now, with (" << app->width() << ", " << app->height() << ")" << endl; + + disconnect( app, SIGNAL( embedded(SwallowApp *)), + this, SLOT( embedded(SwallowApp *))); + + embeddedList->append( app ); + + if ( orientation() == Horizontal ) + resize( widthForHeight( height() ), height() ); + else + resize( width(), heightForWidth( width() )); + + m_layout->addWidget( app ); + app->show(); + updateGeometry(); + emit updateLayout(); +} + +void SwallowApplet::preferences() +{ + PreferencesWidget *prefs = new PreferencesWidget(m_swcList,this); + prefs->show(); +} + + +int SwallowApplet::widthForHeight(int he) +{ + kdDebug() << "**** width for h: " << he << endl; + int w = embeddedList->isEmpty() ? 30 : 0; + layoutApps(); + SwallowAppListIterator it( *embeddedList ); + while ( it.current() ) { + kdDebug() << "current: " << it.current()->width() << endl; + w += (it.current())->width(); + ++it; + } + + kdDebug() << "**** wfh: " << w << " : count: " << embeddedList->count() << endl; + return w; +} + + +int SwallowApplet::heightForWidth(int) +{ + int h = embeddedList->isEmpty() ? 30 : 0; + layoutApps(); + SwallowAppListIterator it( *embeddedList ); + while ( it.current() ) { + h += (it.current())->height(); + ++it; + } + + kdDebug() << "**** hfw: " << h << endl; + return h; +} + +void SwallowApplet::layoutApps() +{ + if ( KPanelApplet::orientation() == Horizontal ) + m_layout->setDirection( QBoxLayout::LeftToRight ); + else + m_layout->setDirection( QBoxLayout::TopToBottom ); +} + + +void SwallowApplet::removeApplet( SwallowApp *app ) +{ + embeddedList->removeRef( app ); + appList->remove( app ); + emit self->updateLayout(); +} + + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + + +static void parseCommand(KProcess *proc, QString cmd) +{ + int pos; + + cmd += " "; + + pos = cmd.find(' '); + *proc << cmd.left(pos); + cmd.remove(0,pos); + cmd = cmd.stripWhiteSpace(); + *proc << KShell::splitArgs(cmd, KShell::TildeExpand | KShell::AbortOnMeta); +} + + +SwallowApp::SwallowApp(const SwallowCommand *swc, QWidget* parent, + const char* /* name */) + : QXEmbed( parent ) +{ + wh_ratio = 1; + setAutoDelete( false ); + QXEmbed::initialize(); + + winTitle = swc->title; + connect(SwallowApplet::winModule(), SIGNAL(windowAdded(WId)), + this, SLOT(windowAdded(WId))); + + if (!swc->cmdline.isEmpty()) { + KProcess *process = new KProcess; + parseCommand(process, swc->cmdline); + + // move window out of sight + // *process << "-geometry"; + // *process << QString("32x32+%1+%2").arg(kapp->desktop()->width()).arg(kapp->desktop()->height()); + + connect(process, SIGNAL(processExited(KProcess*)), + this, SLOT(processExited(KProcess*))); + + process->start(); + } +} + + +SwallowApp::~SwallowApp() +{ + delete process; +} + + +void SwallowApp::windowAdded(WId win) +{ + // determine title of newly mapped window + XTextProperty nameProp; + XGetWMName(qt_xdisplay(), win, &nameProp); + char **names; + int count; + XTextPropertyToStringList(&nameProp, &names, &count); + if (count < 1) { + XFreeStringList(names); + return; + } + + // is this our client? + if (winTitle == names[0]) { + kdDebug()<< "embedding window with title: "<<winTitle.latin1() << endl; + + QRect r = KWin::windowInfo(win).geometry(); + int h = r.height(); + if ( h == 0 ) h = 1; + wh_ratio = (float) r.width() / (float) h; + kdDebug() << " - - - win is: " << r.width() << ", " << r.height() << endl; + resize( r.width(), r.height() ); + + embed(win); + XReparentWindow(qt_xdisplay(), win, winId(), 0, 0); + + disconnect(SwallowApplet::winModule(), SIGNAL(windowAdded(WId)), + this, SLOT(windowAdded(WId))); + + emit embedded( this ); + } + + XFreeStringList(names); +} + + +void SwallowApp::processExited(KProcess *) +{ + SwallowApplet::removeApplet( this ); // also deletes this app +} + +#include "swallow.moc" diff --git a/kicker/applets/swallow/swallow.h b/kicker/applets/swallow/swallow.h new file mode 100644 index 000000000..8e472f415 --- /dev/null +++ b/kicker/applets/swallow/swallow.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2000 Matthias Hlzer-Klpfel <hoelzer@kde.org> + 2000 Carsten Pfeiffer <pfeiffer@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software + */ + +#ifndef __swallow_h__ +#define __swallow_h__ + + +#include <qevent.h> +#include <qptrlist.h> +#include <qxembed.h> + +#include <kpanelapplet.h> + +class QBoxLayout; +class KConfig; +class KProcess; +class KWinModule; + +class SwallowApp; + +typedef struct _SwallowCommand { + QString cmdline; + QString title; +} SwallowCommand; + +typedef QPtrList<SwallowCommand> SwallowCommandList; +typedef QPtrListIterator<SwallowCommand> SwallowCommandListIterator; +typedef QPtrList<SwallowApp> SwallowAppList; + +class SwallowApplet : public KPanelApplet +{ + Q_OBJECT + +public: + SwallowApplet( const QString& configFile, QWidget *parent, + const char *name = 0L ); + ~SwallowApplet(); + + // returns 0L if we don't have a SwallowApplet object yet, + // but who cares + static KWinModule * winModule() { return wModule; } + static void removeApplet( SwallowApp * ); + +public: // for KPanelApplet + int widthForHeight( int w ); + int heightForWidth( int h ); + + void windowAdded(WId win); + void processExited(KProcess *proc); + +public slots: + virtual void preferences(); + +private slots: + void embedded( SwallowApp * ); + +private: + void layoutApps(); + SwallowCommandList* readConfig(); + void createApps( SwallowCommandList * ); + + + static SwallowApplet *self; + static SwallowAppList *appList; + static SwallowAppList *embeddedList; + static KWinModule *wModule; + + SwallowCommandList * m_swcList; + QBoxLayout *m_layout; + +}; + + +class SwallowApp : public QXEmbed +{ + Q_OBJECT + +public: + SwallowApp( const SwallowCommand * swc, QWidget* parent = 0, + const char* name = 0); + ~SwallowApp(); + + float sizeRatio() const { return wh_ratio; } + +signals: + void embedded( SwallowApp * ); + +protected slots: + void windowAdded(WId win); + void processExited(KProcess *proc); + +private: + KProcess *process; + QString winTitle; + float wh_ratio; + +}; + +#endif // __swallow_h__ diff --git a/kicker/applets/swallow/swallowapplet.desktop b/kicker/applets/swallow/swallowapplet.desktop new file mode 100644 index 000000000..2ea055461 --- /dev/null +++ b/kicker/applets/swallow/swallowapplet.desktop @@ -0,0 +1,142 @@ +[Desktop Entry] +Type=Plugin +Name=Swallow Applet +Name[af]=Sluk Miniprogram +Name[ar]=بريمج Swallow +Name[az]=Batıq Proqramcıq +Name[be]=Аплет праглынання +Name[bn]=সোয়্যালো অ্যাপলেট +Name[bs]=Applet - gutač +Name[ca]=Applet contenidor +Name[cs]=Pohlcovací applet +Name[csb]=Aplet do zanurzaniô jinszëch +Name[cy]=Rhaglennig Llyncu +Name[da]=Swallow-panelprogram +Name[de]=Einbettungsprogramm +Name[el]=Ενσωμάτωση μικροεφαρμογής +Name[eo]=Sistemaplikaĵetejo +Name[es]=Miniaplicación contenedora +Name[et]=Põimimise aplett +Name[eu]=Swallow appleta +Name[fa]=برنامک Swallow +Name[fi]=Upotussovelma +Name[fr]=Applet englobante +Name[fy]=Ynslúte applet +Name[gl]=Applet Swallow +Name[he]=יישומון מעגן +Name[hi]=स्वालो ऐपलेट +Name[hr]=Progutaj aplet +Name[hu]=Elnyelő kisalkalmazás +Name[is]=Gleypi smáforrit +Name[it]=Applet che ingloba +Name[ja]=Swallow アプレット +Name[ka]=მშთანთქავი აპლეტი +Name[kk]=Сіңіру апплеті +Name[km]=អាប់ភ្លេត Swallow +Name[lt]=Swallow priemonė +Name[mk]=Аплет „Голтни“ +Name[mn]=Шигтгэгч-програм +Name[ms]=Aplet Lelayang +Name[mt]=Applet "Swallow" +Name[nb]=Svelge-panelprogram +Name[nds]=Lüttprogramm för't Inbetten +Name[ne]=स्वालो एप्लेट +Name[nl]=Inbeddingsapplet +Name[nn]=Svelge-applet +Name[nso]=Applet ya Mometso +Name[pa]=ਸਵਾਲੋਓ ਐਪਲਿਟ +Name[pl]=Programik do zanurzania innych +Name[pt]='Applet' Swallow +Name[pt_BR]=Mini-aplicativo de integração +Name[ro]=Miniaplicație de înglobare +Name[ru]=Аплет поглощения +Name[rw]=Kwemera Apuleti +Name[se]=Njeallan-prográmmaš +Name[sk]=Pohlcovací applet +Name[sl]=Vstavek z lastovko +Name[sr]=Аплет за гутање +Name[sr@Latn]=Aplet za gutanje +Name[sv]=Uppslukande miniprogram +Name[ta]=உள்வாங்கும் சிறுநிரல் +Name[te]=స్వాలొ అప్లేట్ +Name[tg]=Барномаи қурт доданӣ +Name[tr]=Batık Programcık +Name[tt]=Yotu Applete +Name[uk]=Аплет Swallow +Name[ven]=Apulete ya Swallow +Name[vi]=Tiểu ứng dụng Chim nhạn +Name[wa]=Aplikete avaleuse +Name[zh_CN]=Swallow 小程序 +Name[zh_TW]=Swallow 面板小程式 +Name[zu]=I-Applet yokugwinya +Comment=The swallow panel applet +Comment[af]=Die sluk paneel miniprogram +Comment[az]=Batıq panel +Comment[be]=Аплет праглынання для панэлі +Comment[bn]=সোয়্যালো প্যানেল অ্যাপলেট +Comment[bs]=Applet koji "guta" aplikacije u panel +Comment[ca]=L'applet contenidor del plafó +Comment[cs]=Pohlcovací applet pro panel +Comment[csb]=Aplet dlô panelu jaczi ùsôdzô òbéńdã do zanurzaniô jinszëch +Comment[cy]=Rhaglennig llyncu i'r panel +Comment[da]=Swallow-panelprogrammet +Comment[de]=Leiste zum Einbetten von X-Anwendungen +Comment[el]=Μικροεφαρμογή του πίνακα που "καταπίνει" +Comment[eo]=La panelaplikaĵeto enhavanta la dokitajn programojn +Comment[es]=La miniaplicación del panel contenedora +Comment[et]=Paneelil töötav põimimise aplett +Comment[eu]=Swallow paneleko appleta +Comment[fa]=برنامک تابلوی Swallow +Comment[fi]=Paneelin upotussovelma +Comment[fr]=L'applet englobante du tableau de bord +Comment[fy]=Applet foar it ynlúte fan X-toepassingen +Comment[gl]=O applet swallow para o painel +Comment[he]=יישומון מעגן עבור הלוח +Comment[hi]=स्वालो फलक ऐपलेट +Comment[hr]=Gutač apleta za ploču +Comment[hu]=Egy elnyelő panel-kisalkalmazás +Comment[id]=Aplet panel swallow +Comment[is]=Gleypispjalds smáforritið +Comment[it]=Applet per inglobare le applicazioni nel pannello +Comment[ja]=swallow パネルアプレット +Comment[ka]=პანელის მშთანთქავი აპლეტი +Comment[kk]=Сіңіру панель апплеті +Comment[km]=អាប់ភ្លេតបន្ទះ swallow +Comment[lt]=Swallow pulto priemonė +Comment[mk]=Аплетот „Голтни“ од панелот +Comment[mn]=X-програмуудыг холбогч програм +Comment[ms]=Aplet panel lelayang +Comment[mt]=Applet għall-pannell "swallow" +Comment[nb]=Et panelprogram som svelger andre panelprogrammer +Comment[nds]=Paneel för't Inbetten vun X-Programmen +Comment[ne]=स्वालो प्यानल एप्लेट +Comment[nl]=Applet voor het inbedden van X-toepassingen +Comment[nn]=Svelge-panelappleten +Comment[nso]=Applet ya panel ya mometso +Comment[pa]=ਸਵਾਲੋਓ ਪੈਨਲ ਐਪਲਿਟ +Comment[pl]=Programik dla panelu tworzący przestrzeń do zanurzania innych +Comment[pt]=A 'applet' do painel swallow +Comment[pt_BR]=Mini-aplicativo de integração para o painel +Comment[ro]=Miniaplicație de panou pentru înglobare altor programe +Comment[ru]=Аплет панели поглощения +Comment[rw]=Apuleti y'umwanya kumira +Comment[se]=Njeallan-panelprográmmaš +Comment[sk]=Pohlcovací applet pre panel +Comment[sl]=Vstavek lastovke za na pult +Comment[sr]=Аплет панела за гутање +Comment[sr@Latn]=Aplet panela za gutanje +Comment[sv]=Uppslukande miniprogram för panelen +Comment[ta]=உள்வாங்கும் பலக சிறுநிரல் +Comment[tg]=Барномаи қурт додани сафҳа +Comment[th]=แอพเพล็ต swallow +Comment[tr]=Batık panel +Comment[tt]=Yotuçı taqta applete +Comment[uk]=Аплет панелі swallow +Comment[vi]=Tiểu ứng dụng chạy bảng điều khiển chim nhạn +Comment[wa]=L' aplikete avaleuse do scriftôr +Comment[xh]=I window eneenkcukacha ye swallow applet +Comment[zh_CN]=Swallow 面板小程序 +Comment[zh_TW]=swallow 面板小程式 +Comment[zu]=I-applet lewindi lemininingwane lokugwinya +X-KDE-Library=swallow_panelapplet +X-KDE-UniqueApplet=true diff --git a/kicker/applets/systemtray/Makefile.am b/kicker/applets/systemtray/Makefile.am new file mode 100644 index 000000000..849795a9e --- /dev/null +++ b/kicker/applets/systemtray/Makefile.am @@ -0,0 +1,24 @@ + +INCLUDES = -I$(top_srcdir)/kicker/libkicker $(all_includes) + +kde_module_LTLIBRARIES = systemtray_panelapplet.la + +systemtray_panelapplet_la_SOURCES = systemtrayapplet.cpp systemtrayapplet.skel + +systemtray_panelapplet_la_METASOURCES = AUTO +noinst_HEADERS = systemtrayapplet.h + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = systemtrayapplet.desktop + +EXTRA_DIST = $(lnk_DATA) + +systemtray_panelapplet_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +systemtray_panelapplet_la_LIBADD = ../../libkicker/libkickermain.la $(LIB_KDEUI) $(LIB_KIO) + + +messages: + $(XGETTEXT) *.cpp *.h -o $(podir)/ksystemtrayapplet.pot + +srcdoc: + kdoc -a -p -H -d $$HOME/web/src/ksystemtrayapplet ksystemtrayapplet *.h -lqt -lkdecore -lkdeui -lkfile diff --git a/kicker/applets/systemtray/systemtrayapplet.cpp b/kicker/applets/systemtray/systemtrayapplet.cpp new file mode 100644 index 000000000..8ae502fb4 --- /dev/null +++ b/kicker/applets/systemtray/systemtrayapplet.cpp @@ -0,0 +1,1013 @@ +/***************************************************************** + +Copyright (c) 2000-2001 Matthias Ettrich <ettrich@kde.org> + 2000-2001 Matthias Elter <elter@kde.org> + 2001 Carsten Pfeiffer <pfeiffer@kde.org> + 2001 Martijn Klingens <mklingens@yahoo.com> + 2004 Aaron J. Seigo <aseigo@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <qcursor.h> +#include <qlayout.h> +#include <qpopupmenu.h> +#include <qtimer.h> +#include <qpixmap.h> +#include <qevent.h> +#include <qstyle.h> +#include <qpainter.h> + +#include <dcopclient.h> +#include <kapplication.h> +#include <klocale.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <krun.h> +#include <kwinmodule.h> +#include <kdialogbase.h> +#include <kactionselector.h> +#include <kiconloader.h> +#include <kwin.h> + +#include "simplebutton.h" + +#include "systemtrayapplet.h" +#include "systemtrayapplet.moc" + +#include <X11/Xlib.h> + +#define ICON_MARGIN 1 + +extern "C" +{ + KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile) + { + KGlobal::locale()->insertCatalogue("ksystemtrayapplet"); + return new SystemTrayApplet(configFile, KPanelApplet::Normal, + KPanelApplet::Preferences, parent, "ksystemtrayapplet"); + } +} + +SystemTrayApplet::SystemTrayApplet(const QString& configFile, Type type, int actions, + QWidget *parent, const char *name) + : KPanelApplet(configFile, type, actions, parent, name), + m_showFrame(false), + m_showHidden(false), + m_expandButton(0), + m_settingsDialog(0), + m_iconSelector(0), + m_autoRetractTimer(0), + m_autoRetract(false), + m_iconSize(24), + m_layout(0) +{ + loadSettings(); + + setBackgroundOrigin(AncestorOrigin); + + kwin_module = new KWinModule(this); + + // kApplication notifies us of settings changes. added to support + // disabling of frame effect on mouse hover + kapp->dcopClient()->setNotifications(true); + connectDCOPSignal("kicker", "kicker", "configurationChanged()", "loadSettings()", false); + + QTimer::singleShot(0, this, SLOT(initialize())); +} + +void SystemTrayApplet::initialize() +{ + // register existing tray windows + const QValueList<WId> systemTrayWindows = kwin_module->systemTrayWindows(); + bool existing = false; + for (QValueList<WId>::ConstIterator it = systemTrayWindows.begin(); + it != systemTrayWindows.end(); ++it ) + { + embedWindow(*it, true); + existing = true; + } + + showExpandButton(!m_hiddenWins.isEmpty()); + + if (existing) + { + updateVisibleWins(); + layoutTray(); + } + + // the KWinModule notifies us when tray windows are added or removed + connect( kwin_module, SIGNAL( systemTrayWindowAdded(WId) ), + this, SLOT( systemTrayWindowAdded(WId) ) ); + connect( kwin_module, SIGNAL( systemTrayWindowRemoved(WId) ), + this, SLOT( updateTrayWindows() ) ); + + QCString screenstr; + screenstr.setNum(qt_xscreen()); + QCString trayatom = "_NET_SYSTEM_TRAY_S" + screenstr; + + Display *display = qt_xdisplay(); + + net_system_tray_selection = XInternAtom(display, trayatom, false); + net_system_tray_opcode = XInternAtom(display, "_NET_SYSTEM_TRAY_OPCODE", false); + + // Acquire system tray + XSetSelectionOwner(display, + net_system_tray_selection, + winId(), + CurrentTime); + + WId root = qt_xrootwin(); + + if (XGetSelectionOwner (display, net_system_tray_selection) == winId()) + { + XClientMessageEvent xev; + + xev.type = ClientMessage; + xev.window = root; + + xev.message_type = XInternAtom (display, "MANAGER", False); + xev.format = 32; + xev.data.l[0] = CurrentTime; + xev.data.l[1] = net_system_tray_selection; + xev.data.l[2] = winId(); + xev.data.l[3] = 0; /* manager specific data */ + xev.data.l[4] = 0; /* manager specific data */ + + XSendEvent (display, root, False, StructureNotifyMask, (XEvent *)&xev); + } + + setBackground(); +} + +SystemTrayApplet::~SystemTrayApplet() +{ + for (TrayEmbedList::const_iterator it = m_hiddenWins.constBegin(); + it != m_hiddenWins.constEnd(); + ++it) + { + delete *it; + } + + for (TrayEmbedList::const_iterator it = m_shownWins.constBegin(); + it != m_shownWins.constEnd(); + ++it) + { + delete *it; + } + + KGlobal::locale()->removeCatalogue("ksystemtrayapplet"); +} + +bool SystemTrayApplet::x11Event( XEvent *e ) +{ +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + if ( e->type == ClientMessage ) { + if ( e->xclient.message_type == net_system_tray_opcode && + e->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { + if( isWinManaged( (WId)e->xclient.data.l[2] ) ) // we already manage it + return true; + embedWindow( e->xclient.data.l[2], false ); + layoutTray(); + return true; + } + } + return KPanelApplet::x11Event( e ) ; +} + +void SystemTrayApplet::preferences() +{ + if (m_settingsDialog) + { + m_settingsDialog->show(); + m_settingsDialog->raise(); + return; + } + + m_settingsDialog = new KDialogBase(0, "systrayconfig", + false, i18n("Configure System Tray"), + KDialogBase::Ok | KDialogBase::Apply | KDialogBase::Cancel, + KDialogBase::Ok, true); + m_settingsDialog->resize(450, 400); + connect(m_settingsDialog, SIGNAL(applyClicked()), this, SLOT(applySettings())); + connect(m_settingsDialog, SIGNAL(okClicked()), this, SLOT(applySettings())); + connect(m_settingsDialog, SIGNAL(finished()), this, SLOT(settingsDialogFinished())); + + m_iconSelector = new KActionSelector(m_settingsDialog); + m_iconSelector->setAvailableLabel(i18n("Visible icons:")); + m_iconSelector->setSelectedLabel(i18n("Hidden icons:")); + m_iconSelector->setShowUpDownButtons(false); + m_settingsDialog->setMainWidget(m_iconSelector); + + QListBox *shownListBox = m_iconSelector->availableListBox(); + QListBox *hiddenListBox = m_iconSelector->selectedListBox(); + + TrayEmbedList::const_iterator it = m_shownWins.begin(); + TrayEmbedList::const_iterator itEnd = m_shownWins.end(); + for (; it != itEnd; ++it) + { + QString name = KWin::windowInfo((*it)->embeddedWinId()).name(); + if(!shownListBox->findItem(name, Qt::ExactMatch | Qt::CaseSensitive)) + { + shownListBox->insertItem(KWin::icon((*it)->embeddedWinId(), 22, 22, true), name); + } + } + + it = m_hiddenWins.begin(); + itEnd = m_hiddenWins.end(); + for (; it != itEnd; ++it) + { + QString name = KWin::windowInfo((*it)->embeddedWinId()).name(); + if(!hiddenListBox->findItem(name, Qt::ExactMatch | Qt::CaseSensitive)) + { + hiddenListBox->insertItem(KWin::icon((*it)->embeddedWinId(), 22, 22, true), name); + } + } + + m_settingsDialog->show(); +} + +void SystemTrayApplet::settingsDialogFinished() +{ + m_settingsDialog->delayedDestruct(); + m_settingsDialog = 0; + m_iconSelector = 0; +} + +void SystemTrayApplet::applySettings() +{ + if (!m_iconSelector) + { + return; + } + + KConfig *conf = config(); + conf->setGroup("HiddenTrayIcons"); + QString name; + + // use the following snippet of code someday to implement ordering + // of icons + /* + m_visibleIconList.clear(); + QListBoxItem* item = m_iconSelector->availableListBox()->firstItem(); + for (; item; item = item->next()) + { + m_visibleIconList.append(item->text()); + } + conf->writeEntry("Visible", m_visibleIconList); + selection.clear();*/ + + m_hiddenIconList.clear(); + QListBoxItem* item = m_iconSelector->selectedListBox()->firstItem(); + for (; item; item = item->next()) + { + m_hiddenIconList.append(item->text()); + } + conf->writeEntry("Hidden", m_hiddenIconList); + conf->sync(); + + TrayEmbedList::iterator it = m_shownWins.begin(); + while (it != m_shownWins.end()) + { + if (shouldHide((*it)->embeddedWinId())) + { + m_hiddenWins.append(*it); + it = m_shownWins.erase(it); + } + else + { + ++it; + } + } + + it = m_hiddenWins.begin(); + while (it != m_hiddenWins.end()) + { + if (!shouldHide((*it)->embeddedWinId())) + { + m_shownWins.append(*it); + it = m_hiddenWins.erase(it); + } + else + { + ++it; + } + } + + showExpandButton(!m_hiddenWins.isEmpty()); + + updateVisibleWins(); + layoutTray(); +} + +void SystemTrayApplet::checkAutoRetract() +{ + if (!m_autoRetractTimer) + { + return; + } + + if (!geometry().contains(mapFromGlobal(QCursor::pos()))) + { + m_autoRetractTimer->stop(); + if (m_autoRetract) + { + m_autoRetract = false; + + if (m_showHidden) + { + retract(); + } + } + else + { + m_autoRetract = true; + m_autoRetractTimer->start(2000, true); + } + + } + else + { + m_autoRetract = false; + m_autoRetractTimer->start(250, true); + } +} + +void SystemTrayApplet::showExpandButton(bool show) +{ + if (show) + { + if (!m_expandButton) + { + m_expandButton = new SimpleArrowButton(this); + m_expandButton->installEventFilter(this); + refreshExpandButton(); + + if (orientation() == Vertical) + { + m_expandButton->setFixedSize(width() - 4, + m_expandButton->sizeHint() + .height()); + } + else + { + m_expandButton->setFixedSize(m_expandButton->sizeHint() + .width(), + height() - 4); + } + connect(m_expandButton, SIGNAL(clicked()), + this, SLOT(toggleExpanded())); + + m_autoRetractTimer = new QTimer(this); + connect(m_autoRetractTimer, SIGNAL(timeout()), + this, SLOT(checkAutoRetract())); + } + else + { + refreshExpandButton(); + } + + m_expandButton->show(); + } + else if (m_expandButton) + { + m_expandButton->hide(); + } +} + +void SystemTrayApplet::orientationChange( Orientation /*orientation*/ ) +{ + refreshExpandButton(); +} + +void SystemTrayApplet::loadSettings() +{ + // set our defaults + setFrameStyle(NoFrame); + m_showFrame = false; + + KConfig *conf = config(); + conf->setGroup("General"); + + if (conf->readBoolEntry("ShowPanelFrame", false)) + { + setFrameStyle(Panel | Sunken); + } + + conf->setGroup("HiddenTrayIcons"); + m_hiddenIconList = conf->readListEntry("Hidden"); + + //Note This setting comes from kdeglobal. + conf->setGroup("System Tray"); + m_iconSize = conf->readNumEntry("systrayIconWidth", 22); +} + +void SystemTrayApplet::systemTrayWindowAdded( WId w ) +{ + if (isWinManaged(w)) + { + // we already manage it + return; + } + + embedWindow(w, true); + updateVisibleWins(); + layoutTray(); + + if (m_showFrame && frameStyle() == NoFrame) + { + setFrameStyle(Panel|Sunken); + } +} + +void SystemTrayApplet::embedWindow( WId w, bool kde_tray ) +{ + TrayEmbed* emb = new TrayEmbed(kde_tray, this); + emb->setAutoDelete(false); + + if (kde_tray) + { + static Atom hack_atom = XInternAtom( qt_xdisplay(), "_KDE_SYSTEM_TRAY_EMBEDDING", False ); + XChangeProperty( qt_xdisplay(), w, hack_atom, hack_atom, 32, PropModeReplace, NULL, 0 ); + emb->embed(w); + XDeleteProperty( qt_xdisplay(), w, hack_atom ); + } + else + { + emb->embed(w); + } + + if (emb->embeddedWinId() == 0) // error embedding + { + delete emb; + return; + } + + connect(emb, SIGNAL(embeddedWindowDestroyed()), SLOT(updateTrayWindows())); + emb->getIconSize(m_iconSize); + + if (shouldHide(w)) + { + emb->hide(); + m_hiddenWins.append(emb); + showExpandButton(true); + } + else + { + //emb->hide(); + emb->setBackground(); + emb->show(); + m_shownWins.append(emb); + } +} + +bool SystemTrayApplet::isWinManaged(WId w) +{ + TrayEmbedList::const_iterator lastEmb = m_shownWins.end(); + for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); emb != lastEmb; ++emb) + { + if ((*emb)->embeddedWinId() == w) // we already manage it + { + return true; + } + } + + lastEmb = m_hiddenWins.end(); + for (TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); emb != lastEmb; ++emb) + { + if ((*emb)->embeddedWinId() == w) // we already manage it + { + return true; + } + } + + return false; +} + +bool SystemTrayApplet::shouldHide(WId w) +{ + return m_hiddenIconList.find(KWin::windowInfo(w).name()) != m_hiddenIconList.end(); +} + +void SystemTrayApplet::updateVisibleWins() +{ + TrayEmbedList::const_iterator lastEmb = m_hiddenWins.end(); + TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); + + if (m_showHidden) + { + for (; emb != lastEmb; ++emb) + { + //(*emb)->hide(); + (*emb)->setBackground(); + (*emb)->show(); + } + } + else + { + for (; emb != lastEmb; ++emb) + { + (*emb)->hide(); + } + } +} + +void SystemTrayApplet::toggleExpanded() +{ + if (m_showHidden) + { + retract(); + } + else + { + expand(); + } +} + +void SystemTrayApplet::refreshExpandButton() +{ + if (!m_expandButton) + { + return; + } + + Qt::ArrowType a; + + if (orientation() == Vertical) + a = m_showHidden ? Qt::DownArrow : Qt::UpArrow; + else + a = (m_showHidden ^ kapp->reverseLayout()) ? Qt::RightArrow : Qt::LeftArrow; + + m_expandButton->setArrowType(a); +} + +void SystemTrayApplet::expand() +{ + m_showHidden = true; + refreshExpandButton(); + + updateVisibleWins(); + layoutTray(); + + if (m_autoRetractTimer) + { + m_autoRetractTimer->start(250, true); + } +} + +void SystemTrayApplet::retract() +{ + if (m_autoRetractTimer) + { + m_autoRetractTimer->stop(); + } + + m_showHidden = false; + refreshExpandButton(); + + updateVisibleWins(); + layoutTray(); +} + +void SystemTrayApplet::updateTrayWindows() +{ + TrayEmbedList::iterator emb = m_shownWins.begin(); + while (emb != m_shownWins.end()) + { + WId wid = (*emb)->embeddedWinId(); + if ((wid == 0) || + ((*emb)->kdeTray() && + !kwin_module->systemTrayWindows().contains(wid))) + { + (*emb)->deleteLater(); + emb = m_shownWins.erase(emb); + } + else + { + ++emb; + } + } + + emb = m_hiddenWins.begin(); + while (emb != m_hiddenWins.end()) + { + WId wid = (*emb)->embeddedWinId(); + if ((wid == 0) || + ((*emb)->kdeTray() && + !kwin_module->systemTrayWindows().contains(wid))) + { + (*emb)->deleteLater(); + emb = m_hiddenWins.erase(emb); + } + else + { + ++emb; + } + } + + showExpandButton(!m_hiddenWins.isEmpty()); + updateVisibleWins(); + layoutTray(); +} + +int SystemTrayApplet::maxIconWidth() const +{ + int largest = m_iconSize; + + TrayEmbedList::const_iterator lastEmb = m_shownWins.end(); + for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); emb != lastEmb; ++emb) + { + if (*emb == 0) + { + continue; + } + + int width = (*emb)->width(); + if (width > largest) + { + largest = width; + } + } + + if (m_showHidden) + { + lastEmb = m_hiddenWins.end(); + for (TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); emb != lastEmb; ++emb) + { + int width = (*emb)->width(); + if (width > largest) + { + largest = width; + } + } + } + + return largest; +} + +int SystemTrayApplet::maxIconHeight() const +{ + int largest = m_iconSize; + + TrayEmbedList::const_iterator lastEmb = m_shownWins.end(); + for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); emb != m_shownWins.end(); ++emb) + { + if (*emb == 0) + { + continue; + } + + int height = (*emb)->height(); + if (height > largest) + { + largest = height; + } + } + + if (m_showHidden) + { + lastEmb = m_hiddenWins.end(); + for (TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); emb != m_hiddenWins.end(); ++emb) + { + if (*emb == 0) + { + continue; + } + + int height = (*emb)->height(); + if (height > largest) + { + largest = height; + } + } + } + + return largest; +} + +bool SystemTrayApplet::eventFilter(QObject* watched, QEvent* e) +{ + if (watched == m_expandButton) + { + QPoint p; + if (e->type() == QEvent::ContextMenu) + { + p = static_cast<QContextMenuEvent*>(e)->globalPos(); + } + else if (e->type() == QEvent::MouseButtonPress) + { + QMouseEvent* me = static_cast<QMouseEvent*>(e); + if (me->button() == Qt::RightButton) + { + p = me->globalPos(); + } + } + + if (!p.isNull()) + { + QPopupMenu* contextMenu = new QPopupMenu(this); + contextMenu->insertItem(SmallIcon("configure"), i18n("Configure System Tray..."), + this, SLOT(configure())); + + contextMenu->exec(static_cast<QContextMenuEvent*>(e)->globalPos()); + + delete contextMenu; + return true; + } + } + + return false; +} + +int SystemTrayApplet::widthForHeight(int h) const +{ + if (orientation() == Qt::Vertical) + { + return width(); + } + + int currentHeight = height(); + if (currentHeight != h) + { + SystemTrayApplet* me = const_cast<SystemTrayApplet*>(this); + me->setMinimumSize(0, 0); + me->setMaximumSize(32767, 32767); + me->setFixedHeight(h); + } + + return sizeHint().width(); +} + +int SystemTrayApplet::heightForWidth(int w) const +{ + if (orientation() == Qt::Horizontal) + { + return height(); + } + + int currentWidth = width(); + if (currentWidth != w) + { + SystemTrayApplet* me = const_cast<SystemTrayApplet*>(this); + me->setMinimumSize(0, 0); + me->setMaximumSize(32767, 32767); + me->setFixedWidth(w); + } + + return sizeHint().height(); +} + +void SystemTrayApplet::moveEvent( QMoveEvent* ) +{ + setBackground(); +} + + +void SystemTrayApplet::resizeEvent( QResizeEvent* ) +{ + layoutTray(); + // we need to give ourselves a chance to adjust our size before calling this + QTimer::singleShot(0, this, SIGNAL(updateLayout())); +} + +void SystemTrayApplet::layoutTray() +{ + setUpdatesEnabled(false); + + int iconCount = m_shownWins.count(); + + if (m_showHidden) + { + iconCount += m_hiddenWins.count(); + } + + /* heightWidth = height or width in pixels (depends on orientation()) + * nbrOfLines = number of rows or cols (depends on orientation()) + * line = what line to draw an icon in */ + int i = 0, line, nbrOfLines, heightWidth; + bool showExpandButton = m_expandButton && m_expandButton->isVisibleTo(this); + delete m_layout; + m_layout = new QGridLayout(this, 1, 1, ICON_MARGIN, ICON_MARGIN); + + if (m_expandButton) + { + if (orientation() == Vertical) + { + m_expandButton->setFixedSize(width() - 4, m_expandButton->sizeHint().height()); + } + else + { + m_expandButton->setFixedSize(m_expandButton->sizeHint().width(), height() - 4); + } + } + + // col = column or row, depends on orientation(), + // the opposite direction of line + int col = 0; + + // + // The margin and spacing specified in the layout implies that: + // [-- ICON_MARGIN pixels --] [-- first icon --] [-- ICON_MARGIN pixels --] ... [-- ICON_MARGIN pixels --] [-- last icon --] [-- ICON_MARGIN pixels --] + // + // So, if we say that iconWidth is the icon width plus the ICON_MARGIN pixels spacing, then the available width for the icons + // is the widget width minus ICON_MARGIN pixels margin. Forgetting these ICON_MARGIN pixels broke the layout algorithm in KDE <= 3.5.9. + // + // This fix makes the workarounds in the heightForWidth() and widthForHeight() methods unneeded. + // + + if (orientation() == Vertical) + { + int iconWidth = maxIconWidth() + ICON_MARGIN; // +2 for the margins that implied by the layout + heightWidth = width() - ICON_MARGIN; + // to avoid nbrOfLines=0 we ensure heightWidth >= iconWidth! + heightWidth = heightWidth < iconWidth ? iconWidth : heightWidth; + nbrOfLines = heightWidth / iconWidth; + + if (showExpandButton) + { + m_layout->addMultiCellWidget(m_expandButton, + 0, 0, + 0, nbrOfLines - 1, + Qt::AlignHCenter | Qt::AlignVCenter); + col = 1; + } + + if (m_showHidden) + { + TrayEmbedList::const_iterator lastEmb = m_hiddenWins.end(); + for (TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); + emb != lastEmb; ++emb) + { + line = i % nbrOfLines; + //(*emb)->hide(); + (*emb)->show(); + m_layout->addWidget(*emb, col, line, + Qt::AlignHCenter | Qt::AlignVCenter); + + if ((line + 1) == nbrOfLines) + { + ++col; + } + + ++i; + } + } + + TrayEmbedList::const_iterator lastEmb = m_shownWins.end(); + for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); + emb != lastEmb; ++emb) + { + line = i % nbrOfLines; + //(*emb)->hide(); + (*emb)->show(); + m_layout->addWidget(*emb, col, line, + Qt::AlignHCenter | Qt::AlignVCenter); + + if ((line + 1) == nbrOfLines) + { + ++col; + } + + ++i; + } + } + else // horizontal + { + int iconHeight = maxIconHeight() + ICON_MARGIN; // +2 for the margins that implied by the layout + heightWidth = height() - ICON_MARGIN; + heightWidth = heightWidth < iconHeight ? iconHeight : heightWidth; // to avoid nbrOfLines=0 + nbrOfLines = heightWidth / iconHeight; + + if (showExpandButton) + { + m_layout->addMultiCellWidget(m_expandButton, + 0, nbrOfLines - 1, + 0, 0, + Qt::AlignHCenter | Qt::AlignVCenter); + col = 1; + } + + if (m_showHidden) + { + TrayEmbedList::const_iterator lastEmb = m_hiddenWins.end(); + for (TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); emb != lastEmb; ++emb) + { + line = i % nbrOfLines; + //(*emb)->hide(); + (*emb)->show(); + m_layout->addWidget(*emb, line, col, + Qt::AlignHCenter | Qt::AlignVCenter); + + if ((line + 1) == nbrOfLines) + { + ++col; + } + + ++i; + } + } + + TrayEmbedList::const_iterator lastEmb = m_shownWins.end(); + for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); + emb != lastEmb; ++emb) + { + line = i % nbrOfLines; + //(*emb)->hide(); + (*emb)->show(); + m_layout->addWidget(*emb, line, col, + Qt::AlignHCenter | Qt::AlignVCenter); + + if ((line + 1) == nbrOfLines) + { + ++col; + } + + ++i; + } + } + + setUpdatesEnabled(true); + updateGeometry(); + setBackground(); +} + +void SystemTrayApplet::paletteChange(const QPalette & /* oldPalette */) +{ + setBackground(); +} + +void SystemTrayApplet::setBackground() +{ + TrayEmbedList::const_iterator lastEmb; + + lastEmb = m_shownWins.end(); + for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); emb != lastEmb; ++emb) + (*emb)->setBackground(); + + lastEmb = m_hiddenWins.end(); + for (TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); emb != lastEmb; ++emb) + (*emb)->setBackground(); +} + + +TrayEmbed::TrayEmbed( bool kdeTray, QWidget* parent ) + : QXEmbed( parent ), kde_tray( kdeTray ) +{ + hide(); +} + +void TrayEmbed::getIconSize(int defaultIconSize) +{ + QSize minSize = minimumSizeHint(); + + int width = minSize.width(); + int height = minSize.height(); + + if (width < 1 || width > defaultIconSize) + width = defaultIconSize; + if (height < 1 || height > defaultIconSize) + height = defaultIconSize; + + setFixedSize(width, height); + setBackground(); +} + +void TrayEmbed::setBackground() +{ + const QPixmap *pbg = parentWidget()->backgroundPixmap(); + + if (pbg) + { + QPixmap bg(width(), height()); + bg.fill(parentWidget(), pos()); + setPaletteBackgroundPixmap(bg); + setBackgroundOrigin(WidgetOrigin); + } + else + unsetPalette(); + + if (!isHidden()) + { + XClearArea(x11Display(), embeddedWinId(), 0, 0, 0, 0, True); + } +} + diff --git a/kicker/applets/systemtray/systemtrayapplet.desktop b/kicker/applets/systemtray/systemtrayapplet.desktop new file mode 100644 index 000000000..ab737d83c --- /dev/null +++ b/kicker/applets/systemtray/systemtrayapplet.desktop @@ -0,0 +1,158 @@ +[Desktop Entry] +Type=Plugin +Name=System Tray +Name[af]=Stelsel Laai +Name[ar]=لوحة النظام +Name[az]=Sistem Rəfi +Name[be]=Сістэмны латок +Name[bg]=Системен панел +Name[bn]=সিস্টেম ট্রে +Name[br]=Barlenn ar reizhiad +Name[ca]=Safata del sistema +Name[cs]=Systémová část panelu +Name[csb]=Systemòwi zabiérnik +Name[cy]=Bar Tasgau +Name[da]=Statusfelt +Name[de]=Systemabschnitt der Kontrollleiste +Name[el]=Πλαίσιο συστήματος +Name[eo]=Sistempleto +Name[es]=Bandeja del sistema +Name[et]=Süsteemne dokk +Name[eu]=Sistemaren azpila +Name[fa]=سینی سیستم +Name[fi]=Ilmoitusalue +Name[fr]=Boîte à miniatures +Name[fy]=Systeemfak +Name[ga]=Tráidire an Chórais +Name[gl]=Bandexa do Sistema +Name[he]=מגש מערכת +Name[hi]=तंत्र तश्तरी +Name[hr]=Sistemska traka +Name[hu]=Rendszertálca +Name[id]=Tray Sistem +Name[is]=Smáforritabakki +Name[it]=Vassoio di sistema +Name[ja]=システムトレイ +Name[ka]=სისტემური პანელი +Name[kk]=Жүйелік сөре +Name[km]=ថាសប្រព័ន្ធ +Name[ko]=시스템 트레이 +Name[lo]=ຖາດຂອງລະບົບ +Name[lt]=Sistemos dėklas +Name[lv]=Sistēmas Tekne +Name[mk]=Системска лента +Name[mn]=Удирдах самбарын системийн хэсэг +Name[ms]=Dulang Sistem +Name[mt]=Tray tas-Sistema +Name[nb]=Systemkurv +Name[nds]=Systeemafsnitt vun't Paneel +Name[ne]=प्रणाली ट्रे +Name[nl]=Systeemvak +Name[nn]=Systemtrau +Name[nso]=Tray ya System +Name[oc]=Safata dèu sistemo +Name[pa]=ਸਿਸਟਮ ਟਰੇ +Name[pl]=Tacka systemowa +Name[pt]=Bandeja do Painel +Name[pt_BR]=Ícones do sistema +Name[ro]=Tavă de sistem +Name[ru]=Системный лоток +Name[rw]=Igitwara cya Sisitemu +Name[se]=Vuogádatgárcu +Name[sk]=Systémová lišta +Name[sl]=Sistemska vrstica +Name[sr]=Системска касета +Name[sr@Latn]=Sistemska kaseta +Name[sv]=Systembricka +Name[ta]=சாதன தட்டு +Name[te]=వ్యవస్థ ట్రె +Name[tg]=Сафҳаи идоракунии система +Name[th]=ถาดของระบบ +Name[tr]=Sistem Çekmecesi +Name[tt]=Sistem Tiräse +Name[uk]=Системний лоток +Name[ven]=Thirei ya sistemu +Name[vi]=Khay Hệ thống +Name[wa]=Boesse ås imådjetes sistinme +Name[xh]=Itreyi Yendlela yokusebenza +Name[zh_CN]=系统托盘 +Name[zh_TW]=系統匣 +Name[zu]=Itreyi lesistimu + +Comment=The system tray panel applet +Comment[af]=Die stelsel laai paneel miniprogram +Comment[ar]=بريمج لوحة النظام +Comment[az]=Bildiriş sahəsi panel appleti +Comment[be]=Аплет сістэмнага латка +Comment[bg]=Системен аплет за регистрация на програми и поддържане на иконите им +Comment[bn]=সিস্টেম ট্রে প্যানেল অ্যাপলেট +Comment[br]=Arloadig banell barlenn ar reizhiad +Comment[bs]=Applet za system tray +Comment[ca]=L'applet per al plafó de la safata del sistema +Comment[cs]=Systémová část panelu určená pro applety +Comment[csb]=Aplet systemòwégò zabiérnika dlô panelu +Comment[cy]=Rhaglennig bar tasgau i'r panel +Comment[da]=Statusfelt-panelprogrammet +Comment[de]=Systemabschnitt der Kontrollleiste +Comment[el]=Μικροεφαρμογή του πίνακα για το πλαίσιο συστήματος +Comment[eo]=La sistempleta panelaplikaĵeto +Comment[es]=La bandeja del sistema (miniaplicación del panel) +Comment[et]=Paneelil töötav süsteemse doki aplett +Comment[eu]=Sistemaren azpila (paneleko appleta) +Comment[fa]=برنامک تابلوی سینی سیستم +Comment[fi]=Paneelin ilmoitusalue +Comment[fr]=L'applet de boîte à miniatures du tableau de bord +Comment[fy]=It systeemfak panielapplet +Comment[gl]=A applet do painel coa bandexa do sistema +Comment[he]=יישומון מגש המערכת ללוח +Comment[hi]=तंत्र तश्तरी फलक ऐपलेट +Comment[hr]=Aplet ploče za sistemsku traku +Comment[hu]=A rendszertálca alkalmazás +Comment[id]=Applet panel tray sistem +Comment[is]=Íforrit sem birtir lista yfir forrit sem eru í gangi +Comment[it]=Applet del pannello per il vassoio di sistema +Comment[ja]=システムトレイパネルアプレット +Comment[ka]=სისტემური პანელის აპლეტი +Comment[kk]=Жүйелік сөре панель апплеті +Comment[km]=អាប់ភ្លេតបន្ទះថាសប្រព័ន្ធ +Comment[lo]=ແອບແພັດຖາດຂອງລະບົບພາເນລ +Comment[lt]=Sistemos dėklo pulto priemonė +Comment[lv]=Sistēmas teknes paneļa aplets +Comment[mk]=Панелски аплет од системската лента +Comment[mn]=Удирдах самбарын системийн хэсэг +Comment[ms]=Aplet panel dulang sistem +Comment[mt]=Applet għat-tray tas-sistema +Comment[nb]=Panelprogram for systemkurven +Comment[nds]=Lüttprogramm för den Systeemafsnitt vun't Paneel +Comment[ne]=प्रणाली ट्रे प्यानल एप्लेट +Comment[nl]=De systeemvak paneelapplet +Comment[nn]=Systemtrau-panelapplet +Comment[nso]=Applet ya panel ya tray ya system +Comment[oc]=L'aplet de plafon de la safata dèu sistemo +Comment[pa]=ਸਿਸਟਮ ਟਰੇ ਪੈਨਲ ਐਪਲਿਟ +Comment[pl]=Programik tacki systemowej dla panelu +Comment[pt]=Uma 'applet' com a bandeja do painel +Comment[pt_BR]=Mini-aplicativo de ícones do sistema +Comment[ro]=Miniaplicația tavă de sistem pentru panou +Comment[ru]=Аплет панели системного лотка +Comment[rw]=Apuleti y'umwanya w'igitwara sisitemu +Comment[se]=Vuogádatgárcu-panelprográmmaš +Comment[sk]=Applet Systémová lišta +Comment[sl]=Vstavek za sistemsko vrstico v pultu +Comment[sr]=Аплет системске касете за панел +Comment[sr@Latn]=Aplet sistemske kasete za panel +Comment[sv]=Panelminiprogram för systembricka +Comment[ta]=சாதன தட்டு பலக சிறுநிரல் +Comment[tg]=Барномаи сафҳаи идоракунии системаи сафҳа +Comment[th]=แอพเพล็ตถาดของระบบบนพาเนล +Comment[tr]=Sistem çekmece paneli +Comment[uk]=Аплет системного лотку панелі +Comment[vi]=Tiểu ứng dụng có bảng điều khiển chứa khay hệ thống +Comment[wa]=L' aplikete boesse ås imådjetes sistinme do scriftôr +Comment[xh]=I applet yeqela lenjongo ethile yendlela yetreyi yokusebenza +Comment[zh_CN]=系统托盘小程序 +Comment[zh_TW]=系統匣 (system tray) 面板小程式 +Comment[zu]=I-applet yewindi lemininingwane yetreyi lesistimu +Icon=systemtray +X-KDE-Library=systemtray_panelapplet +X-KDE-UniqueApplet=true diff --git a/kicker/applets/systemtray/systemtrayapplet.h b/kicker/applets/systemtray/systemtrayapplet.h new file mode 100644 index 000000000..7afc18bf9 --- /dev/null +++ b/kicker/applets/systemtray/systemtrayapplet.h @@ -0,0 +1,126 @@ +/***************************************************************** + +Copyright (c) 1996-2001 the kicker authors. See file AUTHORS. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __systemtrayapplet_h__ +#define __systemtrayapplet_h__ + +#include <qvaluevector.h> +#include <qstringlist.h> +#include <qevent.h> +#include <qxembed.h> + +#include <dcopobject.h> +#include <kapplication.h> +#include <kpanelapplet.h> + +#include "simplebutton.h" + +class QGridLayout; +class QTimer; +class KWinModule; +class TrayEmbed; +class KDialogBase; +class KActionSelector; + +class SystemTrayApplet : public KPanelApplet, public DCOPObject +{ + Q_OBJECT + K_DCOP + typedef QValueVector<TrayEmbed*> TrayEmbedList; + +public: + + SystemTrayApplet(const QString& configFile, Type t = Normal, int actions = 0, + QWidget *parent = 0, const char *name = 0); + ~SystemTrayApplet(); + + int widthForHeight(int h) const; + int heightForWidth(int w) const; + int maxIconWidth() const; + int maxIconHeight() const; + + bool eventFilter(QObject* watched, QEvent* e); + +k_dcop: + void loadSettings(); + +protected: + void resizeEvent(QResizeEvent*); + void moveEvent(QMoveEvent *); + bool x11Event( XEvent *e ); + void preferences(); + void orientationChange( Orientation ); + +protected slots: + void initialize(); + void systemTrayWindowAdded( WId ); + void updateTrayWindows(); + void layoutTray(); + void paletteChange(const QPalette & /* oldPalette */); + void toggleExpanded(); + void settingsDialogFinished(); + void applySettings(); + void checkAutoRetract(); + void configure() { preferences(); } + void setBackground(); + +private: + void embedWindow( WId w, bool kde_tray ); + bool isWinManaged( WId w); + bool shouldHide( WId w); + void updateVisibleWins(); + void expand(); + void retract(); + void showExpandButton(bool show); + void refreshExpandButton(); + + TrayEmbedList m_shownWins; + TrayEmbedList m_hiddenWins; + QStringList m_hiddenIconList; + KWinModule *kwin_module; + Atom net_system_tray_selection; + Atom net_system_tray_opcode; + bool m_showFrame; + bool m_showHidden; + SimpleArrowButton *m_expandButton; + KDialogBase* m_settingsDialog; + KActionSelector* m_iconSelector; + QTimer* m_autoRetractTimer; + bool m_autoRetract; + int m_iconSize; + QGridLayout* m_layout; +}; + +class TrayEmbed : public QXEmbed +{ + Q_OBJECT +public: + TrayEmbed( bool kdeTray, QWidget* parent = NULL ); + bool kdeTray() const { return kde_tray; } + void setBackground(); + void getIconSize(int defaultIconSize); +private: + bool kde_tray; +}; + +#endif diff --git a/kicker/applets/taskbar/Makefile.am b/kicker/applets/taskbar/Makefile.am new file mode 100644 index 000000000..35cb337e6 --- /dev/null +++ b/kicker/applets/taskbar/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES = -I$(srcdir)/../../taskbar -I$(srcdir)/../../taskmanager -I$(srcdir)/../../libkicker $(all_includes) + +kde_module_LTLIBRARIES = taskbar_panelapplet.la + +taskbar_panelapplet_la_SOURCES = taskbarapplet.cpp + +taskbar_panelapplet_la_METASOURCES = AUTO +taskbar_panelapplet_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +taskbar_panelapplet_la_LIBADD = $(LIB_KDEUI) ../../taskbar/libtaskbar.la ../../libkicker/libkickermain.la + +noinst_HEADERS = taskbarapplet.h + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = taskbarapplet.desktop + +EXTRA_DIST = $(lnk_DATA) + +messages: + $(XGETTEXT) *.cpp *.h -o $(podir)/ktaskbarapplet.pot + +srcdoc: + kdoc -a -p -H -d $$HOME/web/src/ktaskbarapplet ktaskbarapplet *.h -lqt -lkdecore -lkdeui -lkfile diff --git a/kicker/applets/taskbar/taskbarapplet.cpp b/kicker/applets/taskbar/taskbarapplet.cpp new file mode 100644 index 000000000..dc3428bf8 --- /dev/null +++ b/kicker/applets/taskbar/taskbarapplet.cpp @@ -0,0 +1,126 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <elter@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <qlayout.h> +#include <qpalette.h> + +#include <kapplication.h> +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> + +#include "global.h" + +#include "taskbarcontainer.h" + +#include "taskbarapplet.h" +#include "taskbarapplet.moc" + +extern "C" +{ + KDE_EXPORT KPanelApplet* init( QWidget *parent, const QString& configFile ) + { + // FIXME: what about two taskbars? perhaps this should be inserted just once + KGlobal::locale()->insertCatalogue( "ktaskbarapplet" ); + int options = 0; + if (kapp->authorizeControlModule("kde-kcmtaskbar.desktop")) + options = KPanelApplet::Preferences; + TaskbarApplet *taskbar = new TaskbarApplet( configFile, KPanelApplet::Stretch, + options, parent, "ktaskbarapplet" ); + return taskbar; + } +} + +TaskbarApplet::TaskbarApplet( const QString& configFile, Type type, int actions, + QWidget *parent, const char *name ) + : KPanelApplet( configFile, type, actions, parent, name ) +{ + setBackgroundOrigin( AncestorOrigin ); + QHBoxLayout* layout = new QHBoxLayout( this ); + container = new TaskBarContainer( false, this ); + container->setBackgroundOrigin( AncestorOrigin ); + connect(container, SIGNAL(containerCountChanged()), this, SIGNAL(updateLayout())); + layout->addWidget( container, 1 ); + container->popupDirectionChange(popupDirection()); +} + +TaskbarApplet::~TaskbarApplet() +{ + // FIXME: what about TWO taskbars? + KGlobal::locale()->removeCatalogue( "ktaskbarapplet" ); +} + +int TaskbarApplet::widthForHeight(int h) const +{ + if (orientation() == Qt::Vertical) + { + return width(); + } + + // FIXME KDE4: when either TaskBarContainer or Applet smartens up + // simplify this + KPanelExtension::Position d = orientation() == Qt::Horizontal ? + KPanelExtension::Top : + KPanelExtension::Left; + return container->sizeHint(d, QSize(200, h)).width(); +} + +int TaskbarApplet::heightForWidth(int w) const +{ + if (orientation() == Qt::Horizontal) + { + return height(); + } + + // FIXME KDE4: when either TaskBarContainer or Applet smartens up + // simplify this + KPanelExtension::Position d = orientation() == Qt::Horizontal ? + KPanelExtension::Top : + KPanelExtension::Left; + return container->sizeHint(d, QSize(w, 200)).height(); +} + +void TaskbarApplet::preferences() +{ + container->preferences(); +} + +void TaskbarApplet::orientationChange( Orientation o ) +{ + container->orientationChange( o ); +} + +void TaskbarApplet::popupDirectionChange( Direction d ) +{ + container->popupDirectionChange( d ); +} + +void TaskbarApplet::moveEvent(QMoveEvent *) +{ + container->setBackground(); +} + +void TaskbarApplet::paletteChange(const QPalette &) +{ + container->setBackground(); +} diff --git a/kicker/applets/taskbar/taskbarapplet.desktop b/kicker/applets/taskbar/taskbarapplet.desktop new file mode 100644 index 000000000..d54e39832 --- /dev/null +++ b/kicker/applets/taskbar/taskbarapplet.desktop @@ -0,0 +1,138 @@ +[Desktop Entry] +Type=Plugin +Name=Taskbar +Name[af]=Kasbar +Name[ar]=شريط المهام +Name[az]=Vəzifə Çubuğu +Name[be]=Панэль заданняў +Name[bg]=Панел за задачи +Name[bn]=টাস্কবার +Name[br]=Barrenn dleadoù +Name[ca]=Barra de tasques +Name[cs]=Pruh úloh +Name[csb]=Lëstew dzejaniów +Name[cy]=Bar tasgau +Name[da]=Opgavelinje +Name[de]=Fensterleiste +Name[el]=Γραμμή εργασιών +Name[eo]=Taskostrio +Name[es]=Barra de tareas +Name[et]=Tegumiriba +Name[eu]=Ataza-barra +Name[fa]=میله تکلیف +Name[fi]=Tehtäväpalkki +Name[fr]=Barre des tâches +Name[fy]=Taakbalke +Name[ga]=Tascbharra +Name[gl]=Barra de tarefas +Name[he]=שורת המשימות +Name[hi]=कार्यपट्टी +Name[hr]=Traka zadataka +Name[hu]=Feladatlista +Name[is]=Verkefnaslá +Name[it]=Barra delle applicazioni +Name[ja]=タスクバー +Name[ka]=ამოცანათა პანელი +Name[kk]=Тапсырмалар панелі +Name[km]=របារភារកិច្ច +Name[ko]=작업 표시줄 +Name[lo]=ແຖບຫນ້າຕ່າງງານ +Name[lt]=Užduočių juosta +Name[lv]=Uzdevumjosla +Name[mk]=Лента со програми +Name[mn]=Цонхны самбар +Name[nb]=Oppgavelinje +Name[nds]=Programmbalken +Name[ne]=कार्यपट्टी +Name[nl]=Taakbalk +Name[nn]=Oppgåvelinje +Name[nso]=Bar ya Mosongwana +Name[oc]=Barra de tasques +Name[pa]=ਸੰਦਪੱਟੀ +Name[pl]=Pasek zadań +Name[pt]=Barra de Tarefas +Name[pt_BR]=Barra de tarefas +Name[ro]=Bara de procese +Name[ru]=Панель задач +Name[rw]=Umurongoibikorwa +Name[se]=Bargoholga +Name[sk]=Panel úloh +Name[sl]=Opravilna vrstica +Name[sr]=Трака задатака +Name[sr@Latn]=Traka zadataka +Name[ss]=Ibar yemsebenti +Name[sv]=Aktivitetsfält +Name[ta]=பணிப்பட்டி +Name[tg]=Пайраҳаи вазифа +Name[th]=แถบหน้าต่างงาน +Name[tr]=Görev Çubuğu +Name[tt]=Qoraltirä +Name[uk]=Смужка задач +Name[uz]=Vazifalar paneli +Name[uz@cyrillic]=Вазифалар панели +Name[ven]=Bara ya mushumo +Name[vi]=Thanh tác vụ +Name[wa]=Bår des bouyes +Name[xh]=Ibar yomsebenzi +Name[zh_CN]=任务条 +Name[zh_TW]=工作列 +Name[zu]=Ibha yemisebenzi + +Comment=The default task bar for window management +Comment[af]=Die standaard taak balk vir venster bestuur +Comment[be]=Стандартная панэль заданняў для кіравання вокнамі +Comment[bg]=Системен панел за лентата със задачите +Comment[bn]=উইণ্ডো ব্যবস্থাপনার জন্য ডিফল্ট টাস্ক বার +Comment[bs]=Osnovni taskbar za upravljanje prozorima +Comment[ca]=La barra de tasques per omissió per a la gestió de finestres +Comment[cs]=Výchozí pruh úloh pro správu oken +Comment[csb]=Domëslnô lëstew dzejaniów do sprôwianiô òknama +Comment[da]=Standard-opgavelinje for vindueshåndtering +Comment[de]=Standardmäßiger Bereich für offene Fenster in der Kontrollleiste +Comment[el]=Η προκαθορισμένη γραμμή εργασιών για τη διαχείριση των παραθύρων +Comment[eo]=La defaŭlta taskostrio por fenestroadministrado. +Comment[es]=La barra de tareas predeterminada para gestionar las ventanas +Comment[et]=Vaikimisi kasutatav tegumiriba akende halduseks +Comment[eu]=Ataza-barra lehenetsia leihoen kudeaketarako +Comment[fa]=میله تکلیف پیشفرض برای مدیریت پنجره +Comment[fi]=Oletustyökalupalkki ikkunoiden hallintaan +Comment[fr]=La barre des tâches gérant les fenêtres +Comment[fy]=De standert taakbalke foar finsterbehear +Comment[gl]=A barra de tarefas por defeito para xestión de fiestras. +Comment[he]=ברירת מחדל של יישומון שורת משימות ללוח +Comment[hr]=Zadana traka zadataka za upravljanje prozorima +Comment[hu]=Az alapértelmezett feladatlista ablakkezeléshez +Comment[is]=Sjálfgefna verkefnasláin fyrir gluggastjórnun +Comment[it]=La barra delle applicazioni per la gestione delle finestre +Comment[ja]=ウィンドウマネージャ用のデフォルトのタスクバー +Comment[ka]=ფანჯრის მართვის ძირითადი პულტი +Comment[kk]=Терезелерді басқару әдетті тапсырмалар панелі +Comment[km]=របារភារកិច្ចលំនាំដើម សម្រាប់គ្រប់គ្រងបង្អួច +Comment[lt]=Numatyta užduočių juostos langų tvarkymo priemonė +Comment[mk]=Стандардната линија со задачи за менаџмент на прозорци +Comment[nb]=Den vanlige oppgavelinja for å behandle vinduer +Comment[nds]=Standard-Programmbalken för de Finsterpleeg +Comment[ne]=सञ्झ्याल व्यवस्थापनका लागि पूर्वनिर्धारित उपकरणपट्टी +Comment[nl]=De standaard taakbalk voor vensterbeheer +Comment[nn]=Den vanlege oppgåvelinja for å handsama vindauge +Comment[pa]=ਮੂਲ ਵੇਹੜੇ ਲਈ ਮੂਲ ਕੰਮ ਪੱਟੀ +Comment[pl]=Domyślny pasek zadań do zarządzania oknami +Comment[pt]=A barra de tarefas por omissão para a gestão de janelas +Comment[pt_BR]=A barra de tarefas padrão para o gerenciamento de janelas. +Comment[ro]=Bara de procese implicită pentru managementul ferestrelor +Comment[ru]=Панель списка задач по умолчанию для управления окнами +Comment[se]=Standárda bargoholga lásegieđaheami várás +Comment[sk]=Prednastavený panel úloh pre správcu okien +Comment[sl]=Privzeta opravilna vrstica za upravljanje z okni +Comment[sr]=Подразумевана трака задатака за управљање прозорима +Comment[sr@Latn]=Podrazumevana traka zadataka za upravljanje prozorima +Comment[sv]=Det förvalda aktivitetsfältet för fönsterhantering +Comment[th]=แอพเพล็ตถาดงานโดยปริยายของพาเนลสำหรับการจัดการหน้าต่าง +Comment[tr]=Pencere yönetimi için öntanımlı görev çubuğu +Comment[uk]=Типова панель задач для керування вікнами +Comment[vi]=Thanh tác vụ mặc định cho trình quản lý cửa sổ +Comment[wa]=Li prémetowe bår di bouyes do manaedjmint d' purnea +Comment[zh_CN]=窗口管理的默认任务栏 +Comment[zh_TW]=預設的視窗管理工作列 +Icon=taskbar +X-KDE-Library=taskbar_panelapplet diff --git a/kicker/applets/taskbar/taskbarapplet.h b/kicker/applets/taskbar/taskbarapplet.h new file mode 100644 index 000000000..f0842baca --- /dev/null +++ b/kicker/applets/taskbar/taskbarapplet.h @@ -0,0 +1,56 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <elter@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __taskbarapplet_h__ +#define __taskbarapplet_h__ + +#include <kpanelapplet.h> + +class TaskBarContainer; +class QPalette; + +class TaskbarApplet : public KPanelApplet +{ + Q_OBJECT + +public: + TaskbarApplet( const QString& configFile, Type t = Normal, int actions = 0, + QWidget *parent = 0, const char *name = 0 ); + ~TaskbarApplet(); + + int widthForHeight( int h ) const; + int heightForWidth( int w ) const; + + void preferences(); + +protected: + void moveEvent(QMoveEvent *); + void popupDirectionChange( Direction ); + void orientationChange( Orientation ); + void paletteChange(const QPalette &); + +private: + TaskBarContainer* container; +}; + +#endif diff --git a/kicker/applets/trash/Makefile.am b/kicker/applets/trash/Makefile.am new file mode 100644 index 000000000..a34f87d1c --- /dev/null +++ b/kicker/applets/trash/Makefile.am @@ -0,0 +1,21 @@ +INCLUDES = -I$(top_srcdir)/libkonq -I$(top_srcdir)/kicker/libkicker $(all_includes) + +kde_module_LTLIBRARIES = trash_panelapplet.la +trash_panelapplet_la_SOURCES = trashbutton.cpp trashapplet.cpp + +METASOURCES = AUTO + +noinst_HEADERS = \ + trashapplet.h trashbutton.h + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = trashapplet.desktop + +EXTRA_DIST = $(lnk_DATA) + +trash_panelapplet_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +trash_panelapplet_la_LIBADD = ../../libkicker/libkickermain.la ../../../libkonq/libkonq.la $(LIB_KDEUI) $(LIB_KIO) $(LIB_KUTILS) + +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/trashapplet.pot + diff --git a/kicker/applets/trash/trashapplet.cpp b/kicker/applets/trash/trashapplet.cpp new file mode 100644 index 000000000..c27c4e281 --- /dev/null +++ b/kicker/applets/trash/trashapplet.cpp @@ -0,0 +1,165 @@ +/* This file is part of the KDE project + Copyright (c) 2004 Kevin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kglobal.h> +#include <klocale.h> +#include <kconfig.h> +#include <kapplication.h> +#include <kaboutdata.h> +#include <kaboutapplication.h> +#include <kdebug.h> +#include <kpopupmenu.h> +#include <kiconloader.h> + +#include "trashapplet.h" + +extern "C" +{ + KDE_EXPORT KPanelApplet* init( QWidget *parent, const QString& configFile) + { + KGlobal::locale()->insertCatalogue("trashapplet"); + return new TrashApplet(configFile, KPanelApplet::Normal, + KPanelApplet::About, parent, "trashapplet"); + } +} + +TrashApplet::TrashApplet(const QString& configFile, Type type, int actions, QWidget *parent, const char *name) + : KPanelApplet(configFile, type, actions, parent, name), mButton(0) +{ + mButton = new TrashButton(this); + + if (!parent) + setBackgroundMode(X11ParentRelative); + + mButton->setPanelPosition(position()); + + setAcceptDrops(true); + + mpDirLister = new KDirLister(); + + connect( mpDirLister, SIGNAL( clear() ), + this, SLOT( slotClear() ) ); + connect( mpDirLister, SIGNAL( completed() ), + this, SLOT( slotCompleted() ) ); + connect( mpDirLister, SIGNAL( deleteItem( KFileItem * ) ), + this, SLOT( slotDeleteItem( KFileItem * ) ) ); + + mpDirLister->openURL("trash:/"); +} + +TrashApplet::~TrashApplet() +{ + // disconnect the dir lister before quitting so as not to crash + // on kicker exit + disconnect( mpDirLister, SIGNAL( clear() ), + this, SLOT( slotClear() ) ); + delete mpDirLister; + KGlobal::locale()->removeCatalogue("trashapplet"); +} + +void TrashApplet::about() +{ + KAboutData data("trashapplet", + I18N_NOOP("Trash Applet"), + "1.0", + I18N_NOOP("\"trash:/\" ioslave frontend applet"), + KAboutData::License_GPL_V2, + "(c) 2004, Kevin Ottens"); + + data.addAuthor("Kevin \'ervin\' Ottens", + I18N_NOOP("Maintainer"), + "ervin ipsquad net", + "http://ervin.ipsquad.net"); + + KAboutApplication dialog(&data); + dialog.exec(); +} + +int TrashApplet::widthForHeight( int height ) const +{ + if ( !mButton ) + { + return height; + } + + return mButton->widthForHeight( height ); +} + +int TrashApplet::heightForWidth( int width ) const +{ + if ( !mButton ) + { + return width; + } + + return mButton->heightForWidth( width ); +} + +void TrashApplet::resizeEvent( QResizeEvent * ) +{ + if (!mButton) + { + return; + } + + int size = 1; + + size = std::max( size, + orientation() == Qt::Vertical ? + mButton->heightForWidth( width() ) : + mButton->widthForHeight( height() ) ); + + + if(orientation() == Vertical) + { + mButton->resize( width(), size ); + } + else + { + mButton->resize( size, height() ); + } +} + +void TrashApplet::slotClear() +{ + kdDebug()<<"MediaApplet::slotClear"<<endl; + + mButton->setItemCount(0); +} + +void TrashApplet::slotCompleted() +{ + mCount = mpDirLister->items(KDirLister::AllItems).count(); + mButton->setItemCount( mCount ); +} + +void TrashApplet::slotDeleteItem(KFileItem *) +{ + mCount--; + mButton->setItemCount( mCount ); +} + + +void TrashApplet::positionChange(Position p) +{ + mButton->setPanelPosition(p); +} + + +#include "trashapplet.moc" diff --git a/kicker/applets/trash/trashapplet.desktop b/kicker/applets/trash/trashapplet.desktop new file mode 100644 index 000000000..f1c1eff0a --- /dev/null +++ b/kicker/applets/trash/trashapplet.desktop @@ -0,0 +1,138 @@ +[Desktop Entry] +Type=Plugin +Comment=Displays the trashcan and allows files to be dropped onto it +Comment[af]=Vertoon die asblik en laat toe dat lêers in dit gegooi mag word +Comment[ar]=يظهر سلّة المهملات و يسمح بإسقاط الملفات فيها +Comment[be]=Паказвае сметніцу і дазваляе кідаць файлы ў яе +Comment[bg]=Показване на кошчето и възможност за преместване на файлове в него +Comment[bn]=আবর্জনার বাক্স দেখায়, যাতে ফাইল ড্রপ করা যায় +Comment[bs]=Prikazuje kantu za smeće i omogućuje da se u nju bacaju datoteke +Comment[ca]=Mostra la paperera i permet amollar-hi fitxers +Comment[cs]=Zobrazuje koš a umožňuje do něj odhazovat soubory +Comment[csb]=Wëskrzëniwô zamkłósc kòsza ë zezwôlô przecygac do niegò lopczi +Comment[da]=Viser affaldsspanden og tillader at filer droppes på den +Comment[de]=Mülleimerfunktion in der Kontrollleiste +Comment[el]=Εμφανίζει τον Κάδο Απορριμμάτων και επιτρέπει την απόθεση αρχείων πάνω του +Comment[en_GB]=Displays the wastebin and allows files to be dropped onto it +Comment[eo]=Montras rubujon kaj ebligas dosiero-alfaligon +Comment[es]=Muestra la papelera y permite tirar archivos en ella +Comment[et]=Näitab prügikasti ning lubab faile sellesse visata +Comment[eu]=Zakarontzia bistaratzen du, fitxategiak jauregitea lagunduz +Comment[fa]=زبالهدان را نمایش داده و به پروندهها اجازه میدهد در آن بیفتند +Comment[fi]=Näyttää roskakorin ja sallii tiedostojen pudottamisen siihen +Comment[fr]=Affiche la corbeille et permet d'y déposer des fichiers +Comment[fy]=Lit it jiskefet sjen en stiet ta dat triemmen fuortsmiten wurde troch se nei it byldkaike ta te slepen +Comment[gl]=Mostra a papeleira e permite deitar ficheiros nela +Comment[he]=מציג את פח האשפה ומאפשר לך לזרוק אליו קבצים +Comment[hr]=Prikazuje kantu za otpad i omogućuje ispuštanje datoteka u nju +Comment[hu]=Megjeleníti a szemétkosarat, lehetővé téve fájlok belehelyezését +Comment[is]=Sýnir ruslakörfuna og leyfir að skrám sé sleppt á hana +Comment[it]=Mostra il cestino e permette di trascinarci sopra i file +Comment[ja]=ごみ箱を表示し、ごみ箱へのファイルのドロップを可能にします +Comment[kk]=Өшірілгендер шелегін керсетіп, оған файлдарды тастауға мүмкіндік береді +Comment[km]=បង្ហាញធុងសំរាម និងអនុញ្ញាតឲ្យទម្លាក់ឯកសារលើវា +Comment[lt]=Rodo šiukšliadėžę ir leidžia į ją tempiant numesti bylas +Comment[mk]=Ја прикажува корпата и овозможува пуштање датотеки врз неа +Comment[nb]=Viser papirkurven og lar deg legge filer i den +Comment[nds]=Wiest de Affalltünn, Dateien köönt dor op droppt warrn. +Comment[ne]=रद्दीटोकरी प्रदर्शन गर्छ र यसमा फाइलहरू राख्न अनुमति दिन्छ +Comment[nl]=Toont de prullenbak en maakt het mogelijk bestanden weg te gooien door ze naar het pictogram te slepen +Comment[nn]=Viser papirkorga og lèt deg leggja filer i henne +Comment[pl]=Pokazuje kosz i pozwala przeciągać do niego pliki +Comment[pt]=Mostra o caixote do lixo e permite largar ficheiros nele +Comment[pt_BR]=Mostra a lixeira e permite que arquivos sejam arrastados até ela +Comment[ro]=Afișează coșul de gunoi și permite aruncare fișierelor în acesta +Comment[ru]=Показать на рабочем столе корзину для ненужных файлов +Comment[sk]=Zobrazí odpadkový kôš a povolí vhodenie súborov do neho +Comment[sl]=Prikaz ikone za Smeti, na katero lahko odvržete datoteke +Comment[sr]=Приказује канту за смеће и омогућава испуштање фајлова на њу +Comment[sr@Latn]=Prikazuje kantu za smeće i omogućava ispuštanje fajlova na nju +Comment[sv]=Visar papperskorgen och tillåter att filer släpps på den +Comment[th]=แสดงถังขยะและอนุญาตให้มีการปล่อยแฟ้มลงไปได้ +Comment[tr]=Çöp kutusunu gösterir ve dosyaların üzerine taşınmasına izin verir +Comment[uk]=Показує смітник і дає змогу вкидати в нього файли +Comment[vi]=Hiển thị thùng rác và cho phép thả các tập tin vào đó +Comment[wa]=Håyneye l' batch et permete di mete des fitchîs å dvins +Comment[zh_CN]=显示回收站,并允许您将文件拖至其上 +Comment[zh_TW]=顯示垃圾筒並且允許將檔案丟到其中 +Name=Trash +Name[af]=Gemors +Name[ar]=سلة النفايات +Name[az]=Zibil +Name[be]=Сметніца +Name[bg]=Кошче +Name[bn]=আবর্জনা +Name[br]=Pod-lastez +Name[bs]=Smeće +Name[ca]=Paperera +Name[cs]=Koš +Name[csb]=Kòsz +Name[cy]=Sbwriel +Name[da]=Affald +Name[de]=Mülleimer +Name[el]=Κάδος απορριμμάτων +Name[en_GB]=Wastebin +Name[eo]=Rubujo +Name[es]=Papelera +Name[et]=Prügikast +Name[eu]=Zaborra +Name[fa]=زباله +Name[fi]=Roskakori +Name[fr]=Corbeille +Name[fy]=Jiskefet +Name[ga]=Bruscar +Name[gl]=Lixo +Name[he]=אשפה +Name[hi]=रद्दी +Name[hr]=Otpad +Name[hsb]=Papjernik +Name[hu]=Szemétkosár +Name[is]=Rusl +Name[it]=Cestino +Name[ja]=ごみ箱 +Name[ka]=ურნა +Name[kk]=Өшірілгендер +Name[km]=ធុងសំរាម +Name[lo]=ຖັງຂີ້ເຫຍື່ອ +Name[lt]=Šiukšliadėžė +Name[lv]=Miskaste +Name[mk]=Корпа +Name[mn]=Хогийн сав +Name[ms]=Sampah +Name[mt]=Skart +Name[nb]=Papirkurv +Name[nds]=Affalltünn +Name[ne]=रद्दीटोकरी +Name[nl]=Prullenbak +Name[nn]=Papirkorg +Name[nso]=Seswaraditlakala +Name[pa]=ਰੱਦੀ +Name[pl]=Kosz +Name[pt]=Lixo +Name[pt_BR]=Lixo +Name[ro]=Gunoi +Name[ru]=Корзина +Name[se]=Ruskalihtti +Name[sk]=Kôš +Name[sl]=Smeti +Name[sr]=Смеће +Name[sr@Latn]=Smeće +Name[sv]=Skräp +Name[ta]=குப்பை +Name[te]=చెత్త బుట్ట +Name[tg]=Ахлотдон +Name[th]=ถังขยะ +Name[tr]=Çöp +Name[tt]=Çüplek +Name[uk]=Смітник +Name[uz]=Chiqindilar qutisi +Name[uz@cyrillic]=Чиқиндилар қутиси +Name[ven]=Tshikha +Name[vi]=Thùng rác +Name[wa]=Batch +Name[xh]=Inkukumo +Name[zh_CN]=回收站 +Name[zh_TW]=資源回收桶 +Name[zu]=Izibi +Icon=trashcan_empty +X-KDE-Library=trash_panelapplet diff --git a/kicker/applets/trash/trashapplet.h b/kicker/applets/trash/trashapplet.h new file mode 100644 index 000000000..bc9662af4 --- /dev/null +++ b/kicker/applets/trash/trashapplet.h @@ -0,0 +1,62 @@ +/* This file is part of the KDE project + Copyright (c) 2004 Kevin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef TRASHAPPLET_H +#define TRASHAPPLET_H + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <kpanelapplet.h> +#include <qstring.h> +#include <kurl.h> +#include <kdirlister.h> + +#include "trashbutton.h" + +class TrashApplet : public KPanelApplet +{ +Q_OBJECT + +public: + TrashApplet(const QString& configFile, Type t = Normal, int actions = 0, + QWidget *parent = 0, const char *name = 0); + ~TrashApplet(); + + int widthForHeight(int height) const; + int heightForWidth(int width) const; + void about(); + +protected: + void resizeEvent(QResizeEvent *e); + void positionChange(Position p); + +protected slots: + void slotClear(); + void slotCompleted(); + void slotDeleteItem(KFileItem *); + +private: + KDirLister *mpDirLister; + TrashButton *mButton; + int mCount; +}; + +#endif diff --git a/kicker/applets/trash/trashbutton.cpp b/kicker/applets/trash/trashbutton.cpp new file mode 100644 index 000000000..e6934a983 --- /dev/null +++ b/kicker/applets/trash/trashbutton.cpp @@ -0,0 +1,154 @@ +/* This file is part of the KDE project + Copyright (c) 2004 Kevin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "trashbutton.h" + +#include <qpopupmenu.h> +#include <qtooltip.h> + +#include <klocale.h> +#include <krun.h> +#include <kpopupmenu.h> + +#include <kio/netaccess.h> + +#include <konq_operations.h> +#include <konq_popupmenu.h> + +TrashButton::TrashButton(QWidget *parent) + : PanelPopupButton(parent), mActions(this, this), + mFileItem(KFileItem::Unknown, KFileItem::Unknown, "trash:/") +{ + KIO::UDSEntry entry; + KIO::NetAccess::stat("trash:/", entry, 0L); + mFileItem.assign(KFileItem(entry, "trash:/")); + + KAction *a = KStdAction::paste(this, SLOT(slotPaste()), + &mActions, "paste"); + a->setShortcut(0); + + move(0, 0); + resize(20, 20); + + setTitle(i18n("Trash")); + setIcon( "trashcan_empty" ); + + setAcceptDrops(true); + + // Activate this code only if we find a way to have both an + // action and a popup menu for the same kicker button + //connect(this, SIGNAL(clicked()), this, SLOT(slotClicked())); + + setPopup(new QPopupMenu()); +} + +TrashButton::~TrashButton() +{ +} + +void TrashButton::setItemCount(int count) +{ + if (count==0) + { + setIcon( "trashcan_empty" ); + QToolTip::add(this, i18n("Empty")); + } + else + { + setIcon( "trashcan_full" ); + QToolTip::add(this, i18n("One item", "%n items", count)); + } +} + +void TrashButton::initPopup() +{ + QPopupMenu *old_popup = popup(); + + KFileItemList items; + items.append(&mFileItem); + + KonqPopupMenu::KonqPopupFlags kpf = + KonqPopupMenu::ShowProperties + | KonqPopupMenu::ShowNewWindow; + + KParts::BrowserExtension::PopupFlags bef = + KParts::BrowserExtension::DefaultPopupItems; + + KonqPopupMenu *new_popup = new KonqPopupMenu(0L, items, + KURL("trash:/"), mActions, 0L, + this, kpf, bef); + KPopupTitle *title = new KPopupTitle(new_popup); + title->setTitle(i18n("Trash")); + + new_popup->insertItem(title, -1, 0); + + setPopup(new_popup); + + if (old_popup!=0L) delete old_popup; +} + +// Activate this code only if we find a way to have both an +// action and a popup menu for the same kicker button +/* +void TrashButton::slotClicked() +{ + mFileItem.run(); +} +*/ + +void TrashButton::slotPaste() +{ + KonqOperations::doPaste(this, mFileItem.url()); +} + +void TrashButton::dragEnterEvent(QDragEnterEvent* e) +{ + e->accept(true); +} + +void TrashButton::dropEvent(QDropEvent *e) +{ + KonqOperations::doDrop(0L, mFileItem.url(), e, this); +} + +QString TrashButton::tileName() +{ + return mFileItem.name(); +} + +void TrashButton::setPanelPosition(KPanelApplet::Position position) +{ + switch(position) + { + case KPanelApplet::pBottom: + setPopupDirection(KPanelApplet::Up); + break; + case KPanelApplet::pTop: + setPopupDirection(KPanelApplet::Down); + break; + case KPanelApplet::pRight: + setPopupDirection(KPanelApplet::Left); + break; + case KPanelApplet::pLeft: + setPopupDirection(KPanelApplet::Right); + break; + } +} + +#include "trashbutton.moc" diff --git a/kicker/applets/trash/trashbutton.h b/kicker/applets/trash/trashbutton.h new file mode 100644 index 000000000..7a5d8b59f --- /dev/null +++ b/kicker/applets/trash/trashbutton.h @@ -0,0 +1,60 @@ +/* This file is part of the KDE project + Copyright (c) 2004 Kevin Ottens <ervin ipsquad net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef TRASHBUTTON_H +#define TRASHBUTTON_H + +#include <panelbutton.h> +#include <qpoint.h> +#include <qstring.h> +#include <qpixmap.h> +#include <qcursor.h> +#include <qtimer.h> +#include <kfileitem.h> +#include <kpanelapplet.h> +#include <kactioncollection.h> + +class TrashButton : public PanelPopupButton +{ +Q_OBJECT + +public: + TrashButton(QWidget *parent); + ~TrashButton(); + void setItemCount(int count); + void setPanelPosition(KPanelApplet::Position position); + +protected: + QString tileName(); + void initPopup(); + void dragEnterEvent(QDragEnterEvent *e); + void dropEvent(QDropEvent *e); + +protected slots: + // Activate this code only if we find a way to have both an + // action and a popup menu for the same kicker button + //void slotClicked(); + void slotPaste(); + +private: + KActionCollection mActions; + KFileItem mFileItem; +}; + +#endif |