From fd7a608ffe99df747f0496300276b95f766c18b9 Mon Sep 17 00:00:00 2001 From: tpearson Date: Fri, 3 Sep 2010 20:49:48 +0000 Subject: [PATCH] * Added Kickoff menu git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1171422 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kcontrol/kicker/kicker_config_hiding.desktop | 2 +- kcontrol/kicker/kicker_config_menus.desktop | 2 +- kcontrol/kicker/menutab.ui | 70 +- kcontrol/kicker/menutab_impl.cpp | 50 +- kcontrol/kicker/menutab_impl.h | 1 + kicker/applets/clock/clock.cpp | 4 +- kicker/applets/launcher/quicklauncher.cpp | 2 +- kicker/applets/media/mediumbutton.cpp | 55 +- kicker/applets/minipager/pagerbutton.cpp | 2 + .../applets/naughty/NaughtyProcessMonitor.cpp | 2 +- .../applets/systemtray/systemtrayapplet.cpp | 2 +- kicker/applets/trash/trashbutton.cpp | 2 +- kicker/configure.in.in | 78 + kicker/data/Makefile.am | 2 +- kicker/data/kickoff/Makefile.am | 14 + kicker/data/kickoff/kmenu_active.png | Bin 3257 -> 4400 bytes kicker/data/kickoff/kmenu_basic.mng | Bin 81101 -> 4623 bytes kicker/data/kickoff/kmenu_flipped.mng | Bin 81240 -> 4623 bytes kicker/data/kickoff/kmenu_vertical.mng | Bin 62006 -> 4131 bytes kicker/data/kmenu_side/Makefile.am | 5 +- kicker/extensions/kasbar/kasbar.cpp | 2 +- kicker/extensions/kasbar/kasclockitem.cpp | 2 +- kicker/extensions/kasbar/kasloaditem.cpp | 2 +- kicker/extensions/kasbar/kasstartupitem.cpp | 2 +- kicker/kicker/Makefile.am | 5 +- kicker/kicker/buttons/Makefile.am | 4 +- kicker/kicker/buttons/browserbutton.cpp | 2 +- kicker/kicker/buttons/kbutton.cpp | 3 +- kicker/kicker/buttons/knewbutton.cpp | 455 ++ kicker/kicker/buttons/knewbutton.h | 98 + kicker/kicker/core/Makefile.am | 8 +- kicker/kicker/core/applethandle.cpp | 23 +- kicker/kicker/core/container_button.cpp | 11 +- kicker/kicker/core/containerarea.cpp | 3 +- kicker/kicker/core/kicker.cpp | 5 +- kicker/kicker/core/kmenubase.ui | 300 ++ kicker/kicker/core/kmenubase.ui.h | 9 + kicker/kicker/core/main.cpp | 2 +- kicker/kicker/core/menumanager.cpp | 32 +- kicker/kicker/core/menumanager.h | 9 +- kicker/kicker/core/unhidetrigger.cpp | 2 +- kicker/kicker/interfaces/Makefile.am | 12 + .../interfaces/kickoff-search-plugin.cpp | 37 + .../kicker/interfaces/kickoff-search-plugin.h | 106 + .../interfaces/kickoffsearchinterface.cpp | 27 + .../interfaces/kickoffsearchinterface.h | 46 + .../interfaces/kickoffsearchplugin.desktop | 4 + kicker/kicker/plugins/Makefile.am | 24 + kicker/kicker/plugins/beaglesearch.cpp | 362 ++ kicker/kicker/plugins/beaglesearch.h | 234 + .../kicker/plugins/kickoff-beagle-plugin.cpp | 499 +++ kicker/kicker/plugins/kickoff-beagle-plugin.h | 64 + .../plugins/kickoffsearch_beagle.desktop | 6 + kicker/kicker/ui/Makefile.am | 24 +- kicker/kicker/ui/addappletvisualfeedback.cpp | 1 + kicker/kicker/ui/appletop_mnu.cpp | 22 + kicker/kicker/ui/appletop_mnu.h | 3 + kicker/kicker/ui/browser_mnu.cpp | 2 +- kicker/kicker/ui/default-favs | 9 + kicker/kicker/ui/flipscrollview.cpp | 324 ++ kicker/kicker/ui/flipscrollview.h | 118 + kicker/kicker/ui/itemview.cpp | 1257 ++++++ kicker/kicker/ui/itemview.h | 260 ++ kicker/kicker/ui/k_mnu_stub.cpp | 141 + kicker/kicker/ui/k_mnu_stub.h | 72 + kicker/kicker/ui/k_new_mnu.cpp | 3779 +++++++++++++++++ kicker/kicker/ui/k_new_mnu.h | 343 ++ kicker/kicker/ui/kickoff_bar.cpp | 200 + kicker/kicker/ui/kickoff_bar.h | 53 + kicker/kicker/ui/kmenuitembase.ui | 141 + kicker/kicker/ui/media_watcher.cpp | 57 + kicker/kicker/ui/media_watcher.h | 51 + kicker/kicker/ui/mykickoffsearchinterface.cpp | 54 + kicker/kicker/ui/mykickoffsearchinterface.h | 47 + kicker/kicker/ui/query.cpp | 136 + kicker/kicker/ui/query.h | 55 + kicker/libkicker/kickerSettings.kcfg | 100 + kicker/libkicker/kickertip.cpp | 24 +- kicker/libkicker/kickertip.h | 2 +- kicker/libkicker/panelbutton.cpp | 56 +- kicker/libkicker/panelbutton.h | 13 +- kicker/taskbar/taskbar.cpp | 3 +- kicker/taskbar/taskcontainer.cpp | 12 +- 83 files changed, 9923 insertions(+), 129 deletions(-) create mode 100644 kicker/configure.in.in create mode 100644 kicker/data/kickoff/Makefile.am create mode 100644 kicker/kicker/buttons/knewbutton.cpp create mode 100644 kicker/kicker/buttons/knewbutton.h create mode 100644 kicker/kicker/core/kmenubase.ui create mode 100644 kicker/kicker/core/kmenubase.ui.h create mode 100644 kicker/kicker/interfaces/Makefile.am create mode 100644 kicker/kicker/interfaces/kickoff-search-plugin.cpp create mode 100644 kicker/kicker/interfaces/kickoff-search-plugin.h create mode 100644 kicker/kicker/interfaces/kickoffsearchinterface.cpp create mode 100644 kicker/kicker/interfaces/kickoffsearchinterface.h create mode 100644 kicker/kicker/interfaces/kickoffsearchplugin.desktop create mode 100644 kicker/kicker/plugins/Makefile.am create mode 100644 kicker/kicker/plugins/beaglesearch.cpp create mode 100644 kicker/kicker/plugins/beaglesearch.h create mode 100644 kicker/kicker/plugins/kickoff-beagle-plugin.cpp create mode 100644 kicker/kicker/plugins/kickoff-beagle-plugin.h create mode 100644 kicker/kicker/plugins/kickoffsearch_beagle.desktop create mode 100644 kicker/kicker/ui/default-favs create mode 100644 kicker/kicker/ui/flipscrollview.cpp create mode 100644 kicker/kicker/ui/flipscrollview.h create mode 100644 kicker/kicker/ui/itemview.cpp create mode 100644 kicker/kicker/ui/itemview.h create mode 100644 kicker/kicker/ui/k_mnu_stub.cpp create mode 100644 kicker/kicker/ui/k_mnu_stub.h create mode 100644 kicker/kicker/ui/k_new_mnu.cpp create mode 100644 kicker/kicker/ui/k_new_mnu.h create mode 100644 kicker/kicker/ui/kickoff_bar.cpp create mode 100644 kicker/kicker/ui/kickoff_bar.h create mode 100644 kicker/kicker/ui/kmenuitembase.ui create mode 100644 kicker/kicker/ui/media_watcher.cpp create mode 100644 kicker/kicker/ui/media_watcher.h create mode 100644 kicker/kicker/ui/mykickoffsearchinterface.cpp create mode 100644 kicker/kicker/ui/mykickoffsearchinterface.h create mode 100644 kicker/kicker/ui/query.cpp create mode 100644 kicker/kicker/ui/query.h diff --git a/kcontrol/kicker/kicker_config_hiding.desktop b/kcontrol/kicker/kicker_config_hiding.desktop index 60c040264..0cf9553b9 100644 --- a/kcontrol/kicker/kicker_config_hiding.desktop +++ b/kcontrol/kicker/kicker_config_hiding.desktop @@ -150,7 +150,7 @@ Keywords[csb]=kicker,panel,kpanel,lëstëw zadaniów,sztartowô lëstëw,lëstë Keywords[cy]=ciciwr,kicker,panel,kpanel,bar tasgau,bar cychwyn,bar lansio,lleoliad,maint,awto-guddio,hunan-guddio,cuddio,botymau,animeiddiad,cefndir,themâu,storfa dewislen, storfa,cache,celc,cudd,K-Menu,nodau tudalen,dogfenni diweddar,porydd cyflym,dewislen porydd,dewislen,eiconau,teiliau,rhaglenigion,ymcychwyn,amlygu,carnau,eiconau chwyddo Keywords[da]=kicker,panel,kpanel,opgavelinje,startlinje,sted,størrelse,autogem,gem,knapper,animering,baggrund,temaer,menucache,cache,skjult,K-Menu,bogmærker,nylige dokumenter,hurtigsøger,søgemenu,menu,ikoner,fliser,panelprogrammer,opstart,markér,håndterer,ikoner Keywords[de]=Kicker,Panel,Taskbar,Kontrollleiste,Startleiste,Klickstartleiste,Fensterleiste,Autom. ausblenden,Ausblenden, Knöpfe,Animation,Hintergründe,Stile,Design,Themes,Menü-Zwischenspeicher, K-Menü,Zwischenspeicher,Lesezeichen,Zuletzt geöffnete Dateien, Schnellanzeiger,Menüs,Symbole,Icons,Kacheln,Applets,Miniprogramme, Java-Miniprogramme,Hervorhebung,Anfasser,Sicherheitsstufen,Zoom für Symbole -Keywords[el]=kicker,πίνακας,kpanel,γραμμή εργασιών,γραμμή έναρξης,γραμμή εκκίνησης,τοποθεσία,μέγεθος,αυτόματη απόκρυψη,απόκρυψη,κουμπιά,εφέ κίνησης,φόντο,θέματα,λανθάνουσα μνήμη μενού,λανθάνουσα μνήμη,κρυφό, K-Μενού,σελιδοδείκτες,πρόσφατα έγγραφα,γρήγορος εξερευνητής,μενού εξερευνητή,μενού,εικονίδια,tiles,μικροεφαρμογές,έναρξη,τονισμός,χειριστήρια, μεγέθυνση εικονιδίων +Keywords[el]=kicker,πίνακας,kpanel,γραμμή εργασιών,γραμμή έναρξης,γραμμή εκκίνησης,τοποθεσία,μέγεθος,αυτόματη απόκρυψη,απόκρυψη,κουμπιά,εφέ κίνησης,φόντο,θέματα,λανθάνουσα μνήμη μενού,λανθάνουσα μνήμη,κρυφό, K-Μενού,σελιδοδείκτες,πρόσφατα έγγραφα,γρήγορος εξερευνητής,μενού εξερευνητή,μενού,εικονίδια,tiles,εφαρμογίδια,έναρξη,τονισμός,χειριστήρια, μεγέθυνση εικονιδίων Keywords[eo]=lanĉilo,panelo,tasklistelo,situo,grandeco,aŭtokaŝo,kaŝo,butono,fono,etoso,menubufro,K-Menuo,legosigno,lasta dokumento,rapidrigardilo,rigardmenuo,piktogramo,kahelo,aplikaĵo,lanĉo,emfazo,teniloj,pligrandigo,fidindaj aplikaĵetoj,sekurecnivelo Keywords[es]=kicker,panel,kpanel,barra de tareas,barra de inicio,barra de lanzamiento,dirección,tamaño,auto ocultar,ocultar,botones,animación,fondo,temas,caché de menú,caché,oculto,Menú K,marcadores,documentos recientes,navegador rápido,menú navegador,menú,iconos,mosaicos,miniaplicaciones,arranque,resaltado,asas,iconos ampliados Keywords[et]=kicker,paneel,kpanel,tegumiriba,käivitusriba,asukoht,suurus,terminal,automaatne peitmine,peitmine,nupud,animatsioon,taust,teemad,menüü vahemälu,vahemälu,peidetud,K-menüü,järjehoidjad,viimati kasutatud dokumendid, kiirbrauser,lehitsemise menüü,menüü,ikoonid,apletid,käivitamine,esiletõstmine,piirded,ikoonide suurendamine,usaldusväärsed apletid,turvatase diff --git a/kcontrol/kicker/kicker_config_menus.desktop b/kcontrol/kicker/kicker_config_menus.desktop index 3dbb52d48..b0bd513e1 100644 --- a/kcontrol/kicker/kicker_config_menus.desktop +++ b/kcontrol/kicker/kicker_config_menus.desktop @@ -147,7 +147,7 @@ Keywords[csb]=kicker,panel,kpanel,lëstëw zadaniów,sztartowô lëstëw,lëstë Keywords[cy]=ciciwr,kicker,panel,kpanel,bar tasgau,bar cychwyn,bar lansio,lleoliad,maint,awto-guddio,hunan-guddio,cuddio,botymau,animeiddiad,cefndir,themâu,storfa dewislen, storfa,cache,celc,cudd,K-Menu,nodau tudalen,dogfenni diweddar,porydd cyflym,dewislen porydd,dewislen,eiconau,teiliau,rhaglenigion,ymcychwyn,amlygu,carnau,eiconau chwyddo Keywords[da]=kicker,panel,kpanel,opgavelinje,startlinje,sted,størrelse,autogem,gem,knapper,animering,baggrund,temaer,menucache,cache,skjult,K-Menu,bogmærker,nylige dokumenter,hurtigsøger,søgemenu,menu,ikoner,fliser,panelprogrammer,opstart,markér,håndterer,ikoner Keywords[de]=Kicker,Panel,Taskbar,Kontrollleiste,Startleiste,Klickstartleiste,Fensterleiste,Autom. ausblenden,Ausblenden, Knöpfe,Animation,Hintergründe,Stile,Design,Themes,Menü-Zwischenspeicher, K-Menü,Zwischenspeicher,Lesezeichen,Zuletzt geöffnete Dateien, Schnellanzeiger,Menüs,Symbole,Icons,Kacheln,Applets,Miniprogramme, Java-Miniprogramme,Hervorhebung,Anfasser,Sicherheitsstufen,Zoom für Symbole -Keywords[el]=kicker,πίνακας,kpanel,γραμμή εργασιών,γραμμή έναρξης,γραμμή εκκίνησης,τοποθεσία,μέγεθος,αυτόματη απόκρυψη,απόκρυψη,κουμπιά,εφέ κίνησης,φόντο,θέματα,λανθάνουσα μνήμη μενού,λανθάνουσα μνήμη,κρυφό, K-Μενού,σελιδοδείκτες,πρόσφατα έγγραφα,γρήγορος εξερευνητής,μενού εξερευνητή,μενού,εικονίδια,tiles,μικροεφαρμογές,έναρξη,τονισμός,χειριστήρια, μεγέθυνση εικονιδίων +Keywords[el]=kicker,πίνακας,kpanel,γραμμή εργασιών,γραμμή έναρξης,γραμμή εκκίνησης,τοποθεσία,μέγεθος,αυτόματη απόκρυψη,απόκρυψη,κουμπιά,εφέ κίνησης,φόντο,θέματα,λανθάνουσα μνήμη μενού,λανθάνουσα μνήμη,κρυφό, K-Μενού,σελιδοδείκτες,πρόσφατα έγγραφα,γρήγορος εξερευνητής,μενού εξερευνητή,μενού,εικονίδια,tiles,εφαρμογίδια,έναρξη,τονισμός,χειριστήρια, μεγέθυνση εικονιδίων Keywords[eo]=lanĉilo,panelo,tasklistelo,situo,grandeco,aŭtokaŝo,kaŝo,butono,fono,etoso,menubufro,K-Menuo,legosigno,lasta dokumento,rapidrigardilo,rigardmenuo,piktogramo,kahelo,aplikaĵo,lanĉo,emfazo,teniloj,pligrandigo,fidindaj aplikaĵetoj,sekurecnivelo Keywords[es]=kicker,panel,kpanel,barra de tareas,barra de inicio,barra de lanzamiento,dirección,tamaño,auto ocultar,ocultar,botones,animación,fondo,temas,caché de menú,caché,oculto,Menú K,marcadores,documentos recientes,navegador rápido,menú navegador,menú,iconos,mosaicos,miniaplicaciones,arranque,resaltado,asas,iconos ampliados Keywords[et]=kicker,paneel,kpanel,tegumiriba,käivitusriba,asukoht,suurus,terminal,automaatne peitmine,peitmine,nupud,animatsioon,taust,teemad,menüü vahemälu,vahemälu,peidetud,K-menüü,järjehoidjad,viimati kasutatud dokumendid, kiirbrauser,lehitsemise menüü,menüü,ikoonid,apletid,käivitamine,esiletõstmine,piirded,ikoonide suurendamine,usaldusväärsed apletid,turvatase diff --git a/kcontrol/kicker/menutab.ui b/kcontrol/kicker/menutab.ui index f5e0ced05..7d3a3d460 100644 --- a/kcontrol/kicker/menutab.ui +++ b/kcontrol/kicker/menutab.ui @@ -8,8 +8,8 @@ 0 0 - 410 - 437 + 923 + 649 @@ -19,6 +19,59 @@ 0 + + + layout5 + + + + unnamed + + + + textLabel1 + + + Start menu style: + + + comboMenuStyle + + + + + + Kickoff + + + + + Trinity Classic + + + + m_comboMenuStyle + + + + + spacer4 + + + Horizontal + + + Expanding + + + + 40 + 20 + + + + + m_kmenuGroup @@ -38,6 +91,14 @@ unnamed + + + m_openOnHover + + + Open menu on mouse hover + + kcfg_MenuEntryFormat @@ -649,6 +710,8 @@ + + m_formatSimple m_formatNameDesc @@ -672,8 +735,5 @@ klistview.h knuminput.h - knuminput.h - knuminput.h - knuminput.h diff --git a/kcontrol/kicker/menutab_impl.cpp b/kcontrol/kicker/menutab_impl.cpp index 1d1053af8..b85db9fc7 100644 --- a/kcontrol/kicker/menutab_impl.cpp +++ b/kcontrol/kicker/menutab_impl.cpp @@ -16,12 +16,16 @@ */ #include +#include #include #include #include #include #include +#include +#include +#include #include #include #include @@ -148,12 +152,47 @@ void MenuTab::load( bool useDefaults ) } } + c->setGroup("General"); + m_comboMenuStyle->setCurrentItem( c->readBoolEntry("LegacyKMenu", true) ? 1 : 0 ); + m_openOnHover->setChecked( c->readBoolEntry("OpenOnHover", true) ); + menuStyleChanged(); + + connect(m_comboMenuStyle, TQT_SIGNAL(activated(int)), TQT_SIGNAL(changed())); + connect(m_comboMenuStyle, TQT_SIGNAL(activated(int)), TQT_SLOT(menuStyleChanged())); + connect(m_openOnHover, TQT_SIGNAL(clicked()), TQT_SIGNAL(changed())); + m_showFrequent->setChecked(true); if ( useDefaults ) emit changed(); } +void MenuTab::menuStyleChanged() +{ + if (m_comboMenuStyle->currentItem()==1) { + m_openOnHover->setEnabled(false); + m_subMenus->setEnabled(true); + kcfg_UseSidePixmap->setEnabled(true); + kcfg_MenuEntryFormat->setEnabled(true); + kcfg_RecentVsOften->setEnabled(true); + m_showFrequent->setEnabled(true); + kcfg_UseSearchBar->setEnabled(true); + kcfg_MaxEntries2->setEnabled(true); + maxrecentdocs->setEnabled(true); + } + else { + m_openOnHover->setEnabled(true); + m_subMenus->setEnabled(false); + kcfg_UseSidePixmap->setEnabled(false); + kcfg_MenuEntryFormat->setEnabled(false); + kcfg_RecentVsOften->setEnabled(false); + m_showFrequent->setEnabled(false); + kcfg_UseSearchBar->setEnabled(false); + kcfg_MaxEntries2->setEnabled(false); + maxrecentdocs->setEnabled(false); + } +} + void MenuTab::save() { KSharedConfig::Ptr c = KSharedConfig::openConfig(KickerConfig::the()->configName()); @@ -179,9 +218,18 @@ void MenuTab::save() } } c->writeEntry("Extensions", ext); + c->setGroup("General"); + bool kmenusetting = m_comboMenuStyle->currentItem()==1; + bool oldkmenusetting = c->readBoolEntry("LegacyKMenu", true); + + c->writeEntry("LegacyKMenu", kmenusetting); + c->writeEntry("OpenOnHover", m_openOnHover->isChecked()); c->sync(); + if (kmenusetting != oldkmenusetting) + DCOPRef ("kicker", "default").call("restart()"); + // Save KMenu settings c->setGroup("KMenu"); c->writeEntry("CustomIcon", m_kmenu_icon); @@ -195,7 +243,7 @@ void MenuTab::save() config->sync(); if (m_kmenu_button_changed == true) { - system("dcop kicker kicker restart &"); + DCOPRef ("kicker", "default").call("restart()"); } } diff --git a/kcontrol/kicker/menutab_impl.h b/kcontrol/kicker/menutab_impl.h index 877e47ffe..3ef197f09 100644 --- a/kcontrol/kicker/menutab_impl.h +++ b/kcontrol/kicker/menutab_impl.h @@ -65,6 +65,7 @@ signals: public slots: void launchMenuEditor(); + void menuStyleChanged(); void launchIconEditor(); void kmenuChanged(); diff --git a/kicker/applets/clock/clock.cpp b/kicker/applets/clock/clock.cpp index 7bc46afa4..26f1ae4c0 100644 --- a/kicker/applets/clock/clock.cpp +++ b/kicker/applets/clock/clock.cpp @@ -901,8 +901,8 @@ ClockApplet::ClockApplet(const TQString& configFile, Type t, int actions, _calendar(0), _disableCalendar(false), _clock(0), - _timer(new TQTimer(this)), - m_layoutTimer(new TQTimer(this)), + _timer(new TQTimer(this, "ClockApplet::_timer")), + m_layoutTimer(new TQTimer(this, "m_layoutTimer")), m_layoutDelay(0), m_followBackgroundSetting(true), m_dateFollowBackgroundSetting(true), diff --git a/kicker/applets/launcher/quicklauncher.cpp b/kicker/applets/launcher/quicklauncher.cpp index 557ea4cd3..a278b39c9 100644 --- a/kicker/applets/launcher/quicklauncher.cpp +++ b/kicker/applets/launcher/quicklauncher.cpp @@ -110,7 +110,7 @@ QuickLauncher::QuickLauncher(const TQString& configFile, Type type, int actions, m_configAction = new KAction(i18n("Configure Quicklauncher..."), "configure", KShortcut(), this, TQT_SLOT(slotConfigure()), this); - m_saveTimer = new TQTimer(this); + m_saveTimer = new TQTimer(this, "m_saveTimer"); connect(m_saveTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(saveConfig())); m_popularity = new PopularityStatistics(); diff --git a/kicker/applets/media/mediumbutton.cpp b/kicker/applets/media/mediumbutton.cpp index 5be2acbab..0fae43441 100644 --- a/kicker/applets/media/mediumbutton.cpp +++ b/kicker/applets/media/mediumbutton.cpp @@ -45,7 +45,8 @@ #include MediumButton::MediumButton(TQWidget *parent, const KFileItem &fileItem) - : PanelPopupButton(parent), mActions(this, this), mFileItem(fileItem) + : PanelPopupButton(parent), mActions(this, this), mFileItem(fileItem), mOpenTimer(0, + "MediumButton::mOpenTimer") { KAction *a = KStdAction::paste(this, TQT_SLOT(slotPaste()), &mActions, "pasteto"); @@ -74,9 +75,9 @@ MediumButton::MediumButton(TQWidget *parent, const KFileItem &fileItem) MediumButton::~MediumButton() { - TQPopupMenu *menu = popup(); - setPopup(0); - delete menu; + TQPopupMenu *menu = static_cast(popup()); + setPopup(0); + delete menu; } const KFileItem &MediumButton::fileItem() const @@ -94,29 +95,29 @@ void MediumButton::setFileItem(const KFileItem &fileItem) void MediumButton::initPopup() { - TQPopupMenu *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; + TQPopupMenu *old_popup = static_cast(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() diff --git a/kicker/applets/minipager/pagerbutton.cpp b/kicker/applets/minipager/pagerbutton.cpp index c56afca38..10a5aa8c9 100644 --- a/kicker/applets/minipager/pagerbutton.cpp +++ b/kicker/applets/minipager/pagerbutton.cpp @@ -70,6 +70,8 @@ KMiniPagerButton::KMiniPagerButton(int desk, bool useViewPorts, const TQPoint& v m_bgPixmap(0), m_isCommon(false), m_currentWindow(0), + m_updateCompressor(0, "KMiniPagerButton::updateCompressor"), + m_dragSwitchTimer(0, "KMiniPagerButton::dragSwitchTimer"), m_inside(false) { setToggleButton(true); diff --git a/kicker/applets/naughty/NaughtyProcessMonitor.cpp b/kicker/applets/naughty/NaughtyProcessMonitor.cpp index 37cba7a37..6228cfba1 100644 --- a/kicker/applets/naughty/NaughtyProcessMonitor.cpp +++ b/kicker/applets/naughty/NaughtyProcessMonitor.cpp @@ -94,7 +94,7 @@ NaughtyProcessMonitor::NaughtyProcessMonitor d = new NaughtyProcessMonitorPrivate; d->interval_ = interval * 1000; d->triggerLevel_ = triggerLevel; - d->timer_ = new TQTimer(this); + d->timer_ = new TQTimer(this, "NaughtyProcessMonitorPrivate::timer"); connect(d->timer_, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotTimeout())); } diff --git a/kicker/applets/systemtray/systemtrayapplet.cpp b/kicker/applets/systemtray/systemtrayapplet.cpp index 51bf2b9d8..ad44b640f 100644 --- a/kicker/applets/systemtray/systemtrayapplet.cpp +++ b/kicker/applets/systemtray/systemtrayapplet.cpp @@ -401,7 +401,7 @@ void SystemTrayApplet::showExpandButton(bool show) connect(m_expandButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(toggleExpanded())); - m_autoRetractTimer = new TQTimer(this); + m_autoRetractTimer = new TQTimer(this, "m_autoRetractTimer"); connect(m_autoRetractTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(checkAutoRetract())); } diff --git a/kicker/applets/trash/trashbutton.cpp b/kicker/applets/trash/trashbutton.cpp index eb036c119..19e6be02b 100644 --- a/kicker/applets/trash/trashbutton.cpp +++ b/kicker/applets/trash/trashbutton.cpp @@ -78,7 +78,7 @@ void TrashButton::setItemCount(int count) void TrashButton::initPopup() { - TQPopupMenu *old_popup = popup(); + TQPopupMenu *old_popup = static_cast(popup()); KFileItemList items; items.append(&mFileItem); diff --git a/kicker/configure.in.in b/kicker/configure.in.in new file mode 100644 index 000000000..4da44a4a8 --- /dev/null +++ b/kicker/configure.in.in @@ -0,0 +1,78 @@ +dnl Check for pkg-config +AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + +if test "$PKG_CONFIG" = "no"; then + AC_MSG_ERROR([ +This package requires pkg-config. +]) +fi + +dnl Check for Glib-2.0 +# GLIB_CFLAGS: cflags for compiling glib dependant sources +# GLIB_LIBADD: glib libraries (-l options) +# GLIB_LDFLAGS: flags containing path to glib libraries (-L options) + +GLIB_PACKAGES="gmodule-2.0 gthread-2.0" +GLIB_VERSION="1.3.3" +AC_MSG_CHECKING(for GLib-2.0 (at least $GLIB_VERSION)) + +if $PKG_CONFIG --atleast-pkgconfig-version 0.15 ; then + if $PKG_CONFIG --atleast-version $GLIB_VERSION $GLIB_PACKAGES >/dev/null 2>&1 ; then + GLIB_CFLAGS="`$PKG_CONFIG --cflags $GLIB_PACKAGES`" + GLIB_LIBADD="`$PKG_CONFIG --libs-only-l --libs-only-other $GLIB_PACKAGES`" + GLIB_LDFLAGS="`$PKG_CONFIG --libs-only-L $GLIB_PACKAGES`" + AC_MSG_RESULT(yes) + fi +else + if $PKG_CONFIG --atleast-version $GLIB_VERSION $GLIB_PACKAGES >/dev/null 2>&1 ; then + GLIB_CFLAGS="`$PKG_CONFIG --cflags $GLIB_PACKAGES`" + GLIB_LIBADD="`$PKG_CONFIG --libs-only-l $GLIB_PACKAGES`" + GLIB_LDFLAGS="`$PKG_CONFIG --libs-only-L $GLIB_PACKAGES`" + AC_MSG_RESULT(yes) + AC_MSG_WARN([you may need to run make LDFLAGS=-pthread to compile arts]) + fi +fi + +if test -z "$GLIB_LIBADD"; then + AC_MSG_RESULT(not installed) + DO_NOT_COMPILE="$DO_NOT_COMPILE kerry gmcop" +fi + +AC_SUBST(GLIB_CFLAGS) +AC_SUBST(GLIB_LIBADD) +AC_SUBST(GLIB_LDFLAGS) + +dnl Check for libbeagle 0.2.0 +# LIBBEAGLE_CFLAGS: cflags for compiling libbeagle dependant sources +# LIBBEAGLE_LIBADD: libbeagle libraries (-l options) +# LIBBEAGLE_LDFLAGS: flags containing path to libbeagle libraries (-L options) + +LIBBEAGLE_PACKAGES="libbeagle-0.0" +LIBBEAGLE_VERSION="0.2.4" +AC_MSG_CHECKING(for libbeagle-0.2.4 (at least $LIBBEAGLE_VERSION)) + +if $PKG_CONFIG --atleast-pkgconfig-version 0.15 ; then + if $PKG_CONFIG --atleast-version $LIBBEAGLE_VERSION $LIBBEAGLE_PACKAGES >/dev/null 2>&1 ; then + LIBBEAGLE_CFLAGS="`$PKG_CONFIG --cflags $LIBBEAGLE_PACKAGES`" + LIBBEAGLE_LIBADD="`$PKG_CONFIG --libs-only-l --libs-only-other $LIBBEAGLE_PACKAGES`" + LIBBEAGLE_LDFLAGS="`$PKG_CONFIG --libs-only-L $LIBBEAGLE_PACKAGES`" + AC_MSG_RESULT(yes) + fi +else + if $PKG_CONFIG --atleast-version $LIBBEAGLE_VERSION $LIBBEAGLE_PACKAGES >/dev/null 2>&1 ; then + LIBBEAGLE_CFLAGS="`$PKG_CONFIG --cflags $LIBBEAGLE_PACKAGES`" + LIBBEAGLE_LIBADD="`$PKG_CONFIG --libs-only-l $LIBBEAGLE_PACKAGES`" + LIBBEAGLE_LDFLAGS="`$PKG_CONFIG --libs-only-L $LIBBEAGLE_PACKAGES`" + AC_MSG_RESULT(yes) + AC_MSG_WARN([you may need to run make LDFLAGS=-pthread to compile arts]) + fi +fi + +if test -z "$LIBBEAGLE_LIBADD"; then + AC_MSG_RESULT(not installed) + DO_NOT_COMPILE="$DO_NOT_COMPILE kerry gmcop" +fi + +AC_SUBST(LIBBEAGLE_CFLAGS) +AC_SUBST(LIBBEAGLE_LIBADD) +AC_SUBST(LIBBEAGLE_LDFLAGS) diff --git a/kicker/data/Makefile.am b/kicker/data/Makefile.am index 41d9598ed..53e72912e 100644 --- a/kicker/data/Makefile.am +++ b/kicker/data/Makefile.am @@ -1 +1 @@ -SUBDIRS = icons tiles app_start_anim wallpaper kmenu_side +SUBDIRS = icons tiles app_start_anim wallpaper kmenu_side kickoff diff --git a/kicker/data/kickoff/Makefile.am b/kicker/data/kickoff/Makefile.am new file mode 100644 index 000000000..20ac737ff --- /dev/null +++ b/kicker/data/kickoff/Makefile.am @@ -0,0 +1,14 @@ +kicker_kmenuside_pics_data_DATA = resize_handle.png \ + main_corner_tl.png main_corner_tr.png search-gradient.png \ + menu_separator.png search-tab-center.png search-tab-left.png \ + search-tab-right.png search-tab-top-center.png search-tab-top-left.png \ + left_triangle.png right_triangle.png \ + kmenu_basic.mng kmenu_flipped.mng kmenu_vertical.mng \ + search-tab-top-right.png search-gradient-topdown.png search-running.mng + +kicker_kmenuside_pics_datadir = $(kde_datadir)/kicker/pics + +EXTRA_DIST = $(kicker_kmenuside_pics_data_DATA) + +kickerdir = $(kde_datadir)/kicker/icons +kicker_ICON = leave recently_used suspend2disk suspend2ram diff --git a/kicker/data/kickoff/kmenu_active.png b/kicker/data/kickoff/kmenu_active.png index fa5ae4de11b105d70173a06875e11de61cbb9edb..7a3d6b08f52a3907a4b6e3d29c6a068a12a085b1 100644 GIT binary patch delta 4384 zcmV+*5#R2)8L%RdBpd;AQb$4nuFf3k00006VoOIv0RI600RN!9r<0K(7=H(JNliru z+6e;`7a&Cz_k;id5U@!^K~!ko?VD?m9mRFWf2VuqKK3E84=oZ}R!<8gWL~zxMn*tD zfH4SUJLSR^`vd-vR4TD!z*TX|@t2sY7)+|1DhCX9nX0&w;FwrQ4tB~0gv3J4Up!>YkZCeWv?Ar_VWky07#fA9Vb& zoOOBkV5#Q*#;Y%sskHYzAFv9g71mf#7A0On!|Cg5ct0vRd^CNo=L0y{U+qKLI&Pfe z2lJG8prae56`;n^XR1%=k4ny2$ojC}RnJ$p(=!O3F$MrZa5%pD&3|<$tq?#Yx~NQ= z_+DbVdLKQkJg>B^FjwzWJh4oP3BmBA@7|eotkR!)I$h4hGg*B-@7eMJ1cvDX9tm_9 z{^IZc!1Bu%apkfOvKgIpC;duSJEEj(_4W1exD;E`CcYmlTi)K&&)+=q2Ohou=A=Io zYgwAEm3exe$?EHQ&wrK=z}RU5?p*ZA?tAb#uDt4F+`OWTjobRDe25z8oU1|5)dB3J z`hlV=0@cRI+7z#iHQOQh8diU%li$3qdGO)qc;wr+B?C@_GEq4b&t&!Wyl2Zd-QtNv zn(X-=pSpYw8{g^Y_8aHZ+~A*+KzAA~pLhlcgN2ZFFCFC5%YQof-JyL{`gFb(=L}*U z%9{q@Y5S1LC{P;l6eEG1z>c_a&x&P3U@g9<$Y_P9h2w>QmaLzc90!aUhf_ijIx{v?791LKs9Z(z=#TmoxE9R$tG1wtsxqI<%UuhS!HkN-!jUF#{4>2@UL`1L%N@^n)PghmR(=x&5 zcc#8QDVlRXkun4apbQH>#&{$S3 zYek6=SbvKkv^Hw?9xt-*=m=V(h{h6Wb+sF3HsojGIU5;M!OwK4%?XY*Iq25_-yjYfn{IfLbWl0e)FWh82rupafQ3I+KO%SvKw+W=4ai z_jn;TMS`*p5CY?9$SSU1(!x;DAOaZ?vYznt=01Y40E^&ab7BFdsw+_KDVziAq5&yU z34i_Gd%O9?FaDM8?(PW()ZE<6>eY9%`tH?}%1G1o?^?I+S+;K3!hK)=I=9|>TMeHH zZOZxCb99;&`xPUuw5Dugn-$|eQZh-nHas%QqV`6*T5^cMPhKp-9U3wBO{}u2|}N0Lgv&0wjDP&W!{cOZ+rFJGn>W+f*GIuKMl3xiOU!M}@vApZ@b0k^IDfEG&8&%v z9B4T(_*!trVI4GPJ+59fgDtxT5$7N@m{LGcG&lq$f=-1wX#grtd+XM%0IXhp_c*Yj z7MN@{lhj*ddHnIm*sx&(0Bvn;6bc32eDh6y``hRE?%#ipxpOZ{Y5T-7EApnh5LWda z_qC4gslbKcieFYCr+H;hmSl$sT?G2T^tKLM~%!1MoOgSntw9jHFivdjHl27 z9)=kbuy}^$rujbiTyX&+@bbPx6q(cH(&v zlSt$!6%OU%C37nGo(A;P$&-n8YVFl%V1*}5C#h5uvVPquqSO&jo9lLBOYA)|%G7r@=0nvICYxX84O77ab8 z4B`}6hgc!)bX6tbv9M+LFv~9q(V=6_UdFGWKzH#>gv{o4FD20}5mb9|(@DMw94S*=b*<_2u zxoVUW#e$|BWl>w+Y3NfS67X^3f9#;_$HoLxW?85flzr(SIO``ct`ATG86t4#4r_$0-)a z$mQ}0&n>HN<+InXOu+f(H~yM13~|m;E=4oL_x)%yIF?tT{dmTd%K;{|D6Pol84?bmgw>Xootoacu7;c zauBUpySa}-DU3~1^nB6mCa%4*gNhZ2-9$WD?YL{@${PUquV;Qmu{bs%kHcc)u*qQ) zyZ!$A?-7K-INlxaz5~F51q+gAo~LPO$fMNwei&=(oTfULurLbk(l$J!A?pcQEo3~w z_kR>ePZrx>ySx7k04Z zl1msG8sfR<{=0^^x3`xKFTRLUitARaNPp^{|HJb<`NR|K+O@kzRyF)psp}k~6z6Jt zs|B>LFvg)37zfHgDPGXD&dBoFML9O^FM-yS#N&tSk5IzHpd-&t77ut6$@x@Bah8eEMmQ9y!Y8S6spH&@k)QKTEM# z2m-NJp2M*`SJ}&u7R8%MJC2NLJM|ugjJvHV7L@H4PQsuztORP?f*MWcYnY@!QvdW zG-@vE%=7V!8#y^RQ1h~uwl;pe{vbWQ1+WIJi}EYh(%#X;;!9e2{`VXC-hC_2*zqt7 z`QY$j1_uXmI9glV=FEr*^i` z#$U0r!=qIMs8$eKaDk<(&5!cd&eD|8gyoRUI|o@XyMgPLwsPIl*gC{Zd}HCri7^fw z*IfItnG6o}M_Q$YYnQd~Y~NU{F%A*I32_E7dYts>bk0W$UGux>nt$I_Yr9X=wynLD zw)WPu;+^zEn`s_(v!?d!l#*zZ*Vs7T_Jsq3hL)M5T-e^oyZZ*&*&7h5EIWEf=$bLi zEmvPiF)-1hNhyB*(lHL4uvp`G>%b`Ye6p1V7dG(0K#>>TI!?_wI&tw~q3Y=@YtG*T zy}@+P7Ft`o$rY_Mg?~VS5(h?Tp4r5k2S#vvhcLNDGIC_B zpRhO-h@tP4<;my!xS}i1o+G1-j2cp#m&wM5vndU?_PhnWWT%&(^-Mgt%3(_dcpiuq zf;e~WTm*XASONp(z@p+UwW3oD70P6?J|b|iZw#e9BDs+kTYoFOeV|aaJ1L+^AA^gP z$DGH27mc1K;HK7wD-nFH5OK*t9*-!mt*u}rTH=S(wMhqhphi}U3UoCWJgzm-_mx+oQ39gTU9 zYnENWQ=1ObOh~0q=VMXUBs&S`a#mcn@7R@0=12c+7k@K5=W+FtS>&>siOzjjX&Jxr zl1Wa^N-ck_ETK>~ytV%X*^>wO^V^qjK9^Sy&WuCK=>9M7yl9GXxCseDLpGb`ufDj5 zA3wd54Ttun{{2wh#}id+87BW%L-k(~}UbM66?G7D+4;MZ~z*QKpg{@ttuDqEHf>ZDeh1 zS=6>2n=%Y?jQkQMv}R(kt{KVAe1G}_p5yg-JY|;rI*xaIW2W=P+e&pjBU@0Yijc|`eiV^^({U|51Q3)(Fc`#9J>YY~vjmGOJ(^;yM}JQ`}fOjqFE znnL6Mg;G5pB?qUchdW-`Y(*UJh|NR^D-;U!B2ha#C$+nSwWTb>V!f?)3dw~_|FK?b8M{2Eirv-V;@e3 z=7*Y`?Xo5{t*?~`M5azRHowlQau3qNKc}#ANxo)bs56zP)9@}R-E#)=$cl0%-i3jD zpN{wkM*++nlD=hCo$Ou>X5&}R1j~1fK}`STT7*}nZOjpiTGz6o-yzX_GMFmjaj`=R zr8Q)CCv~(e0ej@g*Zh+4%U0%bFG!h!`P)&BhDC}Kn)vXnLyU#=Zu;W8bd~}o?vF2T zl;hvKr9f_4T{!lW$Un>Rw zZ(1tCLtnmX7&Q6e5tv{2!J;C)@fx)txU?m}^jCcgXnXs*3ZKIXYSdp|ag3YU8ITFJ z*W=xGtoc)u8K2_f0v!z_a_*cWRir)17ffuQrJLaqzAWhxEUR8i4t&{Q8cF|f6>;iG zTgL5RQJ8`l75F{J^hsz(i(nj9yCLfN=7to~R%5Ej=pmI^t7KgB@vaE@k~~^nKy~$D zhjiQ{vNq!zNH~M%q`4Cz_^#G$|_ti`QM$Qtn~Ik;t8 zc4oKRqR|&i7)|QePHVcV%~z0pp+I2J@6(GiPX^6JuJwp>n76XecK$xy06Cb2`z4rF zAW-Hz&0Q3%JJ9))!|t@CWuaLnuFx3b3e{ckiKC|(U*6QPMdr%g>$QJuk_(79{&$a< zSq`YvVnCfRLwoF zN&y}LBL%f#d1JvA6;~Q>2}EPfAXg?Z=>kyDo;-o)P1xZpe5-KM*3QQVMz35#)lK#< zIfCb69`Q@AD~v)Zse6V5>&#*O@bDo@LBMxoIoO0(8ujlLFMr%$cU8u^4L8v4jz84) z6TU53*y6t|oI>-Drid5zSX(j;Z&F9R*As4saBmrSHRrY>w-Kbj^^m^D!&or9`1^vz zbZ?7d8Z(u=YaGP@esmR7a(;l5bWUa~JqQ6;y6;Ux_J*`}JxoWXadeF)kN#C)#ZtvQ z1xDt^P?sJ+tIKN*5l;;txjNgNolGLL2&3>KxSsj z9Cy?%nnXq3)-hPIX=V=LRU>EjW90jv6y&F7fiXa0`~MJlA3@AczFus zI9k!-{SK|kZ_LV42uw}Q60A1sgrlt~z&+mDXj+;PouLU9NeOc1wz?%O!nUPvC zP3sZ*gV$qJ!0QiGz$X_$^&&sOt>-uid>o97zx8MKMP(7^&Txq%Mwa}=B2O1BN6{K+(agJ zF01dyf2f#!6FYQw)(v0pVBb*`{z-ntr^#QH+cZ8XjXa}V=0;I@NT}=K$PKJ&;qHA5 zt^h_{-G2ohW7Lu+0Pp)aGMwHc-Wt*R_Mr>mJ%{{W0^U%yta_qv4B>{#Y+9}lUx-E> zo0*cv>fG2m2Cf!)D~C_7SaD}Arh)J757J)AP4ePBFH{hk{w(~aR-!?gD*G3Zk0U5M4b5sKQmGfYhdZlzkm zRXC%)S+bQytjE+*ikEGGp0k)g_0=qM;u|u1G&-%##E?|arG%xycAginC0oc8#t|Eb zXc3`rNaMXzQBLJlgV*Z=3?~>>W{2!I zA#_6rF+2kUFqYZA+dOIMKRh>CaMIA>%+uydeK#x7|p2YqIr|`sUv(Dc_Y8BEPT(ob* zV81KrZ3sTh`^B8F)SM1?($_jplWbe7<;(hQye3p;9H`Ur?8ffQMjR=7_@NX3P^Mp< zsj!pZz%C@BF6i+TpK#R2&LV2g9cp4!bnZ;veKP059@b!6FUMaKuk+6fs8q1{GJxY_ z(x)6hDDUwH$p%Z~mm926+87%l?XsG%b9*8uF@@QqiL#}YWBNDO<`06FZ8UqDmXtZ# zDRXXJ3XChV?J~<(mWY>ZxXXnvagwdg>lw_5HPi(uO#%bwdDkJQ|IgvXXe@WpwlnHeG|gK<(%R)tvoCC~pI5u+!)RB1on6HP=fKgrIS4E<*(Z$L9Z8q5vZXGdoQam| zHIfZDThN*_$XC0D^1PtTThfH@upPUx*9H{zzv`N z&J1ddmN9uXB5(Z0>_d==U#WOY??t544gl7x=}Lp&q-RZ7dY4a{?O=`8Y=YVkUMiL$ z;{GnFnx|uqb{r8zZWS+!G*CxS122A}Wglklx`>dH#qpcPhNRmV8mTBT?D^)Tani4= zv+;#E2iS?1l~3<7JGvB2`q_1e5nxV$D4o1}K)qy`!)arc{sY70M(d6LoR9HjcEFiU z&U4MH1G_V@v;vHB)CWGgugt@ID>N1J%~ExP;w$xgBvM+rr!TnKfzeTnJAw!eVqCOE zmhNT9EJ~lLCmMap#RByE)C7UtMWnVzH4DEEAW(qv(+%3w TS*4Dh{{wJ!_He5Ik(Tj)Dyr_g diff --git a/kicker/data/kickoff/kmenu_basic.mng b/kicker/data/kickoff/kmenu_basic.mng index 4cea61fffea646a623b888b8095e44cb466f903f..d3423d8679de616725e7525847aa929cace4d165 100644 GIT binary patch literal 4623 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 FV1lk8^(_DZ literal 81101 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 z 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

uM& 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%0Duw=TGTIF$u_a}aN(KQm7 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 WKcG+c|3}3bKoMH*OiA&u_`d)Q;R|#C diff --git a/kicker/data/kickoff/kmenu_flipped.mng b/kicker/data/kickoff/kmenu_flipped.mng index 2b78b40073434dc9a6824c45e5d3c7976bb31a21..d3423d8679de616725e7525847aa929cace4d165 100644 GIT binary patch literal 4623 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 FV1lk8^(_DZ literal 81240 zcmce7V{jl*v-X4=XE(OZO|mgJww-Kj+qP}nwz08oZEV|L-h1m--M`jc|I(uWCBh@-JYN$?{@(%;U(bAvTuQ+A zKNdX+5yAhqK&w8ke_7zg{#O`KkYAP=WtFxs1?^<>+xScWcYLFzW>8pqW+h2*jH#m0Sffj}TIJ%l$+OIUPB?v9+YS@b=&>-gaCdX*=l-z{T zYv(rB+3{t+Z;Y9-9MTD|iu(KE{4sF9#|B0?OhXP&Me9=vso*5@B_Lp#WO7i|=Lfw1 z-iufIBbS3S&r%kL3oF&eNKHYjrnvGmT23x)#d=G7XoM0z2mmfNT=#?sUN#Qv8Cpxt zL$Xts%r`Su6E04SYO25NpFc#C$wI2QW`x^hYs}EEAdV$mr475z#MuTvODid#KH$N9 zm$*Ka9$U_hLWqZ?qr~oPBnf05IxH$4JB{P^zO#SGOVijj-af(X(q?&V{FPAVQ^kYo z+*?nJ1j*iTO*fx&X%w6Ha4?$B%RXMQWZSM_`9`vpA~4)2m{sM+YIY;!-7m2AUd#?1 zIGh(@*rgHnvBlGI_OUs1?sWac2<)`WSeupOML&ej;b%*!IT-)rs^9u>h*DtVEo@4s ziu;WX<2`#4-J;|*QYjy=o{+3XcV_xtxqyB$tC+^huCj0GOuD$8zwut}xTONg$A$vl zdN5K}ukTeNkCLh`_|$Xl@gP#gIspI;X7c=xCAA!v=}}}KHNA_@7FW9KfAP`F!@MoF z7Or+z8av%5#_XG2<`bN3($mBaP9jh8;yvT?k)U=ofUDEm2Y;f{Jwe2Lptb#Z22`ac zweYx``YDJ{Y~nSHPSF`(;#!3(Fb zwy?PkJd|?20dS*UQnGjECW(+Y_xry3RVp+M z;$U(iB_8tM^k>JRNEfr?uLCz_ULHV!+V@`1^ek7C^V57M&O%uVI`=u`I;KlUUZo0I zY3^S3M7fAOC{)3Z-b^KK@972(=k#|InMUR?ViJHa)~fa!WnKXkSenc78PuZf%4({B zpmvO_{RjJpKyyT_X@wQ}8E(`MXZjx`aSN7+i9-9s32W|P00ru=HqSwS$Hp$tL9$O< z1rHqMruF7|hxR_~i)n+>+kWAXkld<>}Parxo zecEo8idNkfbC0XpR3ZKSl$2}$pCH-@G56{4h4jye+zIbN zKFWCtvS515?ATfu>SF80=z=k#-HA5kPfP&7)xl_73$3zRB9-Pn@gQwqe660ENG`x^ zgV}Oi!Ony$6l(g2*L>|!o5Z!y&xg*_SYGjPUBrr(PDH&H;d0P&TM04##d&&(%+H{U z)&tS}c{1o-boL;Tv59{nx8RPL8sjk`qeu=n@m~C2;^vm7YNdIuru&>GP>tvI;lXmnJSy#Yf#IvfiMdvx*Lv)@#=0 zw^J9YbJW5 zKS8~L!#mu3vw^uw1sjk0Dm31{Kb@-LrPX8qE}a|VC9Gg_bxf)}|FZcaLfZ=#kVneA za&iBb`Js0n1?uiio2*Kptv%`8=^;W5^oVR+Vjwh}kA`Xu4uspYuq~mjAA`31WS`1@ z7Mp2A>LM`@l_{PK1AwPKEjNPMa!|kqVHr~3n*=sV5{sZo+;8DZrSYXH`0??S?1bsQ z>)~(dE#ZWafWQoLvscO;SR&}t{0TyrFYX7h@#{6%2>_jZz}b8F#ckP%v{DKrsEKNY4(vJSu$l+ z^TS>vS9iOlADfnuISJCaN6S_2juZH|f+9HNkG^f9GYl9TC|*U#hXX&)%r{es$FhU) z(hOvc)T@@cr+@7c?m;`4l%0ARz7M{MsACqQZ8ZL4Si6j}pbpm*?pPT#?1HbqaQrFm zG;EQas(h1M!soU8QFpC+c$&P?RWt`#I$gsjiZj&6C-ks-lvTw0XPQfB1{Fg;gj`Kn zAdZ1nr5{6pd~5iMFq$e$JI=N9(l@c%tIl)A+LCjz!~Uf$gUVR1Gig&nGrl=!0K-3R zHctsc1Glf$5YtzAM65ZPTSrAfJz+W8MP5PuZs2eGpY$>3^K88NKAbwaW5(c?=06!`r1u&*yeWS$>kkU8vBBG zCH*(ALPLLgsS&Zh=UXPKC&8?-!qlL^ze!;tX%vAl@P|j}8xD*(yP5MSV?Ps`7P%ZQOV&0=+B_k-DK=&JIPyJTHyxQWBMi#q>RJCwayp-AYc!KF}9sR zzpF;Z%Sv?Dj0&DKsTB+DQ1RISvv)1ByP51@$>D-!UG)Z6Wma%EfV~J+vGeph0ONb6l|)nwZ$lSH4g%XfP_I0D5W9%q%z0E$W6 zgRxIS%t1;{a|stIl*a3sd6=+ksn11E0=>sXenGpbd2M4JY8!gRyTt1`!d@T2(Bbm( zSwSd$+70ys4mmP^Tdz*l~&`4$FR|& z%9})Kjo##xBp5714p4yYl6@4<>NU8Qop2BgNWe(GQ3+du8z2hOQwTdI?#-zc)uwds zsqNasE2g;KQ+<{f!c#dYzhqC4#}!Wsc9NH>J6f#EUd^s-Ep~YliBLb}5va7!-Zm>> z*DSU1ys+%rUPp#z9wB1x%QIgg1AsU~zB;(ljtn`*B>IsCo5I|7>THWLp4XU0Ruh@y zGZLl=s!g?L!Bf$WY?0wQIJ>YdJPHp?Ox*8`x(~ zar{~)lJF~Lr*#9uF4Gn$Z$`(eCvyPsNe}C~ROjBcno_%T&xqt5ckRPXi8MTll=?EF3V|kFPsLS=j9{#NePRs|b*Od%2QLRHn2*2tWS@5o zWZikkm)Nk~IAiYO#&i3W`hkp`+lNMW;ROU9)gU8PkI~7{7KePFZ9r1faEA}49m5~jLZPV_Hc02E4hR+rI6rNpx;*`f zP-n-*Qut>3tw4hbw3rFl?;E8|d}r6gjoVK9sm{kc%dqc$_@8IbjsYu@0plW zJF>y)s5o&GLUnc23Zztc36tp;57gu=7j!A%M;4?Q=cYBp?g~NRQJHUVhChau?k5Cr zuut=aVDY)syn5Ad5Uc8CMj5<$N< zQoFBVXiDU@kPfv~mznzKI4-F*psIk3=^W_LS?MgY|EO!)q7l!9umeqf`)b zkYVxWoPVf!Sktm?KIcM3Wc+YJw;8@mnn*Crkt_&&kYuN{o>0amgKT=2ygGv(Cb2(bvCCOyuqsC8Xq7lb1Gw zu^1W_tadD_lvKByLXr$ne5RdV?t}Bg@^!@vVLz_XUO-oQ-~l^QX&yC-OU&4o*(%$>%``jn_2j0zTIdte)7zV1}UIFOn2b1#IYR}_Cx)ceRmnh z*$X9Lh756{)7>n?)&CRD^EBmR_bkjAx0_BXR7`zz(>(ypN61Q|f9|)b(c4lB6PfpD zskp?fyd2RqniS=wl(Sj2?V)7&5v{-Il*&ZyNujG{ZI>)GpC#n57BJr3-5um*H1WEN zzyN0tEAu$Cih|#&MOBEAmbAS|f&=j9X(#5>iJI=Y5t0@iVQlZ!?lHGYmI>D8rg5Hn zw)2rG`?7)^-q37&<|;rq3HjK6l%~v$hV&Rv|Qhupl7NcBHz4shuDJsMZ~sSu4?|DYZht zXl}olz?;rHM(1V-Hwb}pa^uaAGFYenmlHav*enl4e@?btYD>%8)rqqYeRPn8 zwh4d>-e`+F?}nF1+rX3`V!Q_~rbhDrE?fJgA;qrl)E<5P)W_7P*4OD@u-v24G>GoW zzAYKpG_YV|gi0PE$Fr^&C!zKy?OWR;42}I=Z!Aih#ssGM`i*3lVf}1_Q*_06*F2|J z`eNVOUL&Qusm1OaqtBT==!y!)}_C6UAMLx96)YJX{G0 zn59sA3p!|iPFtrq)IGF>c<82Vq777Q2wsSiSYDhk}|FhSJAQMr5c-(Uw zGXkN|v00+_4iZm!x-C(#edUBJC&DFicCUNgi|`)E*Du@F&l#H40s8G=|Xj!P}d^ zP_)wowJI)9<(-lh|B|H|%=O)wa$a6wv?8r{YD^nfQFP(&P271_>*6=ROHEf@BZBSB zB$P`RxeD6o`Gd*_)cyG=$|aty8-fK~(-D+rz~m{J6+b6y%f&*HFzDp^`J3JgHau_E zafsH8k#kBZWwb>`cR4PA#j!QPKJ#p}DN4D{F-CPcs>aF1_c`>u6)uzb9Pdol%rjJz zqe46;2Ez9XGe$VRQy#DxSo_8w95hq-OI}}Hcq|Y{iBP+s`XEdSmh6uDQ%2f?Y)Js_ z3yS-`22PL_I6sPtAE7-S=AWMW z>O&a(Rsp%WT3PuN|DXwN0@F~p4QzB~C|MWpy-Yt4A(1}twO91HPrbI{DAxp%8y>Ri zPmpejq$f)P6PqS3h%H~O(nztCvsY!`J;?!sXksw-e6)+~cf>3r0cPit=fIr;oBE}4 zj6ZHI#@4K(QWCcap;N|ThD5#)d~kek%2VQk{4qa%pJ9^_{UAW{bD`olUYeS4CrEi; zf49ebq@iCw8ojU|yiQ`DD3}SX8KI0m<@jSkk*Uakj zvv1UQzrw;b4=VNa(Yxb_hLSqRCH3xq_+{QfU14JvOlf<^qtA&=mKV7ke@lISeRmWe zDl?`k6xGI^z#4-g3cFj9lO}ZsJa#${*H{r`95BwXT$gFH1R_zNtuZDMP%}|Ejg@$hwIt2@W za;bKl%8^ytvR!T|vnw_p4=aLFYNg&oZ;u8Apnhh9LV)J+d7J47iiU{B#LD;mtsw{* zhRTkbA=miOAtUN7DkH~*E8ur>7v2elk`e^v8JJnQUunB_Kz-9YRkvSv^_CJ9lt8b;mIwD+hX4%B{G$u)SMcvds?}>e zC)M1FBF2w70ZZFK+2|RM9wwtz@iQ>G_Vvc4mM$gpJ zgbOgh^a*?A$GGjZ1tK#X$H*iLZD<5q^?rqLOnsW|z}>cv?1U)yugljB#GIAG0mt<@`;bjYbAxqIsH}5`9DLT$p1tBx!Y(ixPESQMtfeGIXA8blbq^G zERNOTtkp8K_|*O=(DCmpZ=j6%D=9xKV!|Xr~w_tZgjr39o`|j?*CDXyRS1Of1mL1UcY^sg zZ-y&G+4?9(zsO5yyXJ%p3@=4~=^_Gh;0%GlM4EH*c=gX<_6a@u^Q%F0d(DGy^j$9~ zgrj{DQaz1+y6@Ip??Kw1;C$NBwnv4yTAzB6y&W(0-1xvxx_g6a(uSk#i*bT$Zop2u z_OlRob6W4{aNh6gGeCyFa8{CQd~*nRbC<5Y9kWghrN6u@L@&!$D1RnRRVA?>v}t8= zYmz*`@V9H+s>GV0o)UizX8JYL|8u-FH0;`Qs#*3OX9`xu0W{Qs52|nyp3P-y>OEt%Qb3UkaNMo?I!#Q z?zT+-1|mZao~8rS3$I6`>U+HiWE|{v!0z)v^POmi!Wy-w0ki=}q%D;l>_Ra?u6T|% zEFWw`IsnM^^e@M;RMBXaR7IhlV3TGsQTg_>>&s%MJk@Z`_ZLt}qTQg{rW+M1nwZ2H zF&$9dJ|tSsuJvK2Elc~d+Lc)Vkm!u~2mXlBS*kR3?ZMuL=P1#tL&GI*wGNXI18*>< zG)xttGe;5ltCLB?18tDC1v>hI)wWXYNYa8{vzuh+e&Td#4?ph<+0DXGNGQ~M%0CMxP%BeYeR)2o3{B87MJJQe=>U4))9 zbvS+SIrA=X%QIrb#rwH%dF(yp=Cen zI!b5jLABk~!9U%_g_QI)&H{l2pdLDhc9&Ta#K$#$GQ;BCVKRvai@lh8@ilEM`zZ6h z$OWEHXWl?!1=&;HOfZDh->=6EwYc>+$Bt(yTLzvStC}@z)t?X1O;XWuxZsDo&s4mx z+D;Zo2^;{|=X9|;!&1FVJXCv@!O`&0alnPjVF3pCc2}_tMNVe=ZXVf6BtMl>Y8WL- zr#DwTo%D279a66ipsiuqeTMX4lfN|vI;w>`mQO(<;U?;&ROUErxxnl9b5&;Qx30uy zNr#`k{`pjA$-tQ|TUbJ00eYA~DQVToGq2ue3zf}lYfWBb>T~4Ws!v~0^RE&5?Cd+| zvyWIDqVbv*lQ)^-)|?e)h#_7fyWo9RApiYqX`I>`u)%S6b;d#BZ2aa)pHyrrP72jl zcwntn16x&)0Ryx0cp2rX2VV?%XDYp&Bfm_fFJzbU1kYWou()vM=~~e4phOogJ4 zP^8LtLdthj@frzCWGzaQ)q}SjVf-LX4O&F=M9i5gh9lc`63-v*u4g(^49;)u7&63} z-zn*FP8ad+yR{~-D#RmC#|l$a?HQ$JDPI{mXF@N9}>l|lFltLP+C zeo>bjG^%4{>+_khu1PJp7O<)}S6RT~V@by+un(=m)}#~^Ga{`ax#VZm$=a%>J&p6n z7%_U-?`u0bMB&sq5AoC&n2PU9xY7s~)^uy%M@H4A$Y|uy$9MMTFV5))uj4q|Mr}7} zv!c=)4i?Qs)kaU`ED!!f3%ZDZHomJ(Wh$X{!8H1e&hR8?C_|nS#hOvKmi&E zmSG@Y`527RnMjX|-Muex#1Q^ye;B|#?cl{%59w3QDGOn5IDa$CToZN^7@7dvVc!+C zlNF&zH91{O5L*ajg@`m05tn>|$e(l-bIIXTFkrTU*qBkQ!RTR;#R1P5!Vxou3(xA>pFsdR@({Cr-V__8y5-V^Ej!t_e&Q5jH!!2TNN)N^sZ;qBM z+(rHbnTq|`*hk0H2AKx!x!q{r%ygs)(#IgvHvQBRiIY56zN;Wq$g4&gqcP88nX^-y zz)qYoDOQ#Q2lsAJ*doqiC^k7sdsp4QD|uo4@T7Zs{9VmxaF;;|5A>D3+1R^o2Mrr& z)v2Dg+3&}8ZT#Rzh4(o}7i9%P#>4LnI7YM3r&5Z{T2|fkZ8mO#>7O&Ft*VmF56oI; zYuW2qXdzeK9#^KKaGMK;8zp#v5R%c*)Hjm?f!!olns)Eu0_O<~f9Dfw$k+TV@rYno z70~Ytth4ky8K>S;;tAuVku1sUoQfK5B2XBgKQ}(|4|FQDk;UOm;W|hy7ut{;3)lFu zdJ8JFuWP!=yv&b!R{o6rEiHt;oeN498CnBex;_>w z&tN%uQxd~R>hFHnN{HCApaZH!+JpZr_Rm8QV=ctS17Kvw{YwP=Ec%c<^c?j z;~URU{0q-H=J>jJ-@c|Sn{?g7wC+neSfl-{1@EO><VwGpp>A_1if^w^MXaZVgV%d7LU=y=f=cUByXRCgsr@c*ESAao|$}wYMl%xCw zS5;7jt>rGX9A5}%AVoTq(l42dO}26k5)sNwmmHEUQnkhll0p>UX*J7Vs9DPnWw6R3 z>FHezKd!9VU+ZoS{&{LVw353`{=?-kqOkYUlD`!;)W6>F?dxKtQzM1k%xLwzSMUU4 zUh&jQT=VVvMn&ESARK4uxj*I7h>|}T3lAfXOJB51716D1;=?@>@EATuR1^oyprjwOF{q0YB^sU*9?}c%IdLG%wfUFVXlw>0H(vj&LnTgG$6pdh z-j^pMMH$$Gov?nRyycbde5BtelKoyhH}{&ADjznb#QiJJ7(Ue^-K$h1xxBr%i~W~c z_nId5m?cv@ikjKJ6e%ZYyrbZ(t`*HH`MC0wo_K2^Lo8|L_rg#lBCe0Qg=DGCQG0r6 z$51YcS2Cwpuj3AB>4;hciOLEUOh!GUx8`Z9+Kp+?B2O;0VML6^$%Hv&`gRAy>tWS$ zXaFgs+|Chw4U2U4(Mj&&A84`4!44MIXmc?MJB47;^@#S-90~ z_R$(5ki!QL28l8sc5VL(mn2P5C$BY4fh6?%KQGsb%a9c(DT;QCik<#aemsyXY?Yu_;NnNxM?J4V)1*Is(Dp<)5Bo(0UU@~~QNY7=4V##6>JV@n()J|qbVo}o)SbjrD~ zIIMeYomjhO2EyO)-%X%SdAoQ&rkv4tT|L42tb79!W+#`j*mw;yGSkl#SQmKkt~}3X zAmTqlVIw8=%DA(6RFv1+>Dtz73Dqyw2~6i9V1TC}bvW|Q?i$DNb64nG)TYKA$q#kf z6qH7tw0N0p7%~A{Gl@9`|Ma+6K1Ni->WAyv**Y>?q6cZ|ktf4=zf}gQ7hDn14+)qK zG1IE|)gJF`w;~U_hHJ^Al;bafHa7N()%z3JQD$dsQg=ZO?aN9DYAs)NkOJg<54+ZQ zdukaU!1n4D;H81T_z>SB9Vx;|MKK5QA=2o48(5HWM{Mx!W=nt>r*zUo#Tjv5kTxfb6R;l!*6dym(atf) z4-cC{6z{=>Sv~il4BQ-8F`PlsPud6&dr|d(G=P!mA6EMbHY{h_mTGU;{C@{S;g|l0 z2zCD=Lcd^W`{U-q#-qgQf;crav{((&4k0*%K2kg6S^zkrlg(fzn{@Cbo9T;ayjVPM z+VL;b+3|+^f<^`7Fk7z1(o8HF93}*oejR@X$Z-h*Xp-Co@%Gi=hH_8M4bKhxmv5_v zq$JZ|-$}Ytt$(@WZE`1sW%Ok9iE zzpgZ3aLLWnw3(-qJ5gtky)Ain-#7b^U2}7nb&W)_&Rh4R&!!IX2!9j1WxWIH?F-4$*$cI`PMAmlnZ`{ z!bo6pLWOte_OB%~K|DNEtzs}s?7fL0Nec!tit)Ouhn;-_xw`5s(H(o_Xzc7wRf1MM z4ZBh@-`gLJs9`ExmDt%5a|2&zg9FA3!;^FUVx(#-BfichVbI&`PWfE-bMvk}O^1Wt z>$l3syMlRI$ep3_?RC`32ohFpPncN?ECHgQ;Q#9I7=74sf=Trcs0B( zL|vNlCcD&w_RyhJ8mT7Xc|MQmSXJ#BMB#BV(9hO?Z4je9)|T9%$VD`aXXp8M4>_78 zi6^6Y5z7|k)#)IZM3Lv$-ps-=u%LGfM5}GuFE`T}DvUT&Q=Sj*FOR95fAN<|;xJVH z(hztui3o{R8c^956-ov?j&ZN%`X*~K6K9jGrgd|xu15vS%^9q&&ZRDEL;M^AR(Y(9 z*?rjKCq0}F-YU2~9U3IKPF*+f#9xg*OZzfc{7VR>4(l8 zoQX&%se@NyDR`N0;jbk2+p(;W-2;FmTo0X35r~hj;!yV1B2V!?@ldH^{mJyoZQA?J z(GVK&Js>Xdz*brHPZRTAbmlMDg2}&|{Rtg9np-t<-Zh7Z%p0>};To=bDd=~BhGk;= zHOi*yQ_l*xbU(+jWZm}6;PnS^Ix`h)nTWa5#Y&>Iy*8OMphF(9zObl2`Tm3Q@mrxk z!f%S+(OD5N!z8q0(MQ;YAIEh~>fM4^;_eM#+kFQwCqqHuuT|8qX2)w?-k?1VAZ)e^ zYlnDxY|@UmDLh!0Xwj}tDu%I!5ZhT{6cDPe?i`tsP|w$h;>NV06*B3&C{em7iBj5$ zc)7pNbLOqI%G~rokDa74=gO*1B8>Z-eAK|Ei2(=RchaKxjRr#=`R7Mu zkXvhuclf=o8&?NLZ|$E-Qw%sGGwY5IYA)&P z^3R4}7__2>N72@HPXM3*>BaiV1{$UooI)vfdDmeYZNUF{fL;&fZ&&SgH46JO8n@aX zH%cFHLWPI-5X{RAj+A^KE6G=ofU>%}kSK3??D@k|Oieh>-%t9qgN|E=hO+U*)!di{ zw+_q%YycWC8jo`r@^PFJ#xWxs|fv69Q({`rG0UJ0b+tbbMcT2UPf?^WQ z{Mmz1=0^EIL!otpig<|LoQNnbG*WhwfSnC>>pF?-!j<+1!^pT08;++eu--?4y44Kf z!ZFYuQ765&*1QHpME6IyA8`s`%WTqZU!^1I0%Jy7CGDHp1adK{q{djo zdl?Aw{Ob&j-( zZ68jDf(HK0AYBW90ipo^VS3fm`!al>+zodN%h=s zLPc@pqCZ*USahP9o@dth+tk#BN;UmW^Dci9{ZIbNkzav}aX5@rpRE{GA#~4BgmE4Yvs3=!zTPRxscR<$GI-mMNhkN__FVLv~s}F#hNkCxp#L3TR?!k z7r{&^FY=S8X}sefgZYFVgIv_DTO79A`m~v|%#lj^Sz0j@iE%X`MqZ95HPRON zE{%xFAFNzBX3FNNESvbfelXT4Wj(I1)@`wvPs$J*YqRU_)+59eJO?th`HM%zdM$_Am$v8|?Z#d2IvC=fSagg%enHCP zGZ@fp&8Jnf7|M)6-B0g-!$8Sv_+EDOadXd`Dn}NRpMd5SBX+$`!!es64~OU+5-M47 zHB-vef{d-vts(l9-PN+@MdvUd_jxXLXX5!G<2ZT4EcW{+MR`VaK$NSu{RYj9?aTT* zbu}IiN;EOz*B$B&ZP?Y|B=KOn%c>N!`Xb%NmC~0@E1ISTWu&}4=R7BIOfU#X;A*{s zUV=jCH<@cJKNM7iC_f}kWb@jWRq5*U3$V+D1 zz-+*w3^9U;hF$$@bq9{((nN}jTS9hdlm@Mzcs@hvd9S(Vei^jf8I!9nO(BXaVSr>z zY=2IgJWSRc)xbVF*DI}m*KV7_q4UZs*SClS`CqI&>=n9=tsd!ob=owGodphm9O}G+ zO+yve4}|q<=@K=ln*zvAMGf>dxeic`$_|yqD-DI3hC#3>2zMqw)vVG{j|jIOS?KEsK|!;)^qiz>Ech#6=Y6rQ9{L2tb&PC@4d__PcmGE z!a0V-OIDVkZ z(Xn3axB1VBd(NnsQ2#ww|GJs?3h%9zY-OoQ7Ok+X8SO90KTT%E2@!A@`O1|D`Ds_? zKUe?1w~+hNJjehFf9`j`@^mtv)}}sZkA0z~f2UIjn&`MKBWqqj&GPz9(!TfQ`_&G8 z#*BrFaw{^EcG^nAK`@xY@w-633r%)W(xPry`F{{4Q^g{g>+2x8n~d|@>?Z2_azg5F zgnrURiAPrNCGsoCz)js~4GLNQFtKc$z6lvF>#r`e%6|bFOr7*omZuw{)o*id&r8FNG2vZyP_^A|0RF zFeknCi^+;C{(2sH2y@8xY*mVe{aUp&any-sM2U7Ji6)gklt}2ihmT84&rWR*=<-mcP6n6{B&%vwwuj+tTge`=}s1HT#gxznZuU1V# zBC0W%gU!tfmi1!!bT8d#>ld@lWS5I%dT(^Xed<%Go=88;i@Tnd?SJ2Zp7OL{t>xX* zR`tgmcInsF`>dl#aPIX*4v1hw%>GA!FU{P_<;;it{x9CvK)EfVdS6}8e|&;C_IX~o zu3r!UwlcE|kGsf|QgnPAiRuNO%9)?1jI|a`+TNtNK`R7t1=6jXQ_A0(o3qeAe$wdH zB0;63T=uxYqI88Q`5Iw7q)GElLaZphWYCdD+OlUDAoVvC9rf;<;ls>{elrRsg_DOq zA2!2u7B1hSpkW~y{hSGt4WiMdsv{-Ei#5t@UEp$VKgeA#-RpM#0sL-3F|yG1Trqr! zu72G$-wrTJDZe~U-K%jT^XmAOhP}^7Fl*je>?3$UPyqsE4hS1{fA``&3Y8xjRtyog z^i(%CL#J5H=^qGxNkLi!GC)GWrh5puO_Sg(3|EJl3mgm61N1Id^xE1R$?sok0%mbB z{xy`9g8-BXV<`_n_#oObHjYsa~-`-{B!S%{SLM+xE(9tbtsRh1`m`^ z8khvBy)WToaGT&E;8G&XnN333Rz7u<*(A6K<~MN>8@ZBEMA>ZDCTm_C$Mc^Sk-wR7 zLq=%2A7?G|-Q<7)Wn{m4;{lZ`<(aU3jKH^nB90}Z)Rw@nY4 zI{u5;h!{@FG`6*ZdBOWS@g`uL>E3YdY4xBwa6up3m8{PChEomT2YRe~*Ym``&-Z4Z zA8RPRh|Zxj=7DU&R<8y>BYw3^!nrHhBAidU8y7v#6^+aXK07RsWH33gcoN%q9Ia4g z+YD-kcVXmW_IaeKi?h=#v|TWH)oww9&%j|kPCk4`tr52_rfg%IHKY`~f8f=?jO9;D zkF)@QAf1>6R|&J9FIkzo%+p5Q)S70NGM-#dx1)k~qK7Q=p}A)PEWaH4HI-fiG4Gcv zA4G7zk$tt`g`fc*ctAm807hC-70z6IE#W!lrG!;#j93z_S!LU{j!{KiMtIHhAtDsF z5#iw>3f3GSl|gAORo{?tgRf$DX^>lsk3(?Npy7LUg+_Xu5WWSRsS?et3y(?Oqrtr)b-A~#AwK0I-CObDUa5P<`4msi3I?TeT9 zF#hql0>p^7GI{>f_7yc)k{Hp;Wv}8PtrPZy7r#gh}-(8H2f(b`1 z=;>K{dXRmQDrh}6c)(c=|7>U}Ux|)=0@~Zz$nBJ}1i@B=Ru@V{f>>Fd!?lrwM%Y|C zj41qbbZXHvNT>GQ2{!E-aaP*Y6pc|n`P*hs?6u0ceKLN}8QuK^{?_MEo;ky>-zZia z^e-PeW4Y^j0p^sI7E6|*oR#Tu!uI~6Opv5MI&-AL30bi458g%yJ0q;ulTk&u=FmNs z5PF?+1Lj9@3RM6gPhPimWqUbJbyxx~9mlx=57+za1V<`_PZay?AK;$`>ajfC@8`g6 zA#cqR>eC(UbUbK!3s$u>LF8Yd`xk$%At}XNMG#ayJfg)0Ss&a8s3_GHkbr8T`nXcM&*Oy*mp}-VSndtsb9O0HL-xjLcwg(CZFscT)&ulo2IAGVH~VSmtfsNvy3izyQUV0HzA z_+SSP=#gakh0oD|LVjxE&#!=Gu#9C!Y=iW43zQ3G8|HAn10nEtvS2Y0E) zngE7sH-ASKE?u8sCZ1Rm8xwP)iEZ0h85`SM6PucS8?>KHs|25N4B;>+a&|u&DcT zv2ssho96`AIj0u!$vm*uw&!ZfP42jhnfBvx2BPtafqkI@LI2plXlqFP5$yPVM{>(3 zCmBaF13QB8a`vE}N<2l^u{1JL;34T^9P<^*U8g=f`&X=NUKf3YMF1+r-M5Dd@ZRqU z&WoAMMS~wVwN-(6Kby-=#CZYVXO3$PI_g%ZAd!mZ zIus%@HW%}Olqvl#eBh@`K@DjS*1qxi_;@FO1fUPUER~_qsG4*UQZ~qH`zq2j-prM| zfPLStope!E`hlw}kT3*y#)aT?Z7nv_A)jRi4&jPCRjb8%KX)d%CXv8=m7A+pY@ACc3sH_A*F zs4{U9sDSz%boPC<;wtE#c7LE=!<2&x)DUuZ-fD`mg0xGT84+32ztWthV7Z>J1n5fnXhEHfeHAb&=V%IAW2kuIsWp z2z#`Qwlmi-nCL*`u?q`$tte=gHI?mzikA(VFx&yra z7bF#ch%9@$o=%jLk!_2f(lztx@aTGLXi&rnN07lx^rR6yYQ$P{zlQ+dXJjuNaV!~0 ztL6Le15=-$9SI!?(sIpG5i_Z!Do&@g(Mp~f?RHxTqiXe{on<$z@y1ckziIDP!iu83 zK(^$pHmR+rV1({q0K)*3?%V!QuG77L0y6N|dM9t|lCdgja(v9ztoCorACK(cZUf)o zY($gws{T<|f6oI4kiPR)5`2HFV)C)b-yEE^w-mK+R*Et~fF1w7ka7NdU_{IG0oAX; z%74Vz_1L{A8f2_3@sp#}B6ZEmpsS*~?~3a??b5ws1pkqR7meNSnCw5en{D|HRo;<@ zWBtp1V}d@F?Gg9yon7?xUbOs-Uxi1p5>DJ?eV5xK{gK9O(tr?2F3-tiA9;@3l^xV% zJhlR4kz{P9WzBx=Odn>q(vPR>l#!iqek#jEXRD8w(EihRUC172j2|cy;4VGHqGjf7 zfu;#7D}B7HZxIh#8#nji)A8ik2bF3NC7Tv~<9Hi4vl0B=i_1F;a9HRu9n~XdFWZy2 zSsQQ4Lq#@P_bdRwr#ZN@HSkZB*stUv!f%NpxCzZb$g|{|H7RYJ@CkBRrc0kxQo~70 z$Qm@E7(nbJ2QFxb92rIb)`PwN;cDkCPkRhd^(Ih(?htQ4mkd6_d$QPV!_kx)f}ca- za*vbxZq<-o&cLH$ho8x!oyEiKw_m6seOLKGdz1itQ8Gup)p~1Z=}Q59WHvZJSm>X# z0R;cB(f%Mv-f4bsH+}1AW&$3e_oL}t!AcAoLt8Av}uB+f-9cWy2yzuQ+&`mZX(~X$VpSSeC~JRp8^@} zRVRaGYcj5qijgSdxLl#wZ3t3+e?pzU8*t2~);A(>@W&WF1(brvbPndWR?|s?PM(L; z<#x$ng|N=|d*(ls1u~v}d`m13_DeVGk;ehENBOeWw|uq~SeI8(*ssxP0sr9m zv?#jhuHR*QLpY+4uoPi zV*E<3-FojF)pF#&TFQ;qxR6Xoyl{R+twN+`F@^}x%kiIi19UX2yrK?*(m8>+EOe%l z@L)$1t&up$TJLe>=v zW7^JE!Izk4{ZlAwc)Rbay;5h%I;EiZ!`P!K_n%v??*kq!C^$*mIkV`D{dtOJVcJzZ z{xtEze30P843$s-4GE8hg#$j9-n{oy*h7KiP_7l;C`Ga!cs|ibZDp{khhQw`-ADyiCtBCZu16G9@=S zD0MUfnHeKNf$+`uLO5<7g*{VAEK4r^t^p4H>^bz#Z4atf$rVl8XFJWSPtK38`cq058B#9YmW#dXZ-MMru! z2u|8g(38M3dbUJe1;FD05}Ae78&8EFy!n>~h5h}>d~a@yE2%zB>#`hSugz-7@vc@z z^^#nGk*L zw?=8!J4|^9Me7;@XlY8}d-1)_FP~ToqHzZ+Z8Fn4?B*D;U%TLe3_H=^QPLnN!Pw0| zCO)sVzjRGPbQl_(xH(r{wz7{+3=YOB{|WM|u%OS(!7k`qPhH?YjJ$mV?tp>HIR*r^ zyhaBaRs17TRKq_nSq{R99I=?THfe6mU4BIvi&23^5BZUpUs$*hAtM_m`Zl|G7YcF( zpDh&QaYtMWLLTz_r=OSgey`%c^s-jD)1&(ShHwP;ZOQti5l`<7TX6B?zW&j1+>0@O zC%kN=*E5Sh+*lWAfa zP|M%QZ?vHz@(SNuk8;>*{E8mOwprR(Mb%d>){+)kmxns2rH38dyD~cx=^sY3xPEt6 z)3ux4(7g0lTsR7*Kr}L8*n+e1?I;BoFP79H95>5*kO}?g-eyyop_H)+X!L5vU_BKd zy3-N}O4nxq>$*?2^;&qm_4CnH4pEQ2-IAnlvKMM*M!7JK3FvVZjaKGb`vV>o4CU`V zIcfEN=u4Gwdnov}VPk?FRm#$6+MMZKuu?NUhmGlMmc>hx=4D9(|9mEbR45`%JpEJiGri^!Ha%>a2nUTuY)xk=EHi zOk_vau((?ojXcX?whhvia+4ej>bNWrxE(|au-QW`ZffHB9ycyeeva<`@{yw>d>(ao zo%nZ0O5Jq6vbg!UGO#IRZfdTz(C)B}F$Q;$edz**i#@S0((M*X1|cKEb-3;Fr|*x} zIT9a_LsR4IPhwJWCf!(tr6hFfZ-V&q)8VV}o0ip3 z3`ygK5m}~vAI43bXtI~jU`c3R9`!IRkcKH{ zD|94o^pt;wnU-(c}M4#vXEZ3l72n?YVusu)& zu%>?t`uvSVO?hqRKz;}dP0*c!-OuRW9Ghbyh`7{<|6Fkk>EvWX$3FDxV2fQ#Az7dA zDXfY=`S(JCKA~-#33B1|?)~}wUHN?)f$)0w#{^eCf1m_(h}0~t#AB$s31J#a&hu1M>A15I z7@PuXQ`dso=J384Km- zOE+`7^cx?AT(O~A@vgK*ie+Q8B@&jJaj5X5MWiea?>dHrzbZ##PyUqP;YE+DY4Mbi z+w$0@uaM+z)kOsjYREYzO16CcJ0!_g#E^ir+h6D~oMEN_7M3%;SQd7oW>&0TK7y7k zFMyQX``QJy;RKHM`MC9gn{`+~5c6cOU}Y}ubom8A_GE&>Q(5|9kfw4iw6yTW@XX`g z%>AO>hk3zl16{j>mJE`(Oqurc;r8!;u**6Qov}#hfyHvl^Ww!89@~3awKght{ms-w z1qADNEfzrvVJRQ}2@2am!bOF-v~G)o@A-C{y!Q7YhF15Ezass4+314EWpJq1cwjz@ zmi8PXC)}`#-c6EJr>f!NX~!I(e8+RE&K0*_c~9vyu&}JP-7H8uWl~Qpo<2yj^r)LQ zTGkN_N&Dcl&QF+Dm|_D6bgC+X<-+`t6sY0$Bsd2!k{6rqMZ#BE*WS3^r9m&f{hV3T zs7apfb!XkxLlVaZFDix(rTHJ|`8^=68n2fN5A|xtnn)$`kwn8C5x7Z-7Kvf(5f3h+ z(jX)dCl~XyAI5E+UF&_*BWii8Q941PiK`O}O}*qV zri3e~zv1MxH90U8)>5-J|HCq5dY#U;low0Gilm@#S;?qsN7^L9do|}V7rnfM z{`7p|4QE)IzJwJQXMBGH4A~|JkNP-arslUljchzvT2_NxuZ6}WVzw2>jP%!zNV(2B z*^UCSn3~2Oai!GPapMK4SMSNz33853Z|Crm>Ka6n)vTKtEN96>F$pdtfBcQo~@H59F$ zJoH27P+~-1)Kh()6&nxF{voQ$?MMB5swW;?)Pj2BAnkb;WC2=EUw%W=nZcyl`Fq&M z`r8J!rK2a!h=1Pnl<}Dcce(RuQSZw=&shmuBN4JfJvlnW)-jsf0x8f0iBf5Ww|oKEv-V^b^P8jcSL*cpb$%?Q8eb3XIA3?M4rX zVVD!}%sjP6J83GO=iaIaI*$pRI3{qun|u1nFS3#5JE?N|f`Vdc91iy!MGm({Gh3%o znrp4`8A>_ed`mYS-40LEqgn>#ySdTh9kgy0N>TG^N-CBLi|Oa)<4zJMJi0fm^itau zG6b6CYjXs;O@wmw)A@h7YI^RtOvac&yX%rx_ieXZshtkSi;u{83I}7SoR^uQnw-xF zJvafASss%P|GvsG2xol*&~BGTbu%^WmlH@FkHC_^;y|wGZwvTE4jdA3kDC~56`=?tEveMjQLu+nCmuW zr$#Tj(}9DYMdkMpeHQQFEg=M8i1O{Anp^1WBdPDs6S$1pV8vQAC-Kp;T!M`H7d$vQ zt(JiT@Yi#V<2R>{ZhsT!-c@oj{shs_HxBriW!w#l2t>^b59*R`P z$z39WBX%}-zy%g%S&ktFKmD!$^ue@95i^-=6t^2vz7oc1gbW{U0L4?EfOf z|G!Cj?s3tfyP{o@ugz0UGi*s3&|RB@g{mkhVE`sA7B3}{5@RG$gvXva%pkqWLN*&m zP6TPveaH$(PQ7zHh9O4oOBJ}Jx=hdC);@~CZ}=kRrzy9o=(^}7%|NtmYEY9GekCG`g@GD$CJiwpg|03AR(0B1{SMC zQL)21>cMxmNjHcV1jGi!uqxSMIWxdlvW~|H5uj;}%fSxTX^20{=?xBfNRg*DQOOXf zB&$C*iB(-K@W(QYGjC+#c??@Sd?B=KHjJ*)iUzBgDY%gI$@pdU6+i`qs&0{Zs=;SM zXH8#{Gjy)IiGKZT+K5AbmDE97L_r(gEJV<~!VE%EL?VXor$n~>ZCpfmS{JI2QL|tx zjb-b`qV$VgF+%7;_7@R=gaY6P9| zM}6`rn@v14oH`&!S_%&T*NNNU--~Chd{3*YJ;{cE_+%tNAFN9sh*6C*FiUH3mzfj| zehl?X#xYhRG`kNTp=6e$qH&`UUYdC;$}74HDpw6HCKl$4Rl>oIQ-i-QS#rxveU68n zq4ksuNVVihrh$}Vz&Tr>h+Z#xDSC;b=`W}`aC2#kGtC&__lrYKcAfY~loWlzwkDbleetK%Ojji= zM?6lenwbD+zliS4k~9S;+U`rm55feB8?lVg(u4DVSYt`?BqUfPC ziW2Dtry0SGj><9X=8q7I%S)P{%2TCQ9=3L5tvCy( zbDw%5ZT94&`sXBwsR!R^sx)WA1kU6aDL@a*mjU>xADWM<_^#JKDF2IfSm*ulj|}!& zlY9pfT6u?F!yUO&gS5KJsv5tA#cK*39%ofl0bR-IeXBuXr6}>u@*yGtj;o77mCf@E zgldOEw)e9i3F}4w6r}vHwb>!>NtnI2PLH|-)yXY8jK*q(`T9R1g$$L6ASgE~9KE_} zePfzm`5jpVNtFHA7k%kY3|A(2l2pb?(`aA-ld>(S;VMdrm7dH|^ZYkn`jU&fi10U{ zd^X<&=z21ar8|gIVqwfplbtqMjVs=`RV!}EY_vjhTOvHx{FPY07pG))=%o15QN)Gk zQ?G|GhlUKj7X7YP#60mkXD8D3ZItlCCeA$Ga7~{Z9AF3h{px#H%`#(3Z;%{?#US92 zkYSA&IG)+M1E}Ild7dt=XpNL1)q*7%-U;ih5~i;3;uX1NT6ON##BjLT8r(KcvO+7o z1{X6seJ?VzUc+8wxm&1T^sc+JxidSJI;#B!aNf7o1bFP{7_t_mgo(-a(_PKypMYd) zD$D4<0Z7=~W|vLTX?tO}I|qbp`PUJhhV`qB2FE|4Kfr!?v zPKWHVj6KthZTX{=&z1If)oSw;zn^yorH~EW491U@M=rS$djla#+=UL^yFK2Su$%=&wx}o!r~L zw0vAb*|d6D8syYqk?5K0EO5Fr?|Ey%Eh~Lcu?jE|k_OO>k_Q@}pjO=-TM%UqxU$uj zDTXU3)9Xj@Um{*Md%Bx^&Zs(@Xu5E7)NS!TZAoHeUo>?%;>k8W?JoTWHMnU8Z`ar6 z-F1X`>~3Z>1{%)UkA8kG0E}GZL59taAT;vg&qTBa#@6wcB~Sk=HZ^nl-BFXxqJfZ} zh7?!iGY8-s`uF>K0GsV)kz~jN3?#)0myRC4lJiGaBgb9HMhBN0J$dGK|6%`Fyh7+S zr@TwoLL9)Bpi*63JX(Z7zo93zFtsf-9MzSHSqtW?VUlAj`=O6I z?!(XTB#E_qSM%-v#EDns5|UD`@1u;uL{j{F-5fqRi_dzvBRRNNuj}x}H-!K0q*$4+ zm_U}>rsrtK=nRnkMZX+_5D(C5yQ-(6I@%7(luFW2PxV!bRriXlPB zc0ERz$v+M)3}uV=trUg?{=~+^Agqgp?t)%iM@k+`e@Ifjwypgio2^4A!aWxXI@`#z zC3@XS_y0@mnMHN+r$vBGhIW2IqCkCPO@kPZBvnAogi&NdRQG6Z9{1>Zp;cjCo@MWi zFZu57#vZ%QLZl1% zX9N(%kcL7YUlZ4!@n9m+EeeBl3=;rom5)Lybfin&X1S%pxbZ* z|G0kAe(cOq&U+Pqv~)d)x2p7>oSWlHu1?mH`VE@OzIBDG_=iqUx{OQ)>Di+{M+B9O z1C#I9SJQ3}%ReMU0N~|J7lAA)iW-uU>4smCk7IiWl^3KbvOa#C&SqLa-j1ENI0^tI z(>oR){UpJz_VwF;qt{pB)yos|&^S{S4tEoiJj%OcU92{C8pB~=L&c9ZlB2B$#($fMOq7|YbUJ^ZE`5LKxeMzJ;N|e} z-1fx=AmlMV{UER|J3H>BFEV-yS$Z7Uo6zoX`l3Ex>*&{Aob-@zQoT#AQVN{)!~6KI zr}O9ZpziP<-0ikD!{oF@i+fc8WXC$0&Eu(q;rm918BDOu@&3#A@?f1c;?LIY9a{g< zZqVs_B^Zg4j63Vg1h4n{%A2kae)&?o`t5&cL&1yepz>yVk$BY38S*^S`CK#uGPnEF z+&D(`x#OgalB0D<+htd2%$gH0QgJ}u07B5Y@@vh>4gJahq9-#$UGajWZ-V|9(}g>! zO@QdO<=O6C@boQ}|Nf_!1q}5EM>*sdu*)xiYsnm6-H)H3mzOA4IwWa>=uqWP`-~1@ zrHW{LyVLiFNsn(}0Ft8LW|?n}R|Q@Z^3`^ip(!~tgh-QsYke@$fzJZFcZglI2RNk$<{x!&9}`-h>lY|Sb*4$3^W(!@PYVB@J3y3S+(>(goOV8g@kT)xXSJy z&l%@gSy_?~W~7p}p5jSgVDzj!zgFk8VO4i4{dK0YNaiWdw{H=sg~>^xS=X+WsU?lk zSV-5-KnNdgYFl?DLc9N-?i$$+Eh)ejSMicjc5rxpbadoR?;}PsepoFUXT7|vJFlAO z{kFdN_V#>o;dOCB0d5FSf41C;V5s%A)3D1ZO{R{~`_?u=yPoft?tt_1^JH#8rGEe!~u66tZT^P{SIff6;Y?ma>~EUM?VLlJ(aMh zu!ZQSb2sD~B*3!Jb~XA3Q+R!8-Ro6DE1;pK-0(AIxq4|Om<|c54L};R@V-rTp`N_( zsHb4keXAcIpb62J%2jXEAPBFLA=aq3xoxKn9(Tf=({;@M{aFLnuaE<>U|pSKs=VV2 zr%tTCbHQp%3l>ly#V(T!wsGWd0C2#}MgkciHe!fWZ{%M=8g!P}vS+ST$Xfhi2c~~S zx<9HYRxU%FGdHxOpjaKZrk^4CItKx8N(H8_Q21=$yd5Ne{kq=^GtRKMK07SN6%8fZ zGBaY_EkwxGdo4kz-{7a2H20+RvPT1RBrVKZB=3a||Q8WaiJz(GUmvV`E30`Zf} zogOlPa=}vWK8&;}*|O=;nJn9~($1!Ws|x>+A!Z-k_cW#!2DqOb#NU1ljh@i{QTzKv z-o@h5N86ml-b*(dTMre}+=OXJVs^Zu*tc*aptOnB2#4`0ckOG z!V+#>xuQlvePoU}f=(CZ)`9nSxs_L;Z`>yqwG2BZ(EUKLrlD_|7(fEkJMBht>E;%r z@LrwcKDjhNXuP^A%tk*71+&S4$v~it<3HcRhB1nkACr4Lc$+K$y1;$;vC@@b{N10je$ z`vF2QKF3@58rc2AGZx_kQO<=OrQ6*fdo7$p@^A^OlTf7Hgq7FdSQqX#gcBmc9^%fx z8p`DnvY$nHVL>bXJ!Be8>ty-#?Faif+0e$(+S-psS6hCVU?Sqt5I~x2NRIjO@w&wq z`pd0oAGs*GwEShwXgdH%Z=WNK17maeD2d%@lFL;Dn1tg4`LA_+s4E`e+%Gd``FRor z-D~8u_d&E4QAMJ#dOfRNlbx;eH`fa;rg#Qs&(v8q!F1QxPm3ckU*pmKX>5@Weq$c0 zIkykkatoA8tC!0FCH$0JHbe3(t6w^Rf}_Ggz*1ltI^))E;-(<8y0pphM&$-#PX4oa z$Ra+Gv8SE`*Y@w#oueBN@2=R}qB&uTF^(xLi1d&seJNgSeXZfUz0M&Hltux%wzRP+ z5y@g{9zfGiZNJ-?D{C*{i8DB3}oHvi!DV9#)tuB66abt$s1Kf6$k*>y^1 zaFrj1>(wk$Mvx?1sS6XJNHzTyhcQ@2pC1FiL;3bINX~?I=fr08$6}uuL&0^ng zmb7-(otdqJu+j+alh3DH4iEl95U*XIHwybAh;rc0ZPPjZkH4$w3p zW()W-I}qa?F;d5ZG)995eWFi0Zp!?I&l=*M^DFXynOC**?k^ZBl|RwiI&MZdw79x4 z6IoL_uE^Qj>*>(U!hP?=()*XFTHgKAS1^5U=D-NZ0bSjv;@LmN>fd@Ik<(^MT>B?{ zl|Dk?8J_hLq7d^F{*4zT=qGJy6^Flxu4k%NwCA|yO<|jl;Bo?gw`4b#`!2k0U3G}a2WLTYT+#C9?0D1Kc^s<0 zs3Cy9G{lww%x{=#%R0ChP|)>Zeo?w^aMX*V>#Vwn3|SJrJ|B|pUKj7uLEZr&E*J|x zBG%%+0%B#zORC?W0j%Z$(Ui23g zJ{KBTZ=t6+g=nWrRG9fOsR%R^aZ4#{Ts#c`@3#-}W%)qNU!+Z~(6muMkg(+IayR0A z0+=2l$8f3Z24cub4U8u}2 zi6?uh0|tSk115#k>s&5afb<+xV}ERmL(E#FV+jjk%L zqkO)8!GqVQks0C3FD62(xIOKcJ3x2k?_IWcg=0&3mbeHG_j62(PIw!*j zUp-+o z!5Ufk7$$^)PdhbV>d~`cge8GD=Mmq}7@ZkUTA+Z#uugDrBpf=5j~0vM+M7vi1z%o- zaq-@*PoQMg)V@iib}>Zn!H9tIs}?qlu*iPDvX@=YqFb;DksTY#LpkR@ntMreYp^n& z>3phscKahcnLz^mz1hp9@9CHob^vQm3^SR>xz^Nm!G}by{bqx+T^Yy7|8MNCR}OYe zM2CM3f|YKE9@-PR?q7%?kLqQDCtv4Uk=AWY|4q}_77cn6w4odc$!_q;bpNwd6IM{kti{EZMEWR~dCpxS{vS6O_JQ+ENo&CV#is)YYV;OM;_tI(aw!-NlGXnkD zb~sq}bw5NYvdp>qf*?Ru`||kURoU=>UUMuXT&!Le<39@yh&gAVkkEEIQuN?);CP+x zHU3bQ*n`%orPQh2Pd}H>g3Y^p@5H_SymNE;W09h0)~pRdKcR;NXZT0{6#W;x>E$(c zw|8*#1URqSn844tfdnmt*mmoCFfi6+$|HRI%?vV#sPm)-;}>{ z*@c5qgSzxbt}m9oJMk9YIAYjU{gv(l&b_GD0yu7UIX|u##T`hy4s`~Pm>9DFR}A3Z z=)0LSe(R^64bh3<7S=hv-WOQH2LdSa-iO;&blJDTo%Rc%B4klB z+vZqxB}^~j()0Mq)ce-%!8^CE)di>L@d9(jSM!SmCob%9@*iH@b#|s=&=C;hd(=B3 z_4CFD!mHJjsjnvXe~9`#|BL#}KP6QYe?6R4J=lQe7G~o3dcq@UMwx;cg7ujPqC!Ja zPph$`f20y)k0|y_q32M?=iv5875WoVk!OkfMnJgw>Qz%jFy2ey((5xqYcj2LC27)@ z6sSnb9`D_Gyd`-Y{F$?F#ApTSE%&NAN!*>=pSN!CT>Nv8)g=;u=DwSptRx9U>sEz_ z#IElqcm1RRi`29aUHZ8xxEw!O@HE!W>R(gb={vcZoy&Jv1N(0d(p`K0A$vj&MQ_@0 z02X%tMMw3oYRiTJJsOOw^X9C>JxH8?D(L>b2U2p#Kuqj5>&BWYzMH<6-iK90_a&}r z_DQIYO)<+Nb%j|p6o7)=pWfJ^?7UrofcM5cKY7W`MS)r;B)d$vAOcdMOsPpveqO8e z<0{oa`9NnN><9B@)8HRM1oU2{?wDyty-HB^;)tRMep(xDo6?9y*HWUR*q^2Ap!@%X z&rl^PR~$5mEnOW^X6`wN`rzJ1>_*kc>?_aZN4W?@TO@?)*fPoEa|6 z%7~}bbRKtG=TX?ddur=!kEDFchQ36%l$fO_9>S2P(nK<3rdoX}As#(1DWAx3FJUyaS;48-%Dy>J(pnol9 znVg-qR_E~mmUEKSGnwc2Rj(GJcN0dSkB}rShqCsrF}+7Th1|sINGx>vc6h^MJPJ{( zZ>so6{rc4RHWRcz^G$LMTx<<|?OEqnvMQC}yu~n0r}sZhxs!Ki;qY1)=l~*czE@8K zCpbWgD^6c6Db+@okK(~CqR{0=ign`iubEF5BfAc^aPvEMbC{w~l3HEWxJ1S3VeckFvR;G23A5n5bh}Hkq~dz%-$o0PUNI?uiWc{!y@y zEk>VNk~PwWQGVhca8t${qY^9Jmj_~IzipT|rBk$I-5oJtqFwr~8D zPM0P2c{|0TbLSTnxehE+PSZEn611Z@0v7QE!4I~^O8q$Df&yqP_ivrR=~$EN8KOxn0RzsZVXr_S`2^KKfr zca83VrAF)a3D1_Tp#Os`&Z^ITj=q$4;5LV$n@*&gKsnB4&|H~$5_DzfeW6) z>~tLlpLj5MWa0Iw!p~_N*47OdICiUH;x*F}%InJl+TT3e0XGXe?|pu)Z6A({epIbk z!ObBB@g3yy95s0fxr|>pXr}gHbMIjz7ONOB;K+kT{C*9k&Jc<^z}po_8iTHQy;ueT z-<~Om3gT;86{eTP-3oF7k$b>M^QB%3UQm86Kk?@bX`#$1rEmg2QC6D&N;8!8@M1`8 zZZ!rD2yLce(z3*>Sk!t&TWk#N3($pL8A8k~?nm@{ZU|TW5CTeDEmQc};GsgfC|?i^ zuL##}eWhs6fSf`*a^`rDO9GjRvQNkD7p|Ru&HB?n?7Sz0Dh&4xVX4R&lI))xV)6~F zo!o-YnnJtU`B=KI-4&pA6*1$n*uGU-)GG!3s&EiIp6us+@;&jkJ6fpp{e1*h6q9XgdZUqB(s3JH z3eJZv73+w03}cEHb*Tr1$@AE!+}CDb;mO3X050A1e9vFF+XA^aL9T4ewK>%)^K;HF zV6A2I99p;0SRna#>4i_)2b!nx`cNKxZ_!)@uVy3fffjs#-2Jq6$%i)B!aNL2ky~r` zOQ~_?=>XQ5W;x-saT-r#E;d4q$YZ_qS!LtSsB6uwYsxH+^UGiT#_0XCue41F06?4e zrtE;;*gtR`CsJXnJfH1f70kc}2_T7zb$$b)q{1C@;ioMQXJLHAYn3bG`Vymq2dOAa zcmH!6c2MLMS{f`NwVxDs!^>Pqgq_Y<#>PUSkQG9 z#b?EOSGPT>!})fwYfww0ePRs)@IM5$Yd`xFz6u{L6bqLxujR!eu&@#b;vVI{Z9BZW zsfXQ18yMnME8P}8xEU_*HxhBKIwS?!3XIe<8umWB>Lv!htb5)V03oaEy3>?@ubxn{ zpIZ2^`zuvtzKY2Yn~~5%DH^&a^Y@Mh-NH|qQ~+A+d|`r5gp-YRiVh_s6WL|`F3$#l zJU0C}!{WTEy$~2NpoQX*Pz@Nb(--xvZ5nU0J%@%rbt`{0z>tOeCP-77)Et)v+0Vrw z(@%N#@6R_-<8YV*v}Svm_tLP6OlDhZm#r=q2m8_{f!nTMSDKc1Q8062uYm=%FtlGi z^cOfIQch_Aye3-b-gtp1t+#Eo$|MO{fG_;ce)?@S91NOp+T5Lu82IU#qeG$HzP7{a*bqY7R$Zv-zhg04LM?`{Dl6hot-g3k)T*@NU6c zzad{G>HX1)F_o}1*DaY*euEw-F{=e7LYnpMJHUrb*a&o`W1dLr3+O3bVqsIU$H{Zn zX{j|?P}XJdHCXF=j0cOEr1uTCXF2k8dd3U=b~Y^c?x@z7`0j(EPpwMMZRw2*8P;p) zbQlUoj)_-Ci)&re^ytqpKpp`dUh|aPOrw^>1(reUo*zIF2ta>huG4zIwU#YCE2j0n zO8EA*RY6%D(rS4;#u z?*livENeNTj0faSegwJmdeNa(=P8lzP^plp2W~DdydtcaWLB83k;CyPmQMBasvusn z?+4Srx#ssyVp`%A2?;>kt;3S7eYKQGvMF?I&L~OO0}rX&DUpinw8`6Tde6C3;~Amw zSaaZi?s=_R9#rGhv#MAM&*2rpCtk)4j^>betfOvio@& z&0noa%=up<$xn5UINbc-Od_-EwdUF$lD(%Fn{$XdcjS{^RxTSiMlnh!|{hN+#X(QQ5E~07!@$YUq8OPLwLnhPj=kjg^X~t`)M2s9_J4Oi| zb6bW%MKTw%5skFqyE-Ym&v+Jyj!~@-29P-YIgQ?0VUb71@K0^_^G~W=liD-OlgRh1 zZ*5n3o0?=K(RoSV06t&t_Z5=N9lCU;l+E8^lpmn5n=FceE5S*0?3#6|JsGS)?(v4J zqVCY!cb`CUh7{M@)qlJCDVr9otjiNpPi5!_3z(BD7Uywhe|%%VboUEUmc`+1xRC?? zvu$&+egB3J$zie5Ax(L9N^N;Ba9s3SHmyHF(bat?0^CUN+W_VT2WV=LP4L@QAwHfo z3_}lhq=PE;>1!3@Opqkq5xFx5AP+s|1{(>ZVYU8fdy}^An(yMMIDQ6Bb;8=*0!1ph z1~Jo*;{!qp$=1X61IPN7KtYIC4!wB+w)y7>JUG3(Vy%jk6l6;HmFL%l0EE%UPM2DO zty|vh%g3k11S$Xox%amEb-gxcbtQ)Q@U%ZUM*q`*>|0iSsO$qmjcOv;u4m$FJ#QzZQ(ft?ic{wT$CU)NJ z^Bf)ZCXtc82Gv-B(UIz*iakk*xx|BW{H!L;5AvK&$bT|xZG~A?Y&bJPOONya+5tp5r9XlncH69)ss3q!I-Qak5 z9BWJEa`0)nm>Bai?86FB)p^@P8Bl|Hjr1exk;g>_0BHI$gS2}_ZNp&!rMUh~%&BjmjaY@!Y@h3ZYe8Gu z&Qt6XZ=DJZdjmD^zdAncY+G$G_8F5Ty7&LcyL(E>k9wni3ONB&?7s|oA$@)DKZB8! z*yn-7xxyK@$wSsmX-=Z=NJ{$2Y?^pzW#U>qY^BAK5>l!dq5G;sYr@R=Qr6?TZKmB< z3{ku4#?4*igSQ1WR)Y{glXN?a+(!GSUJ2TBkJKGWx}>#cwpl9+ii|6N(^XdGk=e{4 zUN)Antsy=pTp>bNa1J6r8r?SyE#;5fP8aObGA(A^VVmD{6DfXRRF!z`{*Cl&= zCmmb6woZ#4mI#_Bna<;S@0A^uba{3QSdq#BTo05|Uc5%g-;=%yLzgV~oPNl>UkB zSyaQnViO=_CHo9FShLdDxdqeB;P(xW;}`La{LhfP?DzHDj!R4uN_o#4%65(rOxYx2 zR!PrTn7S%A2m=;0@5Q$e0l#7*YkA=+TaDkwOI+AGNRRrmYTWOB;$GL$7-;8X;}+af zeUe3Db6WWPYdU@|nezu;P2JQg6Y#-FAAN}{GC;unkKq1D=Oa`cKu?UW)C?JbPj|dk ze@iXr$#h{g>+|ZQ710r02sfbXc8wMK++^HtSy9%S$^!>QQ7#XrSY)5O{^_i|^JvL3U>YL~RPAV}*H>E=%^F-5hP>-o48Kf`9uMs~3?nys&%?j# z_Y#@--7}?0!9u_$l-xE|=zZ~rEyp~Xqhs?o{VZ18st^esTcSnTqzCc6gqW)b_Z8&$ zAANVO=%*ayKf8n&F0W0vPq-R#4$b@YmJB+iRx?)OKWmyH=Wr<)RGUMD|T3{1f zbr2UyP&({o=-EzL`u8jE3-2?z?nC{xt*e0BAS|{xq~4pyXGXl%Nwddjomv6zFn(O^ z^))!7@1H8smBn(CugaJYDNFMrcm`|W)zHD)BhaucM?sP=Qn!_?=J}YG6d0Jrqqs!< zs<2+`Hkyqd@>`P_XA_)#m#ZIyeb} zT*ycY-+M5MYrq= zlRMYi_-9+WE6GC4j2&BCDDw~_VSWn0Osno|56imBz3aee&zSKSZ2`MaFd#_615_B7 zSV|bCP%F)*2PPdR2J^3d`#XjMn;6D8hKz~6f7L`T&x;kB!Dqu$jKIt}O!p>C!3|ax zaodcV(bk{Q)hFw{+bmb7fBPXfCZ4}ME*i80| zYMyhKrQPYy-a9?-1N|#DD>;VPh(oJis|m58S&{n)lCi4~V}r8qCf!ro`=?qN=qJS(j@MnYh~uF#t|V2z1-L8A>xX;eX|WdF9QnD_cdR~k3oYctPb(FC1M zCv?*)C~ND?K#Wae?3L$F$c1VsGJF{_{DO78G{oVr-QK_vW$26`x&@UEt^|o%#V;3+j^{@z!Hi=KDD%AOAs^ff*m8{0$nRwo8PxodUa)NpR+? z4BrAOam=h*rpiQ&%b|stmaJmedLUJG$_oaSxA{RJ!STkdC#?3CiC=Y9=@@=|V)^Is z{io1x28$fo&6#umr_8xFOUPNzp`Q&kUZ2$>Y>!I%=|~>509{T?f4NrFdzMM>kJZ(d zpms*ms*Sa(k83UzJ=D83R<`i_QAz-ys#3_G*6zSE<<#T_^#2j#Obo1M>U4O;A(%TtitD7W$D1Of zdGth0ti>u>n*3fISJ_UC@5)SeqMes>ZUvov3`K{A#>J#O(>}NohlreDFNSc}Zcc*0 zfHr(*LoMb7(IsAEWXMt-1(fy5DDS+lG6KNFwfL1|?X1Cci9a_S6 zGAnCf91~H+wOwrF)$997I@z~$A;~H`a`*9kT@+tou zJ67^sXw#cT2bn{LH`rwZ<8GGc-{0o%MyI!aHvQ)OuGC=~I zEF($U$F4=1ah5VaiC>UhUJ}8;cs@?NG)h3xyo3JtpEr3j+3HcH8@QsUL)2=UQ77ET zq85di&z-Gz>}lO&Gt9yTJw8mnLN1#RAkZGiR^qC)J!5s5w~KH$J(SRbL($0JR?T3^ z?bWgF0}7I;u!D;PGUA;=#!*1wuf)x|9GYe&r%xA$ovg_5v#yxvMtwe4T)M=V$;Num zzho+GxgUM~{65P*V&>#|peC%0eZLZOzBtuW_4070E41Cc+8s2BL!76`X*B=IK6X`! zpHjEjTc)?LSfm||R&|MrW&|EtNz>*oh+X&^tH|22jk@+|j$r3np&R)}RKC55nPio0 zKB=5x5-xiXV-d}7CHsEe`}Cx^gwTevVl#jqPN{VFIcM${<>)*2~jwKb8Z#*n@j1M_&HDCXVxnsj5o^Ma9O> zF&S)s&rwRcmE`qleZP%p1Z{4WNE-d%o6}F@QLpnd%E{GZln%$vl3yL2vG0yPrFDFo zyJ>hzIwdj1MrhRFmPmrOQ?vh%lhOShDJVXMwsT_vQMc*(7;)p)Oi7bUKT&jYR7!L&F}=fghbza!*OKSS>5q+$Xb}A`8LF{9f_mLKo{Nh zn!GF4%q8-kyKNjvtnRet87?XX0nqEq*?5h(Oq(d>FnU&(L|8C#x9t3J?b4h8zOHg4 z8&gL#*nN97Ns+ku7J?V`p9`>cYP=B)tz6#nuWWS5nEE=~j@;S;_b%fn7KgLfNoQ6u zl>6>Tw}3vH+>tnvB`=~b`V3a(d(hlx0WT5wF$M+n9W-D4&0*1X{xSk-!X6LHlbS9F zFvf*ElO&{fl|(OD0#mn$=KjrR5DO75T@2W!u(g`dFmI3RCrgC+O{O0Qa(1f2F9s)w3Or+A(YIkcXs-CUjhbzp1~C z%k~T2$IT$k1rWe}A`J&!dZHPq5J8Ks>hYd%MPL{Fyua@6CRbIRMYLrN5uuAE(TtJwHSnVv9R3fVA}&Vy%{^lq4nuUq)!rbT`#FrOb_` zXQI5Lmbs9ik~js+kOl6siR4BcD7I0MQJqPUUW18L12ZO%=}FdCA0p8@*d!}@saJJ( z*U8lSd)BALjonNZhiEXoyYHsdGooO_o}MfOVUjQiK%BZ3)}i{WXNd_Xp+ZgB`OWw= z;$&9TNxA8et+Q31+Rv+Muveu>$?e`S^K0xO_=L@^#@yGTX+)o0)@xTC+>`1LZpty& zey|wWe3iLw>zBUm`wYsom#7hR({t=`p(dvum_-3^mpg*W)Q<_BH5q8i*HeHL4$Bmi;Ssf#}5He)PK4BC>u(1@TKLYb`(w$3G8Gu`2JN@9rq5(7SH-~#u zw1WzPUfXdiU(_-o6Lpf;dHlDt|EbsLzs`e#Lqo?M2ORsTTacH6AWxn95h^oj7 z`_mhlkV_0oF>%B(Rly+MiImCZ6^S7y6vulxpbza8D#qpa2ulolzpCS<9npO#-vEjbvV%DR-i zVb2ZDa$&iVc(Hwwa8^vAoKY^SMM+0VR~2-wAFugif@n)0dhly~o-bmm44$lwyL zXSqjKi2np1*8QPTXZI5QNix(x!?dkM|INI2_@UJYqp7!aR^on%0TrD;e??YC!!e&gUSkPO8*e%f1wd~-H-A>qC}Gxd;s|5r zdwNW*;^ngdH(ZZgbzuJ-G|SwW!I)~3bb?T?B%g#f? z2q`3wEn?Kx<`&exDPfw!N3jiy;=~}QfHBLE$>a&_b^1A3t)_H8GJ{&!Oj_s^>9^o9 zLqMy5|2XWWmL#8{YeGR+i}?5jPu{X+pj^a(T$pNk+z}9zcA(6Xu`8{v7ApSt^0?uv?VvGbp%2clGlmFb-A>s zp8`vF^ip0IO>W3!7@!BWGj-RVENP2B{&0f|{9ZbYluatQUvdg??t}ot5k&|v{G_p5 z*)0HL0jBVyt*!Ltz$!nH`*Ma=$ zavbp03$Qk;478ANo<%;F>O=>Voq9;8-`ar67k3z9Ew$a?ujdDPL^&qGNKVYcl70IK z?)@0{xk#nd<5ka?XK`Wakjg$dD-JGJ~DvNkfZYk2g#6ILn$GjpSuDJ68L=Z@bt<;qg~B^KikF#Dh^Qp*yol zd1xOmK`QC9+n=RH;=2?y-%mQuiAt?d&=_c=PR^6w{)(}6bWqgYW40z}1cpD67 zvF$d^AUO1`;X~5FpWEYpwL8Do?i%^j&VK%sGRpeUf#{gKR;kBm0!iv4zP8H6;2&|k z16!lw`H}A?h^b6RNTE`O`+T8yUJ8FgUg1;BOj|0^(st3x^;fKS(O4i|q<$n1_4Zw! z)Wo}`zxtI!9;4Z0wDqR~(X#y#aSHDSf=1gsOI)lR7bUIlQj4GIF3H*AFTT!aI-#9N z1NaC({)*V1q?kZpmN_aOhBadA++8XZ+A|WQh#+;Vl(Q8|*mk;l=S4El^Tb}W0tA{Gr;+sr$<=3fbqv{{3EgY{gnwVA%LBk zdXA!BNXcbX8sv^Wr?rHt!P{z!iV+x)#U$mQrGby;Q<#eA``4J#ZLO@rkt*2kPV%k; z{k8)gFx+FKnvYMry8+wMb=$mRX|W|N7S>DfhJymsrxOSIx2$mzr4a4pmWs8*QOwkk z%;Pmyv-lTWClDZRFaK4 zKyo=3Xsf+DpaA~dEt`DU-UJbtgkkru4cfb}(rkd5FShAu28HtTG!QNt?NcBJ)z4i0 z`82t6sQ>`V@9pR=`a&6?U|OI}o*0bxL5@4#xM;92e}z5vhb$MweijPo5Zs@Bm2%J& zo9XP=J<63q!E^}=t$rRD03e^YS<#cZ7RXgc(d@0nk(FHL2(0fpe)dReo;OvH#!oFJ zaw8c7a22>&l z>R>~~8J3pM@%evw{H6~moA7Y@2xb}wfR;t*8B^UDqztlG%FDL^k@CB+mZ!6^%7EhY zvnNv9dRGE9V#{8l{bRDJSKD%|)AjsYYIA>?+}&o6KsT$qPQ}~s+3>QYcX9N~suQg7 zM_PJegGj4AM0MDP!Aa#rF@QdZl8iv=*gw+!k1-6ClK`|J!X#WNAq9^(7{D&;w0C7_ z_*_EsB%1SG#oA!uC-nE!#jN z6wctzH@@qQ^Nw?GmjZAgMl>#VsWr2(yNySkm@RAOfo}3m-f@8EHx2z2AMvo1dw0^r z@cfvfw;Np$Ex88~1Ocbgr$#OP{8@GumKJ$Xh_F4a7h84?C7Mhuji@MWX< zw~EiGN znU0|On55+X6vXMUpFulyPQUFf%asL<)Y~#c!|hY1o6)l-Wek7-C4bu1TOT3ZTyAQy z@weV}rTz4b%MWr=?QyS;Kz&vV!$+{1^?xqM^gpI=+PIy!1(( zXCZ+XOu2rwJ@kLSd^jO%`ABUL$Y7MxuZY2*=uQ38!Cw%*Cb!G9NaJD|E)39GAQ`ZP z005MHq-#Ac+O12TiWJ(DqNz0x6Rwzof@D(aFa@1Y5b$70I2)-^X`UjBE4}>Tg&#^X zJ@=Zn)pmah!KcmF{sxp*Wpuy(0>K|iLwMOb^Ts0=2|;|UEpy=5d$Iq@KX## zPU_~ldtJrr`hW%if*{)gpte{SfRVzkyI~~m!aqR1-HVh|JxV!HovN&SLD+AEq{LR ztah=r%1DSy*7F%ZayrgX_6-oE(sazD_3Js!y5Tu> z>OJ+E;o<0q7raeu-ARvxpYZ=S45rAXEVG)pEnaR4vA|0}@p=8?-dqiOrM~QjIi@0! z@OCZ(_i5}a{}fA9a=NWS|A)7eYxcgc&jUjB?LG+vn1gxLwh!seigMwDlHg6*^)SnS7YPc$jQ2@j7p`XM>@0EX<~bik zUvCnprTWbDu966_b8aBK!2qW4%KQ=v{$6kbWJXCmgAdbY_BhLx?yH#>eM@%Bsin(B zdTp6^%W(-_((1j%JZy}# z;ZfnSoE?&B%EAaEB@}@1Ej-37Qc7*dJUpBbMG?-i?zKt+%h6YxIRnh}i9Z(M>%kSp z{#CX2?b=ctY&hB;b|g_0oNXb9`nBi34~JSbqw9~{UX}^YFm zlzC6bvnN_mQ|f<|>$Ob#RIZ;PjRD<@hF9z{j(plj_avKgm$zzSV8oo`@8MJ3c<3@J zn=-=TWHn)Ft4NnG6jg^gVp}^OTwYWDvg2r5m+ID{{!Q~FYgSljoQXT*amXU1+XjmP zzk@tx#W0vJdU~J+@dw%NSW{Y~PS03N1$X6#kJ!3~n$V^nviwd?Sv`1uFDP?#oHet4 zT`ZWxtq}smHJvZrr^kK8I!expChkvL8ChE144QklBlDmAyH8H+#-PNrj49oJz?-e z?s*$WUu|J^Uf2&MQpl)u@b4?5CIN^lD52|(=vZh7#07sbXeV7xl1UtFaOk+ytD3Ce z4uNtbwq!@8Z+RrEbB@4df);0Q$kf{7>9OpU}Y3-^O zNwix~90`pZlSxX4QoZR}y+7&he9tx%Pq^gjCck2#{8vIoy0>UoX zdu?dg+{WT#%NkOt16tx=S(GTr&>bNg>OC|z9Z9xF&JXxSza{S6fgFSOl>L-U>mrU> z4T0=m;~S}9v_08WRaN1{r@LdWr=mtx5(|s*R5hN^s=^s_eqfPMKF${w2g-=r)nXP< z+*^>y9y3Zq@AKgfi~9iA8Ji7po0luTTThutqjkJjQf*&~t!cJ|`#n^|t70LaTfvMU zqBU0J7wSp>_YKM7xUX8OnFADl8%itigs@7v$a@9O&35N?;@sWipS-j}S#KotJ4DA*iedzHOL$;m2b*AkuESpqTao$Tm z@um~?Q~E0)^Rr=FuVn#ec660yGvn*{!|GDQ>w5M?mDC$m$PgXXWKQLa8RYN~iuQau zFpe$z>SIK^#cHa$t4J25{N#|bp;|&9m#<1`7sPQsh>y=m#oCX56QHxGxGzWQ<^d z{i+0g_DbS4jfS|#-R};e#%G+u^GUvRThqHAae^zEOcPh(OjJih1c&XHW~VtenZ8ex zjFM@V?J?a%uly-LpYW*m^~&`_LHS3qLgT;csWr*10XCa{ubd|UU=N5cpS>eFpLV$% zaug#uDT5$b4CS`JGxuIRLm2_!Z?9!x`gzC^e6baWefNYWxU)^occ{@yUrwFh_;eVv zJ?&MEbrZ#K+QanCFGvCA)Y{?_S@ZoEh~2S|kF=cs$E^{Esznp%2d>!AnR9EWhd2+# z9O^DT!uE7&XCaVqVEe0KYI`hnf@%kp4U)>y`W$K{2MhTuzwh!5RG6Z zD6`2mN$&)An0w{p(&p;bz3h3Bk`3Xz+OG43AVs)5NF=m@T)x_14SnMuXtvEk`uH`P zL*{)OI4kMQDtLNl^W-j#x+>jR2xT(m)t5=Xu$tAti$F%JWtj>+G!Md0=CX+rtVfF+jo&HF$tgmNy2gpYUH5twh4! zZ;`k-^;^N=D@Y&meF%bZc;RqEcV*sKc;NdF1(a)t1q81$^P1Af?UIcr7B9?gEUnmo zjX6$ZoAZz07!|I$umqq983>f$)q{JP;@WSX^_{0M!T&E<*u00P5v0d4A z^H{RqAar7ol{=3)42rmE?L$N*X(8(0`5$LhTXJ@n)LG-r-+POVcfq5ZSrNkC(5%Tt zvc-hWFlr%a0trLGGlp+b&JC|gm_MYM0VxKl*}fybdbxPU`Z-s{{Xr1~4}ATVY};O^ zY}71!o%yryslhEc-5w{w)EHPhz;D%eiNz3V;;9}^KvNP2z9wQX#OWtToY zQ3!GP8SJ{rB}zwyu{vb!|O z1O;qM&4#z?80|raNMW;~{)XFjzm};z1VJ8FrHewrs4&?3+BZk`Ma+_eQ;q|^CY9m} zQiU;LROV;W={+6n>aU+oQXm8X)C=TQiyl@O;Rc2FJTlqFcPZU`y{VVfQh?gV+l`qe zoHF_`t3){p57ka1SfuGg2b%2naRc>ZeD<~&M0SW<{WS~9itcVpE{T0jHbmwqsmm1& z@PbEx?CXb4Vuxi|{^Rnrnb2Wm5XjxC-iPz|r#8Pn*~kWhAW3G5=HXY8Se3hli8Ac( zV;RV@*h?7#&W-Z17UT;=`)NKtCi!VIWccgAy8f*Ya}r>HyvogLv5h;4lFLUV^Q-F0 z^Hp)G&>`tFC7>W#e4zyP#j&hN1|^83(dVmSxY{2+JZP_xudcB)i?+tXIaULZ;M-qK zQx{uPdZ$(HWa_1HRs#qp9qK*mzLc24(Q$gGP2%-ndO z;T1bsQWRW-n;djV2A*xjn$1%4?Tfl>c(uANv08?`b7$H9qp$lG4o?6rSlT=iIVU4itb}w{86dGoaEQ?xL3iCDd zWLI^b+Hk(hSUtDTA#q;LWjJSgN3VG7%6QPb<_)`+oZROUOMUTC!nJ^kZm*AAoA#Gk zrrSLKaoM>llmrhQoT8c4z1CGm>g2DGLwh}w;`w+e&grRunfBuhMMZl>0#p3+tRfN% z(3}+grKzIR9vw*58m0R@KZuqJ0*FHtjP}QOD+8y;R@k{U zOj9Q9>ep&A_CmGx?rXvB)bC_h6a4#_kK1P-AZPE9pj$jt-d5k&;spZ=$@B8?Wa(H0XslFfMbgk>Di0Pze!wKk-*QwsVZ79kv|~ zSU^WU?2~JQQbw)=y1cI99aCotmtHr*%hDI0st?1Gz>e*T`1iU&DJ+>87d&v|m)-F( zRnV|mvZSpzFq2EpvTm)6JRbp}1E+K4C@V&aR_|||vp#$>Q!WBJK;K0FK_hl)TRBi! zV6|jWrD)+z#VRGC&TpB#KeNa}rXJXbVxl6C5DIhAe}bb(5cK*lbv(g3XoH&?il|65ERPbfJ55sTq!~o=|0d593$f`t1iCx1wjAt0zaaz8r?iY-;DJ z2zgoSQzpFQ;PW+VvseAI*#>rDc#h+<2GobR8IaLASdh_qsEErQS`cy>LmVv{*g zP+dai?QJc{<=JVD@Oa3QW|(f6Yg$&I!Z_*U6aV-@16W}~@y^{J$=IY9r}dQ6rCsne zk$Y5+qX(vj21MLIlfS4eS&vM>EzzcN$Uv(bJ3osfffz9*kTZ{tJ8kC|mi zKMY*N@HZV4OL;%N<%=2x{V^QwU8H2M5SWO5KTPBQGtHp)^qk{zD<)%h(9Okw9}XDP zvau3F#Fw~$dz`wqYc=amG~kGtZlOcx^;k82+1z}UuDh^`n<_RUr#8&SO}ei}SUFWC zzdCh{xSTx?mO%M>3Tt5*Z9VKO!mZSD-l53K$qnbP2J^3yD4ki+9%ElrnRe;A3qt#i zYy*Qq7a?S!-hVg)?st9UZS#BOR1rQI4AST{cpAI~tx<<&Vy{cZ_NGCm=7ra{eEC`T zBBk6Xyf96Jj;40f@_noUI#B`qP|r8Bmgm!!762d*6*GNS*FWoE71}mPoOogrl1ney z7oKYMw3526!HZeU<%z@Uc~*cscT#dp>E7JRR>#e2thWy zf3CtRt@fZpQ$~hn8})=iounL~9Hjs$BZt`PeU0#%`~m|bjOsY963t>}f}s5E%mOYw zUOzkQm1KayLi~De{q3D%Qd4^tu>#fgbu~@mES)F7a{0fK{;Vcgab|jX(Vaa3P4GI-M_RG$UH)Y zBqac=o*OP-M|LUG%Y@(XEj!Dddfe<2FI^Ic7N31FrCQYC*xV~7s( zBcqG{z097-aH>rYdH3BIQ2XvkOR2H7GfM1PAD$oxMbS*+VW_~2J;^J;fGnD2rfXbz zmvhxmu!m1srY+H1I4jGY$JtO84B{W0!=8nnFt3k<;&z-X`T9_SGJEfnmNL*>e%Xi6 zgk|V=!COmv(qzHpped-@wmaZ^%{8Q?6|HHM&5TGjlv!7s(YN6~Q_6WYSD9~l-k+r)D=x>1Grv?PmD#_xS)|Yud|1PZXN_Icuv=CZm!W0$&R;@DUwNU6 zhLDW9>^P3#Oj*lIY+w>$`Aojz+1HF4CH2=gb*Rir8uy_h`8gM2T44MtB+(3-@vN6hq8x^8;TDX8GpJN5~Ust&$>+%#SH<@iO0b&o zZ-D(>HZbxor1?f`hsAy=ZHs4VNKHAT&?Rdwg)bb`&t)$&o1Z5gmtt!JA|+v+*PS7u zVn0sAJldO28v-p}%DI)J>tPVvV{TC479(vNobq)Uq5V~d2JlDphIaY_;aN6SQo0sp zIXe2o?q>*^T>FT)6aF)sV}-`M?}pGTCK-suy3#T0XCMna;JoC|4w_~*W?OK2yo)T{ zL=^9WPt|bcqzuVhOB++2ZPkjCZQrQ(iCA#C*vP9l4*>vu*(;WLF+J+M_i;mRyK1eD zZws`(WxlZOc2n$a53BG|lgLkfYIq<(-)afKFYHIwXj^%c3|lpsEMSHpP%RS2#~Ubl z5@N66DNMHMv`hs9i72Fhc!SHHuG-QFx#9C#0G7}rKC%05!vy#>wa$089<&(=23#n2 zHF=CFdsYl%$AY*Md^EaiZS3alhJED#E6zPrKW6ML!iK@dCcNY+EYgATcA)n-_o4xt z9V#u6+}mmZA6uBfAuxai4*+_bv-^E` zK|gAwEl)a>`jaSgV^6xY$`l2H$L|7?z}m4M!j4Vcd^|{^eUj4Jv5QmtZa|sVo{yeH z4Vjxl*pFw$xvN5megzC^`8OQIXKokP$ID6wAlR+7^! zKoZOyq6-8jZrvdWLim4D!}+ppYN>Us(S036Mjn*7VHNha=)_H~b{xL{_&S}Hb;LFY z!{b|m419kTwisJS0qR)QP^t7RNi9Jl@Wg5>VZ=3imPu5v>x@)`lnnew2um)#5`7%6 z1GS^(RR(%V@em%eo5V+h`C~x>LCMeoNWjELrH7Nl>y=%1pF=2GI)8IUHxD=Gmc3}Y zAE9uc0#^vh4i6pvbsRdpIkj6Wh<|*2Z$OS78bjZ9qMYe(nXA=vHfJnv)F8wHjFMIy z>;y~5e5xJ&1!E2q{eE zcSxL7a`PYxfPYv3MFJ2E5l<*#Kc$@oa{EPd;S%-m>ZGFb9z|EX-%EZtr7eO_!9Osp z@4<{rQbR@13&7t5@7+)ECBb%0N{tnzOM!p_4RL**l<$&z8pO$~hmchp3-V{aW~Pzu0CWbp|BD5!?An; z1EIynL7ws*KN`FsLiK)%F1yX$YO5P4PT|`WaNnB>6C0lFa##bG*4vhsLFUZAkI2~6 zx%o@j>bWvSg0{!25iN`l`SnZ|t!y+MvXjyZBL3^0-k#!F6D3rnt*M z<3MdWQj4bGAX-n%hRwL!=h;K@9uXqUGY+pHbjLht(wxgj<~`TVoRkM<$Sz?I5hIp7 zS59T=b*0Zxo!00`LPVvZhKqQ&OMH|A`C$Kuvlig->r#27(^bvf83xs$!wnr}JsPjK0=A(Eg`^D>KzV z@XP5ZOp6N7)v@tQ>vTM3JAUTx&p^Syf8!k`)zvI7A0htta146t|8dM&X*GH0G{=iX zN1}zAV0~mwePjsP$mpLqAZ*k)ft45vSV+>`JYgYpfmkPTYQm_bNrp)y+j)VpvUYhY zn3y<(8`UKtl%6IaNieTV5OHb{eboO)iq;ZGt@YHrb9(u@zb5`;LwSXt=4*9@xAC0i zGxODS!+Uz^1wlCA$NgPN60Q#L8x5D_am24|&l^=f{Zrvde=^^%ma+~Pd#rxsr@QmP zeroMflHH>^SMy!guG4BK5cpR76Zd0OHHw?ITe<$_UnR}uMYHdJ4UPz&lA7oHmTdBr zIy%6+{`S-&$8>en9O^U>V~Vw&j(+72(Ws|0?`Y8UB+{%o5pJ}uY}I~$++Us{ zvfQ2@zJU&T5fPYwnZR%NMNOii^>5vcn)$r6pV|sPP6-p@f-G2!@upVXjd}UGJ2%bH zvZvi+poy(_+0~OdD?EYuDV|o(I_a^c7n9{Wl$zi&5(^-vAIl#uxi=@d2cXINv06O> z0xX{V5*m^CiZjv7Rb-diKc_BnTQU37gL>6R7@|C##>$6`&9;s=RW1J(VXLf&;Z)+F zGH&smtBqawXQ&p$e^hi3ZoTtnFYWlD3Tabjqhy;jf3+BcD)S6Ki%lioTb2d#Ei{_g zwBar2*K&6N7U~P($N<4^u+TQ$2Jg+gy+MI4eBEWUN=;7ujMGxQ><4ef_X`~A<0&2n z0b+rk`DohAQl2C8xe`XMYm>PP4vpq^{qw(q!&1urbCR6BqxRHpAg2)@t1g-cv|m6- zD|iMdfGBq|BFO}HLoybzZ*xVzx1NxfPNqi8SonF!z#Bv*)rU*}B4`6dH}E$9F?EQeFyE#PUmZb2zbF z!8x$Wdrsy2)kX*x&?1fhT9LwQb&#q%7Li`giE!$DI*4p^Zd!PBJun=CXTG)@l!!KX zBM6HvT;y*&2y$~i`nJA_x_i>WGW~L;d~9PC>U<2*=Om`VVe(00Tg!J}1SxOlXvXTNMj#%3ZLOwqy>lSZ&q&oSyQCyRrM_jE#jW zMUgL1ojRX$Q$%v*)C)2T3pn|RJOjeZ>4_hkD#y_Nsbz1P49tMx^y@;>iyM3t;h$M9 zNu55Yy#zKp6rr6@{CiNmPKUq9#w@H+?{#w6_oLlOca1<8rV{R$^OZz>;uyzq3tuSkyI+cn>dd(sh*Wtvo_DgV=*aom7j3jna4lSMYOV;}C+XU2YKXZ|HyQb7MS-f=z*dDw33rNx znA`(SqgZO!bfY#Tj9$r{At`q@ngT=P`TU=lOpiM5qp)NF+~Fnxi8iw&#Cku$CR@i z%!TQCU-tG!y>uH6Khofn zKYo=V6s_<6#CoHazi((c7FuZPaU0PAmP*-T*k^xlO150r;;h*@ebU8VM#;cp$3Sev z_oD#FGZ6g4in;KAUHA88P+d*cz6V@rgnk5{3nas@hUu53k+?QwhmxVy{h7?aU~K)Y zE+HRb#eW zXN_U)QnhOiEiPcc*q`F#L#{&ZvrvOO(SN+3a?;mh z&Q;*LmZY0RNw-_;Il4@AAa5{p+zXgQ%3NIOXmZxU9a)3;;Y?)S8#S!5aR&paN+=#9 za@RvBzT=X*=qgXFS5R5nl1`*3zzlGT(hP&Y;LJY*^8wMXkWF=xXL1-2``uXojB*3Y z43->&_+-NXTbm_+y8aCk0zeC<4JyD{S%b<9{#=2Ll2_lAuiA2uZ9hgSm1&+MO`uW; zE-U6XIR*)R-fqe)METuAFQdWxn@1xUOc4GGwR#{Ki@MW`wx7w@L$^0Z+1=%Ar6*ZH zKmY?(?}~F+!6yW6#Y?PTrGohzBJs~VgUl^A9@l%iM8hvnw@UZq&()}A%!S6FE)OB_ z7>9Jn>t>DcW*>YwDH&n)M+nFfHY!(a|LYh>)XtZ1nB}XJYo2AwRA?T9NW$Z1gNf!3 zZOv%#?d!$O#`zGDEN~D)|BgjFnybCJD5>|<^e|Ji<}sibD){Aw&Zng;%4lzKp>x2}|z__;h+Kz3Nsk(C}(7ZEzm7?f(X}7aNUU*Qf zu8o85IJ0Lp^)o6$&3p}_ygswoJ73-i8!_YI5XK)#QlCOVm}Z?1q{obe%No=;WD?We zx_2vKS${lTUu={}#cRj3$Vr7c?PpeW(w$@I@r#^g?W}u-Ok$v(V*i(Wo1INL<4PS8O8 z=3op*5!P)yX)X_;5aX2o&Y+8zSA7nM-#j;_u7MSQwylFo8UN}(4a|T%ZuM|D7yN|~ zpl62Vprv$2>;#hrZ3DS@frMx%oKvRMd>E^2*qy)w%Zm;&+3c-$(`K3P=$&mXzlf8b(#zt&%%;&JsV~h6WGOw^Hg*42Ooc)M{r&A)< zSE?U=;WlLIrtNz(?5cgDmD3Q@WJrFryGQo@QE^q_nM=^6@+{=#Gb;8kkw>UctCjbt z?XsbKEM9M?!`KsW{T-5R?d1#X%CT6H4R0g>-}|XYlQwnzEkr*PaF=iEbSOtKzHDhQ zw!F^C zs{5*`lb4ymgomE@D9`$WZ3*oHvl1U(gAdVu>`Sh<5rR~6P3TIq7j5)y%C}r^_C+t_ zE$pvlg?Emwt*q3^PnnJ})xHbmbp33)F>VD@{e^TWJYC1O0}Z6$k z^@|n(a%_OxMQ|foy>bBw&2fgRI##A-%<^{u7IZSh6fcWK?HshypPX4Iy=fcw@1%Q0 z(+CZp(Hak_oOegP$tdRiHYvQbOZ&_r@EG)>4|4LyBe7XwXy2|bGLCsJ;I#5FW_uL~O?pY%A zn4NXEoj==s+@$HF6JVi$3h)O$A5{2<7)0|{CdhF@5>Du3Ubii+>$NcI(uErW16PzS z&2BG&vVtZ1b~t*@$ekPiSNZVI$=0G&orvJ5tBy(3lW8}_%rE&W{}7hvr}!}dTs;A- zI}-2>ON7IN8wok<5L7bTnEfYD{t}1Lc+Y&d&$F7`K|vadoV6!i?IF=5qqHp2*hTL% zF(+*2j|zjJwY?GME@N6~%x}m5G?R=MEM31)r)@v8UwE+aSacR4wP=@Dn)rQ)VR<@< zGjeF+uwkc@F*|w#;33L2S*P*4G!)~e~LWVfcD3kagDHiv^8}Bx`ZGSjaf~g2`Jv>-Fd}$ zU}(-lJmy{;_kVbM$L~m=?tQy6NhZm}wkDp~wyjArv27bOv2EM7ZQC8&cJk}bv(|S# z|H1vDU+unD)m5ujbsx3&KF z0Kn^Jj3IEB1&c}Ecbf!;pq_DX(L9gd)iz^`$%QmPQiGVL8jZR0SR-W?2-J~M{4Q&+ zrh@>5XE@o+1Jus|?k)M-azM|W?9Xt!5TG;|_uF!f*p-X4VwMi2`f@!yZVozyMU$=> zr%7REyWe2@Hc+YDml1Zot6xPgUrbUB%22nxzBVbv$!MwXWPbaH>QrYh&e(iECD>Kw z!?Aj+=9>2eA537;bo%ufgpLIUo_0=X`Cz^~R_VQf)~2P|Fbl-?VK*cm;T)8q^4P7! zCm=y-$(!0%7DQxKe-M^c?J6PvwcukOQ~lXjGy4$6i*40_wL=gZbGJ2}QLcpii=Rr_ zrCP4f)O}5nt(0dwPAcP@wS`J#1H3cqJ|!3WIbxwV-p(um-^TT}*T1R_%4~#+RtZdg zhk44gPBt;_c~RLW!UnQwf@bqv@^bf`;-G+o6N(gjyt6mYC(Vzqk%~q&zNQ<2rADlz zygB~O#UXyoodUMP?FcHgouwD5kb#J z^LGSh9>9KRRe$8n?*Kmnxi6JFbapW&XY65%E7POy)n@v7f_egwxbYjFc^;~B#O%K- zxtHZk<6aH*vNsnq6hXWD+Lb9~e6)^) zrQ>BwKrEh>`I*)`L+r`K?lmutaygEtu6Aq@=8t(fRtJIK zp7MW4Cf|Nk!Y+>;M%%*ILLRhP&eOk>(EAi`gdpf(?8pL*6^E2!KsA@O$Ou&Zpi*3e zd#_VHk@Q|E1pi%`5XTpkj%L-JUrR=0PQO$q))gKdhLumKf{d~a{zy;_lZ&;H{+p-X zeSF|Q2G0@T23_l~+?euU{tRzmYgwq5#aKSP@LAgzfy`dLBwY3MeHr|jg=`)4TdDFI zn1)5W!XPJNR{18I&NgE(q>iV#Y1zFzPCKFv5?Hjp%|sY%n=Z!s_GCoO<$a0R;27i- zNgmMC5-JLdE>%YY_!irobyM)D2p0_*EH77k56=_>$}swqt+T zV$VET9@%!5eO_1P$v{c-A`+mffC=)b{%NrFJhIxi?&|UfvnsJT9VDtW}8%US9Aja!@W$g-l zG;+ORe{9yEULGsGTlkzm6nV465SoIqZ9lvdPTlZhV8~dk@ji{MPcRZ!hLMtVG3wH{ zEZIJU@}No^R(60!*b|~Z=&vIawG3krJA(KU@DiD^Ru7GW`3uvJ{V3RujL2MivAbwF ztaOxWvw>5$WJMr_kIG#;%?hNiC`mR4S{U8J4lYWqz0PB`=g=LlT`K_02uvMF4N0un z@RHX=mVQZA6h3rZLobGL4ZL4%zb z>f)QYr8Dx0;hx6L&5iV>i!r~nx-ZsRk%ZsSsaTJn1~YL+xTUZLwZE)}xMX{eN|YT# z7;Y_Rm#SySA9v*cCQ0dOAq#3FEf|42t5n4XnnX00e-uxWKsCMO(L!n8?ob?_@KTTo zL0@0~aQ1>*T4b?late$alVt!4Yu?q$K&#sPxvsj2J+`m7wR)5RKU>bTT#p@#fR*n_ zN9Qrt4-4A5{}>{0r3Ox(W3_Gm!K^It+9l<=XRPt-OQjH}0F=>7lfAT6=>Kf@R46)V3amMS&G0nVMdX5DWrk zgKG%faiB>qHUDHB?B-nwt zbFW*;5hE{%I2D!+9lq)E7CHn6m|B=Sm{qEj@u*+`ENgLEsXab5gB;e2JWvXZDQPsb zD+YWv4qV$#1|tVn%6WbW%3sMV0VLzbtlXugcvwQf$$EWS;PP$1oni8UyKH=m`ve${ zY?d_i`Kds~c3Y111M(p4gPOaIzWk{^;7HzU%hzIiv90tKIwsv^Yn}U~jw&1`izVez zrSuW>8X}0iItC)&9m+Cmn#!HVld>U-c0Y6>`mm6)F4@>$7-c|dsw^ARBJahrk8*Bq zTGlh;A##H;k={WW8qJCoo3}zj5$?7PiqO?MDz;g){&N1O08{CrnUwNSakbk~J8|3Z0drnGrj4?%J2Gb8agN+CTZxggN|<%A*O(QROg3 zuAt@p7PzFLK}njMD;Y2IIFZCYzHV~Z>G?rInwcuMCaJ_#`8oCS*?I-sYTvT$dfN2t zZiN`ay6iamgvT2bQ&;%&g>bYtKJgxEq06gP zZ_D*qPmA&NX~*Ffe+T2W|NH~OZY@9m3#c7Q`r>>wpfkyq89E}YYo}6Q}-Kb`i9!o z_XbFRvv4o4zJ36zbSeMyJbOC@lt$Ep%D}%lbYq{vXe|4KjJfGd&Kr>7ep0EW1*J>=hFd7Tns z$;Cdr7w6(!?cb|i#=|b1t{_R(q09!up7SGuLW1zq0o!Kv>!R>8$uLiQvfMUP$nk0o zkW~c_?M!N5>jlHhNQ3Pq3Ue-|l8*c_MGmP}5fkQq^uyG1lY0zSFyO&!r}?f~Cm zf^+^~-7VzZnWXmpglqnxFH?>?u~lRxGRyNe?(aGHxh(Flg){`Ee|ofJx}6L%q$!3t zvAjknS5MI|JE%qHe0N}UdxAIERUV36q_OOPufk2>Jsqx0Ed3K-XY#JRR|y`j?M&Dw zyePXLGq(-?t_#!^lj(Dbl47XQsg1&jDw^NkNO!_)qfDL*dj{#7bUsxDR=aNQkLP@-KuXTAe91^G-{T#tWnMPV~>-QvnFbCn8NDM`!l)>&l_li?1Ya}!- zS71yMep*p_%FrgUPNEIQs58P@n0*6EThA3w}dCgS-P=tqh*u&6^h4E4E_D+9-Y%NF$K%7L@(>BtA| zqz?K3J}eE3!8Gc^;dZ=Pq;9yX>>01o@Epi9^iqtKRwRdCDb|u6-SufnD1;qnGR@ly z29QZAW62F#Ie97m_hp}B)QvrYCW~So=lZ6MZ9PyCh`3t0Q0yS`q@JV-9Z}3}OqyPo zrfIX)I_3J*mgy+R$l@I)u}pGxySv`~fDA`Y#nC(`8Aa%kVwl;*(tih?3um4KVJQKV z#5Ai{rBgHzZ9`uy!=fiKOZ0f{0SW>ERdgB*EsXf&5o6uoM=72 zg6)kkyCmm=*jRFB;ndKC-+IC8n@+-%OHUC{`k4@bnl?>T2qh|T>2s&G2?#`u)v!an zS^hswxtEzM<$?i@KlJaU80*8LlwmCD&RqF&;Nk+}Y`Tvi160=PU#ENukIb9VeCDI< zf)Wh1*zkw7#O*X))FahE7XnEp z=-dgI&(sN(w6lNo5twraLet+pb>Am9#Na>+2`MU%=Q9u=H#}9!>qa~hzxl7@Yxv*b zD&=Z&Q*fm$yU1d%nw3U2pR>hK2oYbr-Ah2bs~~*QWdpN+BfM?HTwfYC6LG3o8Z)$)cj)0) z!jeTDl4P9iu?SuLXQPa&u!M{h{h9w1cVEXxW0Xj&T8^+0?|H!CXpb94i@*UT?z)Yw zF0wy$lg%4m*cLxb?^oYFCb$4@bt~@sAyEw+i4$Cv#{CDnWL)&G29({KzgP3perf(5 zk1OAmqm>)vZfZ5mhdK|uJy;Ep(a2*qr!qyHfYqtAdMOr;sDCVAb;CRYDL@C+$(Nkz zBjlm>D!7q%4glRcXf~SXGq*|&P7Y%#54e!^PJ?4TzKo?q!RW|jYv7J%WL>Iux=gN!q)7&GP|Y5{`_FcK-5C?TaKA z9_G6u#%A|2NljgXXVqS(_KH{G}?FT4f0`Ei00>Tc9R2Wd<+=aAo}(_Yp&3*@di5 z+6+Serh-s!_|-NH>-TrxN^Da63g;&b&cRbUo2Gkx!?EELBu}!pp_W+(mra#re85oo zo{D8Rd)srH?o;w&6|yOAIP$lkrsKmlHmdb#W_`jEjdVfi-UMvFn%Ub$Yr;o#GSHtg z6S#x4(NkHFfi?cgwz9^*ESu(Jc4=w8PdD31R(?XO_l2z}IT%MTt)Fw2NpFuoundW0 z*!t%|$M19`^7kN&`?AI@TrwFp5>tXJZ2dayOX)7WGV^x#1X8TuA^s-T9=Www&8(iP zo=8kZ5WezW;JBJ$DERtt^GSrF(6WuV8X04uQ+HJ%rqPa-uTmrPHGFBC6l)J1j<2oG zFXXYgv>trjgWg?nlg#P%&em~XbL(P1ycx~a3&M9eHiu?avx);R_xf=CVrKcCB-kBf z(Sv0{Q~XmNyGd~31*9Terw(}H*_L#=s4=tJcD?c z67&F5glM*nyqL#?zo(BPKHp=U<9<(n;+h0hG~@UBd>50BD$CZ5E_4Sr_919!Rffae z0_zv@+S`A)LN7cwQ%CrPNS%cS@R^t=elfKV3wfpo;<*l~8Tg_eZkw54txO$Uu!s zS$>d18D}}Y%=Lpc$`IE6C>Wimm5))&k{8+U?icot%Qk6IkV6@peL}i?fI5GTq?Xs@ z^!M{D3>adF)Ce&##P?C{Ff&t$d4a!X-48~2sQ&%=oRHNsRoSYc{ZLk%-EgBnw@$00 z?xa;G+c2}-c7_*kLie6fD=*2yp4vQvOwXbdI06^2MB6QooG+|#Je*=OdG6U=4bE$} zb>A|P0WDBOF8%IO^tXw)Vi;IR+V_!SS4~x7rar{91Qex zSS%Q?ZgbwK9H1%~C<(lsL5luQ`=L6fH*w8qA-U~kxUEV9)zLsh+~Pb)Y2(QfT<3{PAMu^JJZ zQYxIja?4^{zFhHeW*{?|sy#6Eo#*DT`CBF`eI=J6l|Ia3P2Cp17!j(z6*o6W^%H%S4+09>Y-vAj2H?E$m=()(>R zJHqBSfB;E^CqP)Z6rTh0=Fih4*Ce_(So4SDG~GY(D)i;0i+Ya|2)%Q`e+;+5%bUe;#c);H3 zh=??R17_3?BJG}uG{AB`Iz34zdM0%$AvY6YxxHzb`rhN>!SKg~9jzUb6sJ_M+}fTH zQ38Yp3gtd`Eh3XPDFlFnfNtSPxH*o9nZrwEdY135) zAnpYt%RK2Xd zE3--Mm*=?+T3`5%{!!zjJO17|zbtaP4H4}(-X)o-vk7@Y(v{CB7~+r&vuC(T+AOe* zr?vLY+|f{zd;4Z%^Xernu&UPMXL(5I00o%vBok(arKM>J)RIhz zykN4ur!?lAG`a)x_SrU%=Jk?rs`4h%!XEa^ykgn(u=>r_P?h%oan?>g#o>mIRIMN% z+qI>sVAzzqk`*asTyGa}T2zH(C2(1UI#uACatrGYYYvJ%M^xM(&hH$uC#xt z4r`7?st{U1GJmpq#3Ew4Fj^CDp}1V+3CBZAY>2bO?4=O>vdi{-bN}PBsMFB~1X-a{ zYD%Wh*E4kVg5ZMvjcPl}?m2M7nw?~F!-nvsBG{xwT6o50NnrKu@?nu38#489M3l;D zc2P%on+^xh#*dMsh9{k@6?c{(#)_-gp(&icjdh&%fH_=4)e_engH9Yli7PJqg}Ju8 zf3bm)4SiuRM`wF+n8hOys{|99YL)KrniA^z_&c<#ueNZn;aU}Lr!2C@y)i{yRJ;%m zo%5>#lTt>3;mg=K-w!&_ruzgJOn?7?oY-+LPd{-a%v2UYR_D0L4lZ@bGrd|Bf7iQg z9eQ0Z7uFS%uLO6;e6q`YpMg6`SdG?srCANU@IE%ApDYF8)$4QUaA(=lN_J+TKp;~r zp4Zf-(`HjZ<=s?*-RG0+$plPz3?f#*Q`fp=9E+GlW>U7`az#r!KW3(6a(ad)P5&y% zq8uD(37NyPI3A>ne@AGiGn+sLyu16`-HU%OFIBd>Y~$9yT1)yB@M$(x_&vgau?Re( z$UxR3+v;g6O&|VJsT9eWv)*>?(J%99g+qy-vRnoA6elJBScDX({FU?9!O-g6)mMqE z@Ul(f^mlwqTJDWgyhE`k&D)zjkUCLD@18oy@NzXaBXm#UxtZij%vZO4 z8TjB&>v;+c&xOg;SbyHOO;f34$={|aT&7t%Bdzxo&2n`bM>Aat3kB0bruf6?teQOq zhf{r+Oh%1O&hjY59ML&9q2v*1Eb|K>kb79p=DB=m;hZ{vbG3epW8~2oz!5R)iry~CG82No8II!lpB(ya&<4z5fz%C zgi*dUPTnbPuKildVfvu!gb1va=- z^s0r+E%+4?=KO=UkI!79i*?O9Pk^9lCMQ*VnOsk#Z#_s4Qj>J_18E&PI^h60Bj!Z~ zM&*KFr*SIFuXzv&Iew_qSU5_N=@86%d<5;Yf$ zpZ?r$6=DW426K#f-6AIwc!03ob#v61C8?lcJh1ThD}xx(7?sJhJpUs=FUQHG@@>f$ zO7h=Oqka5g4K1=KJ0H(l(;>Cu`fj9py{~g`lbfN-Y!Foc^43ki-|#&B1RI7S1kI$S z$=9inF@aiNQuBt67<4aG5m=wbRq{@}s13**zSU_yW9_A5FJ^t@!jm#!?O{gZDkA zN3Z`~xCQLu)8w3TyqjvNwl-{IBKLYkU*CTy>S||#D_pjCpXxoU<3~>(4{}P}pp*A)O4?fK_+8dq=jK{{ezw7L5l)qK`KWGn9S` zw+P0Rh^}3uh%zb>bl33*gGxO#!jlu8S&*N5n6e#Yv0O9q|WKatg2Ec0f}{Y z^I7B|$$Bmi(E{R>hPpDO+5TyE{Q&(NS=}h4l%d3Bib~QW$_R!8wAhM!HHW)PzShNh z)so$k7M*I&*UK>{Cu`~7KnfI(VI>Ua1+7*L3*O0QH{_+9IR-JyOLvdb=1Zw&I*sh| z$~=kV7*V+F?Uy9JR?|uFJ#z2yg=K+nk(*WM?v9?mV1{t3RuXEgT}4bhA5Vj}&*|9^ z8l7hlC}uQdK~8t248wI7skm%DwhQ{YzYGEdRKi8>NA6@JOI(8X#Y{>bt950Z3~11* z)~<25uj7-wD zht)Svo~^8tCfs9j+wT?@iM+Senrbz`p_A_6C z&`RwK&Rx};nC={ZNz%dLG`J`tY~aK{+ATxaHbix6>|d+5^WVct&>>{=Y;AZyL7+*_ zt>rv3eAfao&ag)} zSUqYB`S0vZgCo7tJ}Lk8lCPUe!Gz`fn8Ecg>E=(j^DvVwA|BuSY%zIRyg%&otZ>gw zm|bXTE-MLQSh67Nll!sFvo1CQx2q$Y(v*yOV5rU!r7Q4rdXM?$(>EXp$;_;2e&d-q zWyg$_bA4i>`F^z1m5*!JdW(p`1E1WPRBP-!O3r;09}$vjDo^_)Q6%3gNV1`CCav3h z;$c){HgqR;n=E>s9W_^|HoV*A zh<`o>1@Mn+Yz&QHpEbRYxq*dJ*M2~N8&Z1o8IKCoHD0|+1B>FX-^`RQ@TkRM*43`$^Yki92K&$m4+m zh-gH3l2@adcw}(Pm#acbf zCLx{birMMv=JzG)9=dvyx}4Uw%$cFU;(f=gErsZqx{}(~u)iqDAx^hU`0tyd zA2ckIJ{b8u0D$yDWryP^D9?W2{F=yPsXlILmLGR8e6P+q@lA^XT9)ezyD84qrUB2aGww|VTE8{pxfrm$?XXKMm*w+Sw0NIzrEN*-vy`D6$6Bh6QKUXH z$|xqn{y8JIM!baG=H_oGoyOLJ@lNIy<7Mv^!tZ0-s*~TFSxo?4n9Mb8Y|4c)Z)9|i z`KWjMPqG(s7sF1S5v31JEmT_!rasjxCGgl&mHG@BPxguIKC;u%2TapCnl&oCd(@!E z_$>Mm)RE{;Aw0{{xKCsVYG{^|&92)=|MW0fOt=fvUv7lv(#|Ihv;UQpIWsuybL<08SjO$c!Wf4xKMWZ{?^exItDTS+=?UNnoIetSJ zPem0^yD;37Of#2j_^fNoCQwkH^_52&J`IVEfR5DFaDan9R#8ljcI9 zF(=_t(nr9_3fdpaGrzAfQvOO+`Q<##Jp9Irkt`?=W7LV5aUjxd%Lz3iC}-!A z6jbZv$5LKy8P^bjK%Ikx0^RWYdY+~KUtw4J(@XT?JRB@ET3rnG0mnG+ztX4WsZIIsZmF=3sb120J*6ErJm?SidZ;G_HzK9 zR8XPj-rBe_N8s~~i+stLQii!aSsrPe#zYVse>+p~#!3YuZXKgl1 z3(wonDRe|JVPNw`?t&lHkmp0~_?8s~%n#~6S*Y2uwc(Z2{P-Es$&*puhqViY(ur7Q zbc}a#mxQme%p|H?DQ)velTl~+YTf0{XBzIV>z@*PVNm%usmzB)1`632{TdPcul^%t z&M@#*T@d+}38Z`@W|5bi7hu#!bu~DfIYh}cCEPhubD<}CCHr=yChrr2<6pCReqPHa zFG4elMgPiuq{pk0N_>@EXO;X1>+B zQAtKaM6DCsZA`^w^u>M01`yGTO8G1{4OyQ!=!C88_!K<2{%sz6WAp@Un-$(s5^H13 z7rZtU1r0ByYt$Cqhrj{!8mciO9vA<09&|Z{r;va2e_10A&@XnpG-BN{$sZbb8FgEk z;2U@j=j%@`c=Z!>sGPOojJ_;TEOaVBo`_K5rP9(Xt3|Jg`xjRph-qha&JIO++xuA( zlJ9y>XG}h?TG0)dVb@zRvz3;nCTMbpCd^%DWWz^ z@J9mvOdPzpf*LPf+4?LS`ecH@e6Okcc`ExQC^*>$@do}7L1$dU2;kR|l5=Yi1}Y^w z?5{8MFLe4Lj%^fX6f3wzseZoMF{q6_YjMwZVeRgEZ)YCbo0yCxwHXz82SzJYGN9*t zwIvIVghcSBJHHsJyK1{{<#kxW>E$4?-2Rd6ov>74we}ftg-FPAHX0U+i&9UuL9d+dk(JWa(ySs2bkJL7)rB{v;QPghl~tDD1P=(x zLo*8PffiIe=W4dAG}6Puc{A?xh=71+tAEJT2uA)Ffm94N#e4OwN1&Fx(DjX(4~3;= zqlyx~DDk~1Q}+p2{{q|l+N5nB?rGW7Wv$lc`Nsd<1^9NgR~bcH8kjW*4JK`Y4hpK` z4`0NP$73#3HD(LTbTHbOJdiRxTbMEM_+vc zC9Z}MW%+8J3U|<>XsI3?bMGNA(|HVgu)s&t5)!jEYaOIoE>mAU8#?^uR8h7qYkdL?U z&i6Ex`p{Y*q7cTHeHvb51J)WQ?Y5Mk<#I?nNVWQ=*Yc{}enJ*Zb+WeW_iw#cBkP`$ zP!yF&d2LwDDv3qq!}%La+U1VgdAw8A4vqv7m z=(gm;H5Ru~p-pqPT;|Qr-84mo>fA)vH8;V;)fO0)#9Qk?$Vnx9h!QVDTi3a|p?wQG z1kLLE_~7hW_Ony&3<+6GA>ogUl;#MTq<_7po}?Vrsc@?OUeD{?8p(nnt3|VRw2~`B6^` zkkn&P-b&>r4@g*AN^lAlH92~Pv9HEvb4w;ZW#IHiots$JAV3hsF)fZU(KbI{)6CII zzSNs&(XDXzbd6AV^Ht&Z2Z8N%MQIPksD(GJn(FdXE3&EpNSO>jy1Q*m+rEff<8=;N zUm={`Z72aWNioytnf8;MZnAno8HjJo7NG<76p zQne%XxyI(70R_>i9k-5}L@ui}?&FvrMM1264(}@Art&-qJB9mY4!lDxPm$A+lk29% z3m38gzejjq{HQFvWs`xG^S|G7dF&tGVd<7vV1Os5lPf%$?qTK10KhH~5e^vZO0#qB zHY!TkZpf>9sR^%SsU&gHSh?$v!^$rGbk2cg)%}T_d{(W|uuvHaKar7^KkU~H0jt$b zGSsQ;c-dKC`r5jc{Snn(Gr-SS!Ij)%{M1ZgYkAMw7ft82$j#trapYx)bzMc0ot@^m znjnNV$;igh9tfP&y?@f>*@#IrPR#Wx+?#Cc?Xf!KO{=C-1UuTqzT7Hjry4} zE4xSNbyt4=+PTeSg9e(V8VDM4IqCG8m+8+%@j8TJY!Rjx$p&*3dOpywq_ng<_!-UOQPp;J-EK_byu1<>&4T(2*wtD=p0umvpE$~@ZH5uV%eHyTjx#l6YxM)?hCxTz^HeuRJ?a8bxX zn!TmVY_2{|+dP57WrlEF*>m^zlA{+wuQ697fS(W+B6!f029zE9ZewqEGL`iprX3>B z%hC_G15}{@K+AG7qW=2>DBes&8)(qGp?BXoLoLL@Z?63>oa75?u$=aS+Wp!5f5%$z zaR0|zo_3mxsb81MZ64h;<4H!`i2S=yk|FpZdk{kAl0;#d=+vh(uIhQ8My>oOf;W>z3Lg(Z8S1*aH}< z?~T;r-)Em0`~}Ks7=7PB&zn|3@Y!Gc>iBpTQnaoag?KrW3gFR2y7XYJeVtX!JUiT& zXGW>Vh8{R{7LC6!OyGo)My@RqXu_UrGE;U9ktl@cxGhLFKObgh)CeESFzp&H0Y5F|A*ljs*fC>o5#68$pM`S7FR!#Pig)vAyTHSCc z3*Z#-1%5*@c-aR+2lt4qpusBBs<*sG%&w6uHK0d#-*5G_t#n1HgGG@{=xZQjSrHZfFKA_&h4f>1rliMN&oY6n{Oy8J7=}_8e8Mudlifv1yVxDrYTw zW4XRyk3?2^A&(2?{8* zj@K;L&FIaV)P=Q=*d*Knj(1dF?A9`m;`s=^P+se83^s1~o7wmKtU|5%A)=12ShC=H)agK%PKTf<9Tc9gcuTttt z&?Kmo+Bd;S7 z8~;~7NZHkf2jpLR6z#%X^$AFXATt<=3v!Rx;g!* zMUl|+Adha-zok5ulw(yOhX?41QPHuhsoKaU-Nq%$ZEadk1?bXM4xfMb6F!K_4{;Ke zQ_{D$1NzccxHiB>%KGBcHfmt_$(CksW?)%k@T6X$a1O`yyspqEz#q;rkl?OJ?bKx1@S6lspdAzH9gUQ7O zNV6|aS}FD>K1qbDLSCw4sz(F8zLrZ(^9|42j3V}LYT^d=VATadO>y=Ge!FEN+jsWlW}qmEDHWCAeH=t&s>%qZMOm`Gn=dj@K%u z+T#nAjO@Hd>cW8F3jx30CHiy9o2uq+mi+?N_niIW_*pe%QN8iv0x?*w>%q)gsLN>p zm(pLuSUtPcj>Q;jsDLU=LbPZqjeNi_H^b0UZQfho^jE(ePTbVyXg6kAwR*th^@jI|}D&m{u;lA^dJD)}6}}sty1S6u&rj z;}8K4mHN0nWnT_&-gu_(jZFXIQ_^myR#e<1R$ac7vNHeDcy6+ehQQI*3=DiVsQ8<; zPtM=eF00qFD9@XB+UP|zdD{&F`TToHqP#q1KPVu1bJe78cn=>^^RDP~ zs!l5n)YjF8sATe#B7U>UCHmVJs>r8vMzLkmV^4n?i+k{}Rt;l;Po^~py$M{0_!Dwl zpSnL`KdoFVQV(IBrt@=GQgE4cd&-M_`9h@q;)47^iI$GNKu^|pit`B5y!D(Ji{ls{67!vtRXXeHqt%ftFO%4r zx-1?{JutWT=Yix+D>n_Xsd`- z@9)P{^F1S9Tzw}ydA?byt!zb7wc&gdX{@jQ`MVASdNpdgH2}RlK+_#9d#Rr}Fw-Me&VxksX$+lR%LRg|67=&Ew>7N?eG=aH;B1(xA{O zOSVBk@u)bv;$4R7WZ-(_*f@KU${{XH<{gL<+HERF$wRV(cs7GESIW`KRl4M8xp8XM zS(d(H5tH23jeVZVv*Zku3?bMzjKIsl*0DfXvh=8D=#H^l1 zO8ShiGO4q1pMb`%aQfw5$Lt>jMBjR6JE8Y{wNGHQs6j>we@GpDUJ>6N=bYH~|9l(h z{erTMWw^lp?cVo4-`EQ-#BGUuJ(__%)o#x`s z>zEth3g?YYPriXCWafe^Q#zTLG7G@MS(`H}kfV(y#KFj=k(;NJThDjk@Z)roN+*%2 z*8#KnPdqo;eH)nK42J762N2#(rudJ5o?V_kTu&RG(N#YT;rQQ2Kj|WB!9PQZ=OtCQ zH=+t&ghWi@II2%BOkF*vw5DxKeO@O1+7cCb>6d2e&_N%|w}H2@H(`|eew^D(8!n&W zmVwiEIei1P6uP)@?j&3);`~)oEcgW0xARVGA-W6Tg6DT@kigWA1 z;diexb2a^WOVE&|4>jyq$j>fElwSN1AXU9_-rCrA+17hMi}bCPVU6(<8*r{(;T=|# zihJru37INVB%d6|!v&`FTL|4injquDEiOfji@{WV+lbbD`q)e@Xuhe~6bD6yplM8k z=;61DSa}sX)*2n15s>;=8=UuDH%wXPsGG3F!u!U@_sW1H*9>Ge?~z7mi3R;*zTc8qb$~2V7V2m;+0j*KpBNQHl!T+mH{aNu8<;# zr$3c;Y|%Tyb~jYVdZN_TI@M5J<$9_B%5|kY2LrZa2cPq~Zid#EX^Y>$#bquRupLoE zZO?C0Azj*%wg&pM3=g+OL0A{2JN@i=6KjW(rM(F6c4GPsrP`@^kNQI>6uHU+?0uhFx9NRMDZSUmSpLGW*l+fK<7ZL7WO;2DIO;mE z3Wo!UwR@g2%3W?{EQHsWVdcnKQdYlTDr530n>L|oDA0yEEw;iEPH=Y)>=D*s@#y@Qv zbVW15&fP4Y4j}OfslrH)SaW=j2g88R>!5^J&@2sW^qo*}rKJIn{nGKyTQR-=kW+MS z{YmeQp)h|X{{D2DX2RP~mVQ}61Db@Sc5nv(AtT_P;C5ItZblRKz-lXoy9V#)MLhFeukW=VHUJOjdf?mbCvfVIle} zbfZjCkz1iSLb&z+di(01I=<-JTPQ71+}+*XrBI5y6xWNpyR=w~ySo>6ySU4R;_j{& zcYXKsl5f6$z)Ri_GkcPg%sDfeNzU4PueIzuIi1;;^-bN3=|d_0EGc=7%(6ZEU!cx0 zgi@vIMqY5_%O}=Iz-y%%j20v~XfJ5G!?7xn)XR}!0s!2$E-1t zXn-ysj!r7|-8wFLzro{T_2ML&m{aQ~&+N8tl9scVQqXCoKaH7?{gUv@-?I63w#i-TMyC%=h&<;+>|P_$plcMlK0Ae z^y-Q43n%LY__@4<`p!Ch7L6vRUD!^x&1oTbfmQ&FJIfWt!)b(Fq3m8Bo`p1 zl8d5(pLNJ}Jtne7PFUpc4dyS!pIZLnnX*gH_%Y5Zc6Pqp{Z#~FdXe`ZxQ27z$z;{F81#~VVuKg%^l-(Et+H%L#fS)p@ zHLH3Wo#fhMawu8!#K? z^2O#oM~mr}mGaTq^R$)dwfTFso)di&74`aKzN{{oS5O4Iab-S5-A0smUD(_8p>-u< zR^jOX5kwSu3Nfb(dyV%R>`dubcE6cJ+Vx%PblqZ#wuv^>sC5^JK3wd3>so zu?ugTuf+2p-NDD8?ZVVf55(ViEASegs0fS)03PHc73B~R0c4rT@+Bz5r74YAwCshS z(i-k6))sqLAjH(@SuxRN`rJ~X+4S=4EjL)-_ELaB?J?iK7kV`L&7tN7s+>dR95xIg z3moqJFHK%d>(v{zZ*3&pK;@=tzgd@?Rm{d({yz(G-b*9gSMD>2*?G6&bVV|TyA%g< zs3%lH_qA?5GeMoQ&x2kG&3IakRh`PqulG;@mZvI13t-!&DmR4>#u^R_s@$JLDghXI zPCSP*cSXU#Xs02P8~%=kNmydLo-fML{9vTanOO&1D#h=y`sN6#*{cjNZ7Kig&ZI*_EF;lJ7XjyZADtotSG zvN+WB?b72G3cpf0%(8$7{qn(92$k(_kcA<CADMe_|UU3ev^qJB0#fr5wCls(M zuI1IyvD3W{yr8_ou6VJLDWr@v)(Iz4MS}t_(|)S7>gwuMj0w`_;yt-Mw>!wzi%5!8 z+j#5U;M2r_mN+BsipQbD&X-Qnh(!qDw4lx#SP=7a#5R*|M>0d}f1|&PV3FB$ubD8|<}>C;xpAnt;6s&qF&U(U~#(pb;=@W{1rqpSZqQh1zO6} zyU_rAUx{v~fFwEMUbE6Ym`Vo9O3Fs6x+>^lx}Kp+AHQ7#DlTo%@A$k#=$TiZ-zue z)-L)TVHeWH;l(6Jple0dNugM_5{G1yFL${yPiqv21c>#V+_b(%ua9^jJqm;>7YJ&; z{q8EWzOJy?g6a?s^CvzSfr6C_9JW1NMb5G+yHXWmmuv%fN6T<;PMLqJPlNSHUh3WG z+_T$kZks#iuXv2v@Ae~Tqc^g)2OdUEptY|Z4@3Oy+JB11kzcS*bh@yva!5=IOgx{& z6U1Pg4zJ!@2|CJE8?g!1P=<;dbnKJ?fS0$KCz1){YfJCk&AgbIIspw^K~iYP+$1R} zHZH920S0fikE!q4km$AvWE_`!@NOh~bENzR@1of#`M{$LJ?6a3gEh_()#q?Krre^O z=O3Kl3C7ua(}s&$hattwTkpH4H`H%xY&P`6hSP=dhDMA+>9&n1;OEjZ#cJb|;5x5& zz}6p`Ew4F6dDqq!za3y_+UXmmxEMSZ*NTn(3*Xp9Lwc&6MU$3Ji111_iGi+{RN|Z^ zS}BbYODQ+cwSGqS1)FP{bG;IV9Q6W&3R$qgxlfsi1N%{#akIxi3%C4KGAY>VlqhOe zM|S?geHCXAiXFx1_lzb3%Nn~FZFyb~OkI?Rwa0T$md`N`t&%Ch;Xefpy!rS#N|SGX zCKfZxg6({O%r(4Cvx+Rdo+T{WbmQF3B0-3oaDW!hlHAnq?aQwgL;Z=gG%6$R=4T~g zcBt*@uQRxJ-lQJ@xoCs1Kl7|giCeIpa^Gz@is&EF=Q~-uRKHBD?RZIWZ(q8TM|O41 zqO3AVhv$uSs|W0kPr8UxaatSv3=4Wk#a8C{7(L7K`~M^KV*6j{^Q~u$+R}sKs^~x4=J`X zo)K5YG_aEGr9`ErYT8N3ZlYdFHa#*pk_Fvo*9R2x{0F^=^G%;K9wj;dt9xC)o_Tb+ zobyWkVh??Pn5ti48+3mV43!wt2>fRklL4ZkqKU3&A@<2zyM9Z0VI7#bJ6#KleJL{e z>{m&aE4+t9U2-Y*sr{83d(;G?iUC7qRxIr)i!m;*#K=K<&+X7zlv9zM7s z#5di6r$Cq9d6h;Yhw|6=#b+>h*o-AolD{KmFvtQb6l$y5Wftma+ylYM(3}yxCvnPgOW;+6ftuGli`3`1gS1L zP%XcNm$KRh!#V6%m$abDJx#4Mq!oD^Rxgy zxx=uTGLx+9KxtLXfxZEa39dTnlR*=lT4yisfDIrr@O!g{@b&@cjywgiIi4zY3Ss&~ zK{hT!{x)CRva63LWcqW^k_*j1+#w^SUb!gh?l~lbC;e!jdMQ| zpnGy5Y2VT1F*=sjzn5>=uOjCv?V&z&CD{+S+t@?OdbAatv5?Ce$=P84#NC)!!IeyZ zA`));!n+L` z=TW&IR}Ls5nw)W`3oP7RSAFy;^W0ATvxn_ILOy4oP8Np06-?~1v$XdsWAS_NM?1on zHMjeeea|Gvlhn6;#rgOJDEB3ejkD2TbGv~)9e?UiqWlN)1T#?j4IZNPS{P@s#;zZK zz91L!4`%)OjO5lQCXbmeS9>R+jv|+JXyf7j@D03Z;gikg!W?2c8J;qZZW|osCzT`> z&A6#Lfy5q)xL~S`#8sv~y1d@SVUH+c1`9muKV&MkiMnRs6#f*^$s%>rShQjdqKyef`)3u*{I?&fbRoGtvXi%=0mOhu44=u- zMv@4T*l69=eTCg|(A5jOmPEIJcPO6t1E2Qd3&DI%K&UR0P^0$j9g?@8-DpyqrDc~K z$n~bt&rA>ffa<}cdsAGp-m*}8@=tSL!^9l>wdK92EHabfc<7%=>@O@Q^BWDHdN&)( zR<~}NC*|2=0EarIbGQ4Wa`nc`q`Nlp+r5k-zc|cL^s_*FA>lG;M}ABrWBCJU z8rhiGX56uqtUxWraGHdz^i(og`5saAU+D6uYb!)+YOHt|e|Gi8+ym{++Ca`M(le7uV7p++C*et&@7qy&7*SJ$BrIESiWJ zT5!M7V}%n99c{tRbEUUdSV~v!PR=@O401cw`fXUenVDi6mEpv6hMM=Y83n$+}4E;Q)8IplG70 z8Iw$+ZO|c>Sq@kBU-CN#U&0WFiegN!dePK5U^? zK3~Q7(BcWy>j8#3nrPw6z$NM|)1|B5sdu=z1Q*>x8CxkxY*`&`j0I`i4W0)PvJj5W z5qI516;M;I!E@=9;`vq{uERf<1>v@xwLn>U#o`#}AnVM=RscZVI{6YgNtY4;SX=*A zj4Fw$^F48`_qLquuJ*0Z$YRMF%)B)Z3V=W6zLS3*rCs~{@2zIv8lg*mgJ_|a2I{#i zCuPZkS@Z!QUb=NOomb=^-9B@rKnp3>Y@ZS3@L9K&<#nIlCE^xvcbKQL5Ul*_fZ!~cJ5y`+WA{yzmaEy z@)QC9Ni8`({-ZdCymk>N)B-;X>E~AJ0qWk;f+bm;h81p|AG{1(9AfS7iC3tTC@lDJ z*Js{$LIF1HYDi&;Lm$`1^NwaSC?&vQX~i$dOpVVcThL$Z1i|<303NK|$?kl3oA~1@-bgNa(R)(cbVaHRTiH_^{Yff7-tP^1N14EfV=-{$>EC;ouc4bZWLx z`h>VlAUye3yl))Dl=ZJ}^HEc$UX_fU1h$QS_DnwVDd)Lf$KJr(ra$Sk(vL4mAOV&P z3m%um%AIE3vzu5%#66dORbvoZG^3rYY>BGM^14!R$_MgR;CfngO(qbSnF3wvH=8}% zfRy{(CCrDf&+Cr|;AdY4s3WQ4Hwf$wfhway6#l;cvF#ZRcu-oTYfU+aj;nq&@>V>f zKsa#%9kXE8baeuYawRKt(D6Twjf1`}gGMuh{FHhW@(j~dQq?b|GSk)-xLmcV0j^mSv;>}X)5)-KuZ_ewDg120dW1i{c3rr*{yvJHE*K>IXmW?=#v8uK!N9Yu$TwxKY&3s1&=aHt<(IkT*_Yy%x4s)7*4!N zfJpxK%30SfduEcleSJLv?N%NE5x3E7a9ji#r<%-Eed#F;Qk|f)rC$Pl$9&=(g@qrT zOg00U28P|!jn+6#pD|M~^|x+gcDdTu&z{0wPy2laZArh;P3a?V+-NRA_d2*Dp3W)t ztlrk35aojMgL5mtWxDz#tI%2D?PBgN~O#Q7(Dn`joy- zeF1G%);HgK9opXJTtqxg`{WncHdpTb)&fS1)e!e?bpU|Tv8djT^6V?uJ0VTLF~ zL3Z}}n>A+9i+}yk@8}%jWCcr#E<5^ceN)yZO<7CU))*wKH5QJ31M@XHMUqr<-Fi)9 zi^HOhJ27iWcCpoW84|U&p$c4J@seW4#IIKW+_L*^h_e^uOn9}|V1 zc2D!e8j9_Xmv<4tSt>Rob8G!oaP7WNZP_k89z1&H((+>6kmoLv%ysoI0$*ik%)Q(c zq)fS8CBlh!8$TIdRivpJxSw#+0SsV%3KkP#)&Egz3pmG<^UkNhb}-);NngB~d>cJ5 z0Bzj!0RSOvXP=SW22eJ=``6bUWE1N)VOxj|1(lIm*CAedTUQTFt7 z#uVh0kYyF*OFsfwGvQ{dkV>ImUjTs2cSVURaic)!C9;gPgeo-ZLk|Y{Ptfz!4^0p> zAhg?Nj2{kz0Y-w=wL52-4-U!}nazGiQN#>8t zuskQ&e)J|uk=6>7MUx@#zFcfoG9z!Xq94!26;tGugcb@T(+7nASn{cT5ziK3q^|Ne zwf6K}QkCnT&&1o-sC1nz)ryhjf5Pz_{P!Lc_cQ#^IMahBevt!JjyDgE#ee(7t~jmw zP-|@w8Oy#)K`$V~J=EV4d zU=37<-sdAA@eA>3R50chX*|ruAklq&wY}3>I;y|zgpbcVXi{w5vL+0Lj|KSI#X^wm zV`a4{8=sJ-zc>>OE>#VCo~Bcyh^z{>TwrF`I?yJVc@B2WI0vw^R8O3z3E2GAdWA_P zqdx;M*)d><1?3`Lz&hiEl(OT+?Bo)t&Z)U*Z}Vd`N*Sf zE)ahxsMkB^M`nz-oD4_33fIOstBoB)+Q5VxZ%@+adR*kv&Tke^S|(q;`Q7CYww~0T zBqkm;i9h357%QW&(S1IgFMhR-W6z^4PgO08rcYo6(!~XO{Pv#x<+QMiDhHi?XD~Qh zuOE*(N(kTi)<^I9!Ux z6m+U{QOu_uDtOSIcnByPy09VNZw%I$|H@0q^T?b&PtQn3K6&~MBIP9i%Kwad zcWR?6TrV<$MkO=m-7S3fN^SUbm&~!VWz)t!#>uc3vA{G6XsIP}?&^}e4Kw}YOAs-FU3 zqw`SkpXD@bae|1dJe_BRHT`wzPFF=wp;TRd!`i}+#=eY4c|LB8C!^ixakb#Si}?*( ze$nzBO0mrWvtYHLHt@fXD1;vX{fVx5ZcK52t_!>7r@3tygegKt-qr6R z%h?y~J9dXEUBSxoBx<)U8oCUn5Tc#XA7=H&gvQDBS>0j15=zmW4`M5<|M<&Lxa1kX?dIoo5`m)hHkQ{l0t&z=Q48~O7B7k99pIuvx~X@S zNcSCqf`pgBM$6Dzn!DS#)G*9p9Ak~bfsJ8!x|52ua>9}K^|{9#th9oPlVt+G&A=&) z*2eT2;iHKxx!HneuL#vk>=plNkWY7zh=DwTKt%E_k}8=f_76S2x>ZJ+;zp-4J9Ur~ zUP4|oSStF|Gh^_t64T5~C9t0T` zJtG>WAAWfSp^f{$e=B|#sgfo~`3%si(8AfQ#UdHP3)2!p-%&no2dTI6$=zT-jn8## z?TCCZ!|pXtGAYIu9vQkUSQnlB3Gb-ts+I-+0r0age)lyZVA6y9BCH2TxLk=G#e>HC zHt^37sovF2C;L48oJ_ga$|8dP!eS1i^tNndUZQTCQVOD1-%rlvEPC&m`rjlC?JZkD z`Kn_L`J_%~Wr|m)3iq`Mk{DX;GuH-Q5l#usr8hY*!UeyPUtLqyM{caMnhco9F|K#S zon?JLIeAr#w~r?&r6d()%-s#A+0q8)tH~Qo2}-TA5}N&XDC6`L;ma4+k!q0AEwY4% zT1#HDa5p^@=P5=-oM}YSgzg5C$=@{&TOUfk>iZXIO?=1nmu2QwphrC{G_Ta6Isp#V z^UTgE7apdr?0}#6!fQy5``%J)TD$hzyJ!!k{qk!~?Q-?j(8jUnaQI5yL)+%ui#6NI zT!Y#Md~-xehf3p6iA#i^ooyhEtac*Gj?AImY)s%O{+E(ED$)tYmP}=*A7;Y(1^Vdh zbT(QDd7FMJ8HtJyf8!3RLxi0b!F$?uyoiA^)w?av!e3fGn6`wg#inBcx-EN?8p~{4 z87W&qg|*99Mz^?>K53cW@`8YN-9x@Q38g~3c2Q@=jOrcdzO~M#W5~K*K$Cejh15G_8BfTlW9}sF2HlS6kwyOqeCm)qNcPBWa#p`>8r1mGxn#YK(O@ zN;&v2)nQgN%|PI@C|f4cqvvSlPag(Sd;^_Ma`=m8QuCW&b1oh_guyjcxS~CzD@6BF zufEja%m-hTp|#l|+yH%I`3IcBeqE+AbvM@ttDvsB!lGOqpOoj#P2*=9JN~m}Lt)3l zT&x7{nctMS*f67S9u+#;QXAG*s4xImRAM@Qk()gF@?b*P$0FsFyZ#FUmvW~Lhqd^j z4jnK4`-|xV+W?{am+h}tPqXt5Q!J?T1JqD*B^Q2S{s<`FWho^HlrUf0V_(a^hKk7lI*)Pork&I0o_wyEEf2=^n&^pDb(kZfsu z_-MX$9*ZXQuUBTOHcT_L@Intel<|HC0mg7&$;h!^D4AIo;mICX{jGHKtq_FQpRV7K zkv9lRc`GB-k2KkCi{0X$Gjg1TomLqADhF>nkFv2plI*qwmQPn3aqP5^%$q#P%A$Qt zWh436&+BJ10Z9&&kb)Omc7|n$ORk3}$3xJMm5c|fFB_IyA^15!&a#Je0?eO#K%(x= z1YPmek;1+PW~2~xvf!`cbPsOxzl9#*)SPwGbKGU%;d1z+8=xUT6Vm1R-$T>ZzKCXK zDwkXd`&3rey4oEjIm!Q-p!C-j%l`tM)6-Z8{INgiCt5#0!Nd)3+8GJh_pD0S-E%>U z#L?o8AM!FBYu_~o;-~Aw)?*1s0>%B?P5Z}iTRAqrPr8HQu#$z@h(L#nD3Hlq73c)@eG}_mSB~yPn$!KmeRB(mK zWAva_s2g2`2j;9o^lzxWgz$S5(!GBu(zVj@I#Ja8o+v%taw&5ajT)PEnCGc6AIR4- zyx@WZbR((=CA6tFN1_v*n|A00DitP)PpNttqLGul+4^uZdblH>EmS%z9x1>7 zCmdc9jbD*}RRVPPq-A50e-K-?ZeXMkV*R?jsE#B$^`86z0iMLxcTff}V9`{~lO zAc@24#YJ3I>?PL|omXxgy}GZQ_%&znK1xS!uWdz4FEO3$CFurkw7)j{xq*_%#Wrba zhPp|SzQmbG$1TQL1O<50Ox5<}#Z?3@yq)EwDss@ zo&WMnErB|iV7&6?ZpFY|ej#ViGIOuDMbS8GuCW=KP{4JY?i`vtdDnYh49{5vH%6W6 zJ%kS^iL{9l?DXEY`UfkY=)Rjn+Zkz{U5Gdwg)~RPnSrY zZFO#$B^diw`3~f(&Jdkml4Na5{mc%vs@gD4$@->t))5(bE4^{*U2G1r-kCE}nICj- zcx{G&B)h(P9-PKx3a=N~Ts4w^y+2#x=h~~ICnqzOwMc0CwbuDka_85Nc}p!Nvfd?i5eR5!TeGvjq1bKJVNSQV;IScC%Cd9Nv#$3ar%j>v6c6m1tN5`Y z9^f~}$b*o#TYWEMl4o0t5kdxEPWlvexurM7-qzv5`#n;&m2;RlB-uR*W%=cROfkOUk0>hqc6`EIe!B%Ese>}5HF zU|@MJWy^otd&hw`IruQkVis$pO=N&pjyu{c5mX?t0LXQQR0I!713k_47Y}VTzCA_# z)>nc7puvd0{>*jHgKfdDZ98w<2`=1(H5iKS(70F@70l^p5!BCs3(ht?1lZ$DX0jfJ z-NQvc8vtR$#MYST?p{PCECWA0IMZ`Nan=7X$oAuZAe+tq^+}%gJF@DC3^p4{-oeEq z1lrnK%fsYZ<(WB(6QrUEP?L{X&m_svaArZRk`{8R_stWZJtYDcNWJ38&Ve}Ee zRE%F?_UG_mczBWxTi4K0_W90el8x-@&+gL6fyIUnMF z|Mqllpu8af*k)DwWs(hN90U4VWjxw7_=nx-`d--hBfU$qMjc}v@$;E}Fw9HNnQ)f( zFl{lJ@N=qcG!5t6sALatY1wNLya49s#2Qt9_7o4_q7 z1>}*0yT^2B!BAy}51ZebvL}2``lK+8EiW*kSGtt#k@VW#SK!HT83Ws`R(~nwx;<0O z4?bemtwZCzjhI&piTti2b6`=CH&^I1o#Yt$mXjSl zDdkaoJYb@J^9;%EmOr2u@Z0aLkS2}dT!gz9eEPA${w)y|$Qv}45k@qC2oevd=2g<* zwug(n`gQRx;9*YwHGCIm>dP~U*Qz z@D-ia?3%Y6qR(0u(csqU6Y(}qb1L=oM|!p1dq|Y9Zi=l~12Mo{9`yHdI>|~3#ObN< z6b9inr**Lt%Q}~lAGM?Rty&?yjD7q2JJAS{g|{G+fBE%G1nVkmIgh5Cr7QuTJsn3407Q`Z) zP52&D7E4dt7IqGe!n)30f2Ue_EJ`#383&_F)3l-`S?NJ+L~iP|x|nu>_>zLrTSsvs zqj0s9S=GC3;#socI$F?H##j@2%~b7iuGWnv;``&e62{3RY~?;A;9WoHmCd z#oyNiuEMHlY|1tHn*P-g3LdW}m^kgze;TSKJa9!_VHRF(Ea4lCF~OqGadFxVJI2L zF+7v_d7@>VTcr@m1dZHJrEQk1jgpDTu=w?O61Ojk{)a`fiDRvnkW~S%Z2}3QXNQ(N zlThzmVRn=gx={b)km8TZ8-%_!{x(~MvrlqJ-`M~^DKw%|Fo)Q1VCp268-DA|YKI3> zO*3-x9_SZ|USH6aqVW`9anc{5M-Pa&zcu0Y!&CNuIhKGXn^fS{RuBVbotN%wJNp9W z(V**8s?nZ^*k1fLAZmwC)Ck6rab9wNYB+n@e8z$4IxApZF2RZzs8Y``_mU}zYGtD8 zxCsH?XBa#^`9PhL6XMq`eS}3ZEO1wc6k*V_2Nc^-)60={GqEE&&pNm~( zXKx=^j~#1+Bd!Z}US{N&$3UkJD>U0?N&fX^D*^1}*1q$ebegnJsAS!GtcTfV5=Rr=wRRp++gv$-H>5w>ZPx-WTl00lzr=rU6iG5w`3UG16_BRRU&OD8 zE<&L$ieRAE5AH3@G%z;4<^l)7Ehkx_GfQaP6|VyI9jHq168*z_K2#-~<*p>^As*?< zy$yUK3uHXQJT4&as+Bi8jePYhaJvY6iW_}zkVT+wF5kvJIZuCYwEP3fiT8xwAIDO2 z+H09ZGMus1F|()2YofF*l(lq5Mon<^eNE?Yc{-0*y;$+s>OR7qff74BiBgIyJ0LJP zaxf5YVu+j^#|@vE8jA=J7$bO}7Gf9tXR{9LF;aQx+UepGq%Jf$M;Cx5jR1Eqy+^`G zPSG;!kDwjjx_sYpys+HF*8&`LE0cF_t(yP1Nh3Y=17K2fI` zUJ7@eP2$E>K84&4Ad~V`^DGF4?0`Z=G(C{AH7SC>1~RtXK5n=dF7JDxUL|JC5+17V3qn~xQfK_fDr>^kNnP@b0Wfh(>aMC?WDN-eF`)}Hh8}&=cKBT)*`mmaw8xX68Pwd&D7-~EpsYkJVBAOX z(fB#QLZ9G4ItneY?aJK~yO1XRaE8}egT6sOtz#s?dL!T9Um>A(aU}H?@?+azC7!@3 z`R5=MXOaB13Y~q-e{h%Wk5i(_4(;eieBuk|vII#X@?Bkiw$05k4Gc?vNjXcCgRu(* z2=HF7Bqq6C#nzxl)?rB!!OGZiykk?{pFHhm6ad-SXSO_uFGO}9=xSx!4TBRt3Pb+gJtXMR^? zld2!97~yKT4E|C&;WtA~tIw#PmSrY-9eCa+r;kP0e~8l!>wOp1xz@QieO=N501Nz? z)aDNfDWLCrAuacI$2hQIm+O&b>9k~UID?o z8&Tow7G>k9a zdnsClpev^|ui5=z%}4}@%ah~Bv7dnRI4FV`*(;FM4^{=?;eSOcuT6g`L3t_z_ucGA z>cVxGpH=&M_pY)W*YXdp`5X~i#dSKpJr8ich8h201!h+W8g(T7o4{S~y!obE{%pD? zVFy1M8nodS*=8Gl{fBg6sn7stUH`Q_I+Rhg7AeFQYSe5x%6!^4SM9E3dS8uBH1W;) zXjfoTj;&|T`qEUeUR#6U?Vu4E;(;Tyn+Q%KV(HnGRBdkw^*6aSD92KBZhoR-H{_Vo ztlN}pywmOCv6Z9NpIuw==(BO#@&vSTc*@%qDV2Vfg47w=I#!@m7C37sKml5aBPY9``#-F-7)Ag9 diff --git a/kicker/data/kickoff/kmenu_vertical.mng b/kicker/data/kickoff/kmenu_vertical.mng index 9cd3aac2b233e8d10d3d73b6abb1543697558e2d..7f7047d116668febb891da0c2e7bee1655861f87 100644 GIT binary patch literal 4131 zcmV+;5Zv#HP)!g9C&>fGiXnurq1G(57t&X-b+9yR?L68Vn@H8g{bR@ zPtud#d*64@=^yvKPtTU+bUMW8^vpMU=bq)Bd+xdCuA+|4Zv20THE$2cxD6737;8-w zIk2;w@Ca~t`iQ`Xt2Kt=|E|JHE`WDBN+yP#(*B+U02Cmg62Za6!wP)3S|4JChp2z| zp`MWlhhYSOh*Y)ljHSjJA|e72Q1Q1CAtDe6aS1X8#Dk?XxlZ>tiC zVJy~YdZoaTxd4jMhzT1x0iY@~&RhP8b5Hxsys4RVpa4LzhQ@!%gW$6$@7R|*`N3m|kO&RY=&;P|ud{>*tFzw(ln z*44YisO(_rcciF@KT?8%DvJVAK+1F>&!wcLg@4)n?@!yaFZ;=w2d*WkswgN%Mj|-| zYc#!5;KZwO{-BlaHxO9xPM#ydv5$t_Kr+R4a)J>dqFIJ^0RN z&uaSVyBoYYROHy`MvcW9O{f0>S0y5+HYgEoiAR)5Sy)R6)0;0(n|iR13IK!oun@Ua z!lVKdSnkXgCRHW8lL{ILMGfaeNgGNTVkQL=0Sj|g(hwb{%m2_oY*R^zi z2XbztRf>bXRRGys;hIl2iXs$ES$=SRcW*BAp$-KT$0g4@u`Uwr9&#PoaBEM23lS4> z$pDyO0=N*s2ol3stkLvJfg^JP1g0W~nb|uG5LuHQb{Oq&1u~QuC+Hh?PSs>+xZuK~ zDnS7X%(=(cZrh)a9MS>JZOm-!%(;A|3TAJBm^my80Bc5&xHV(3M$;<=j?5Ji%M}qG znZXHUOg1kE<*8OUtN=7DMg%ZJg@}+Zy1^l*Ktb_0pPsaLAXEht8tM{1-_)~z{}3^W z5|@`Q0D&!~ag3Br>0w!irN_wc;J$(s@&usNj@|Ce_+EewW*@tv6s2kSG~0Em22E3RQMOfGKBwA(ZTJG-rCd;~0Ad25rMN?-)S4YTcC3HnH|=fhg+gKc`0=x5&6+!RUJwL_RjJgC z@y(kzb#-;km@#u|)6|$NA_*e^ijEUTEPx;|emP^yxlrSbJ?A5-OHMt;FkLlg;_Svu z>l;1C)C9XbvjCV00s<96smqwpoiTZTE@GmDrK$wq{>0AwU{OFqBp?SECO@Quz=%Rb z05G#hUVrViZ~w#JZ{D=&kob%rKYrn&8y78FI9eq}Dl1&JZ0VD$R(=0p?)cI**Tmc~ zEG;_TZEO5+Cjexu&4=Y32DEo5B7y=3dizdmte-JCLxdY2$d)2Z1cD*=wey+=i=sfl zG@;7g@zlLy*JZ6_g+_iHanSmn8zgfyqVXSxT{Xci|Je20s4L+GEDm z_xAPG)YiSct$!%*05FlVGH}EBA5ACiKtT*seVQLx-?jeT0}OS5fE~dpWqXE>t+q;~ zH0Fw=+q!k@lEsUibGP1l>&=T7$Mmz#ns?o`*REZ=_UlWQ9HKHBVy^!sI#j-L`)d83 z(;ooXnu4Q>O(p$w-TqmBQk}4zs;d3xXFAvI$W=oKFboJ0VSG)pZzy6y($e5?^uSA< z8VN+9>L8JGg9q~Yf=krZmJb{3L<}l%rbVFaQw&0fZnhE+SQ^DwF{1 z>~*{L4o#m@b;q-vYjzAn;8^1b6DVJZl9n_=B&e$j&Odd+s&(BYiZF_Dg~Cv%AVVA& z_9mp(6^2t>IZdka;HzQ`nNIISNbm!cng*U8Nu>wFtL&Lzpz%QSD_MUrw^2no) zop|DjD7$)yx`;4yWx&~!{BTPJh5_F+1v#bT>v{T=s$ad=_saIX6F7l$C~cUC0A2Rv zHWjfjL25O)|Iq8|%iTI|xzir#_@NgoTJpJ_3eKTTK zjKuV%OMkp##foXuj$ga(wJmSGz4iTVU-`;cJ3BkSaK#nF`MekQtFDU1>OP+hQT%ba zZvdiH!UTjsqX}F%ZGyXEX5!{gA4^0Jzu6Zli?H}J5n!17yt{jEc+a!Ds}k%NDyMYo zf{!H<3?^bS0IRZUqBLujy5Bcw-`>4GTz>7TWW|VwC=8b^T}nj%^|PN%Z*B&VPNn|o zw%caUKIQ%Q-+TJ0C%v$>7JxCPQoR)9GE@v{Af4cp<;-giip;0&NpYslXph=`w5R8c78 z0c6tYxMf6m?9oSBTU(7W%uFC=`q{n9H*9#Ludi?R>{DjVnh8J^s$vaae);8_H*Z?K zdiDJI^Hot(T@9eRx>{Ad390C{ksS6BR=^s{Skxqqfcu6b!xR|w4~49pG8{q$fHHss zy?x`y)%y(@8~cNYcipq7##$4J4ux{z8B;cF?Rjflwzu-^{J+&7CV7C=o+O)OR#IELaKwBKGU z0SZ_`b-+>RENNZJBn=T@4J9p70VORN!*NafdwP8$fO$_p?pn1o6)+PqVK|g;EI4Vx ze8Cf|t82X5v6zU8lzN(oh=`-eiHLW@7^+Ti`=I?!>#qA=XkY*C zK8@VC>YzTQ2}=n}2}6GVD`|OPX6ENM_P_q_zLX^A^~`pCXx*Mn(#~#3BrPQ^B?Ail z##Kdi#9=VAHFrMVJ}?vp2L2-O)M?```m-h_B7!Qs!(K7|F1>UCfPekL9l6}#AuLsK zF7mc!ojeP`uYdh|u~?`ScxBDY0A|fP*%yPr*4EZA)4`QE!{k3^=5o-XfMrSrlnyu@ zP%5BQz}xmk{kh^rr;kq}Oj=49vV?p-EV$(MZnxsaw(e{MVlX#O&ivJ<8kar3J!v`H zpI^1EW8u;bPrcmfXY*3$5Ttxm@W^#v{&GV@Lt9(hb=O^+&GuKaVi3`$rlvE_JhP{# z=kB}zBNo`bd-pGX{&QxYf5|1Dc;E8nOBUa}cI~=Y(MvM1{|u2Wjj_^#fpmhM(?}>1 zHLOAyE<{r%Wd8cps+Aju)!HFWTzS{~ISnGRDoUCmb~S1Hw---sZphrSqyLKU{ibVQ zQJs==Z~|hGh?ee(MMRWUA|e*$WHNc*{SRFB`Om+&di7cJ=KRHl7d19E3=ZbHKIq!C zX`^pj6h&<8cfNDmCG#)--tD(<-?sfz=bqcsbKu{W{WzD)ef4YCAAiF1Fbs+Kl{IUg ze(K4YvrhigIdi=U9DE4}z^V#}iJ(x!6fikMk<&;Zk^9sM$#f>!Gvv-Wd14~yS-HMn z(y2m_OhuXxC5i;9QzzPQeD=6nWcPGuZ~AQ0e?D)!d%B?xN|4fRwI~!sL<&Gk1J%#^ z)2B~=?)m3`^ur%M{P08f-*<1^vP>p3fByV0UUfAwt3)j=Ef212{nod>+1k3YwRI(c zR4R4z;>9=JyhIPK5UL{-VgRo4ihJj-b^voPyZ_s_ETHPzs)UI|Bd4K*%O1G;f{*kJ z3lWl*CR7F4!NQAg9oW>7@5(w+OsNmfZm$01N%ecXyW$!rH8fm**E>6R^+O^=qRzp& z#-?$nojK+HdsjaG{RJ2UipApAt-tN=?pD=?hKAjYQaqe|dmvDTgCL$PjTSH6$aDsMe=!m@hv5>YmxtYA>EU`Qo`Gssv(& zbNXKA;FjOni%y@or>g@%I^c`uOSl!>-QX9p-4KpeSgkH;uNgt>CbQezzH!(+xEF7 z%iGSKQN8}XY+p76;>RceB;|FuWbWv~u#8*@TEB?V=)pUqSSPU~xe3(uN#OY7U?5qfh+8Q4VtU{H)eFRfYYsaO8v zx{1v*&OdW%b=sC5hiPP+Qu$(BeusvCkF0z>K2!!!P=7x9<;Kp;-Yq}A?uc{9Ar`Ry+9r0OBc$hybp@V*$XQt}I1= zcg5U9!p61|32TGEBfeQAS&@Jz=E00IpG&{NO6aMXMJXv3>}nxd$5nPmYK z!lExEgM`PEL1HZWA;^cN_2D*SG}l^*A`YPRz>0@HoNoL-4DS?Xk3_in#N+?Ct@%Sk hXLiSh6YJvQe*^p!^Tu|@t`h(N002ovPDHLkV1gC`@&Nz< literal 62006 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 diff --git a/kicker/data/kmenu_side/Makefile.am b/kicker/data/kmenu_side/Makefile.am index ee19c9a20..2d3e00932 100644 --- a/kicker/data/kmenu_side/Makefile.am +++ b/kicker/data/kmenu_side/Makefile.am @@ -1,6 +1,5 @@ -kicker_kmenuside_pics_data_DATA = kside.png kside_tile.png - -kicker_kmenuside_pics_datadir = $(kde_datadir)/kicker/pics/ +kicker_kmenuside_pics_data_DATA = kside.png kside_tile.png +kicker_kmenuside_pics_datadir = $(kde_datadir)/kicker/pics EXTRA_DIST = $(kicker_kmenuside_pics_data_DATA) diff --git a/kicker/extensions/kasbar/kasbar.cpp b/kicker/extensions/kasbar/kasbar.cpp index 06bf3c24f..bf2b64ba3 100644 --- a/kicker/extensions/kasbar/kasbar.cpp +++ b/kicker/extensions/kasbar/kasbar.cpp @@ -719,7 +719,7 @@ void KasBar::addTestItems() i->setText( "Animated" ); i->setIcon( KGlobal::iconLoader()->loadIcon( "icons", KIcon::NoGroup, KIcon::SizeMedium ) ); i->setAnimation( resources()->startupAnimation() ); - TQTimer *aniTimer = new TQTimer( i ); + TQTimer *aniTimer = new TQTimer( i, "aniTimer" ); connect( aniTimer, TQT_SIGNAL( timeout() ), i, TQT_SLOT( advanceAnimation() ) ); aniTimer->start( 100 ); i->setShowAnimation( true ); diff --git a/kicker/extensions/kasbar/kasclockitem.cpp b/kicker/extensions/kasbar/kasclockitem.cpp index 386a7922d..f025af857 100644 --- a/kicker/extensions/kasbar/kasclockitem.cpp +++ b/kicker/extensions/kasbar/kasclockitem.cpp @@ -38,7 +38,7 @@ KasClockItem::KasClockItem( KasBar *parent ) { setCustomPopup( true ); - TQTimer *t = new TQTimer( this ); + TQTimer *t = new TQTimer( this, "t" ); connect( t, TQT_SIGNAL( timeout() ), TQT_SLOT( updateTime() ) ); t->start( 1000 ); diff --git a/kicker/extensions/kasbar/kasloaditem.cpp b/kicker/extensions/kasbar/kasloaditem.cpp index 2d33a068d..7b6939760 100644 --- a/kicker/extensions/kasbar/kasloaditem.cpp +++ b/kicker/extensions/kasbar/kasloaditem.cpp @@ -33,7 +33,7 @@ KasLoadItem::KasLoadItem( KasBar *parent ) : KasItem( parent ) { - TQTimer *t = new TQTimer( this ); + TQTimer *t = new TQTimer( this, "KasLoadItem::t" ); connect( t, TQT_SIGNAL( timeout() ), TQT_SLOT( updateDisplay() ) ); t->start( 1000 ); updateDisplay(); diff --git a/kicker/extensions/kasbar/kasstartupitem.cpp b/kicker/extensions/kasbar/kasstartupitem.cpp index 53d823430..4d2ac3b00 100644 --- a/kicker/extensions/kasbar/kasstartupitem.cpp +++ b/kicker/extensions/kasbar/kasstartupitem.cpp @@ -79,7 +79,7 @@ KasStartupItem::KasStartupItem( KasBar *parent, Startup::Ptr startup ) setShowFrame( false ); setAnimation( resources()->startupAnimation() ); - aniTimer = new TQTimer( this ); + aniTimer = new TQTimer( this, "aniTimer" ); connect( aniTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( aniTimerFired() ) ); aniTimer->start( 100 ); } diff --git a/kicker/kicker/Makefile.am b/kicker/kicker/Makefile.am index 857ac9cff..9a89a0ac0 100644 --- a/kicker/kicker/Makefile.am +++ b/kicker/kicker/Makefile.am @@ -1,6 +1,7 @@ INCLUDES = $(all_includes) -SUBDIRS = core ui buttons . +# SUBDIRS = core interfaces ui buttons plugins . +SUBDIRS = core interfaces ui buttons . bin_PROGRAMS = lib_LTLIBRARIES = @@ -9,7 +10,7 @@ kdeinit_LTLIBRARIES = kicker.la CLEANFILES = dummy.cpp kicker_la_LIBADD = core/libkicker_core.la buttons/libkicker_buttons.la \ - ui/libkicker_ui.la ../libkicker/libkickermain.la $(LIB_KIO) $(LIB_KUTILS) + ui/libkicker_ui.la ../libkicker/libkickermain.la $(LIB_KIO) $(LIB_KUTILS) $(LIB_KABC) kicker_la_SOURCES = dummy.cpp kicker_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) diff --git a/kicker/kicker/buttons/Makefile.am b/kicker/kicker/buttons/Makefile.am index 2ba4d20c6..1ec3d42d6 100644 --- a/kicker/kicker/buttons/Makefile.am +++ b/kicker/kicker/buttons/Makefile.am @@ -1,10 +1,10 @@ INCLUDES = -I$(srcdir)/../core -I$(srcdir)/../../libkicker -I../../libkicker \ - -I$(srcdir)/../ui -I$(top_srcdir)/libkonq $(all_includes) + -I../ui -I$(srcdir)/../ui -I$(top_srcdir)/libkonq $(all_includes) $(LIBBEAGLE_CFLAGS) $(GLIB_CFLAGS) noinst_LTLIBRARIES = libkicker_buttons.la libkicker_buttons_la_SOURCES = servicebutton.cpp bookmarksbutton.cpp \ - browserbutton.cpp \ + browserbutton.cpp knewbutton.cpp \ desktopbutton.cpp extensionbutton.cpp kbutton.cpp \ nonkdeappbutton.cpp servicemenubutton.cpp urlbutton.cpp \ windowlistbutton.cpp diff --git a/kicker/kicker/buttons/browserbutton.cpp b/kicker/kicker/buttons/browserbutton.cpp index 950882696..2e2cb5a2d 100644 --- a/kicker/kicker/buttons/browserbutton.cpp +++ b/kicker/kicker/buttons/browserbutton.cpp @@ -65,7 +65,7 @@ void BrowserButton::initialize( const TQString& icon, const TQString& path ) topMenu = new PanelBrowserMenu( path ); setPopup(topMenu); - _menuTimer = new TQTimer( this ); + _menuTimer = new TQTimer( this, "_menuTimer" ); connect( _menuTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotDelayedPopup()) ); TQToolTip::add(this, i18n("Browse: %1").arg(path)); diff --git a/kicker/kicker/buttons/kbutton.cpp b/kicker/kicker/buttons/kbutton.cpp index 73454b99f..d39346edb 100644 --- a/kicker/kicker/buttons/kbutton.cpp +++ b/kicker/kicker/buttons/kbutton.cpp @@ -33,6 +33,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "menumanager.h" #include "k_mnu.h" +#include "k_mnu_stub.h" #include "kbutton.h" #include "kbutton.moc" @@ -43,7 +44,7 @@ KButton::KButton( TQWidget* parent ) TQToolTip::add(this, i18n("Applications, tasks and desktop sessions")); setTitle(i18n("K Menu")); - setPopup(MenuManager::the()->kmenu()); + setPopup(MenuManager::the()->kmenu()->widget()); MenuManager::the()->registerKButton(this); setIcon("kmenu"); diff --git a/kicker/kicker/buttons/knewbutton.cpp b/kicker/kicker/buttons/knewbutton.cpp new file mode 100644 index 000000000..9c9f438ac --- /dev/null +++ b/kicker/kicker/buttons/knewbutton.cpp @@ -0,0 +1,455 @@ +/***************************************************************** + +Copyright (c) 2006 Stephan Binner + Stephan Kulow + Dirk Mueller + +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 +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "kickerSettings.h" + +#include "config.h" +#include "global.h" + +#include "menumanager.h" +#include "k_mnu_stub.h" +#include "k_new_mnu.h" + +#include "knewbutton.h" +#include "knewbutton.moc" + +KNewButton *KNewButton::m_self = 0; + +KNewButton::KNewButton( TQWidget* parent ) + : KButton( parent ), + m_oldPos(0,0) +{ + Q_ASSERT( !m_self ); + m_self = this; + m_hoverTimer = -1; + m_openTimer = -1; + m_active = false; + m_mouseInside = false; + m_drag = false; + + setIconAlignment((Qt::AlignmentFlags)(AlignTop|AlignRight)); + setAcceptDrops(true); + setIcon("kmenu-suse"); + setDrawArrow(false); + + m_movie = new TQMovie(locate("data", "kicker/pics/kmenu_basic.mng")); + m_movie->connectUpdate(this, TQT_SLOT(updateMovie())); + m_movie->connectStatus(this, TQT_SLOT(slotStatus(int))); + m_movie->connectResize(this, TQT_SLOT(slotSetSize(const TQSize&))); + + TQApplication::desktop()->screen()->installEventFilter(this); + setMouseTracking(true); +} + +KNewButton::~KNewButton() +{ + if ( m_self == this ) + m_self = 0; + setMouseTracking(false); + delete m_movie; +} + +void KNewButton::slotStatus(int status) +{ + if(status == TQMovie::EndOfLoop) + slotStopAnimation(); +} + +TQColor KNewButton::borderColor() const +{ + TQImage img = m_active_pixmap.convertToImage(); + + for (int i = 0; i < img.width(); ++i) { + QRgb rgb = img.pixel(orientation() == Qt::Horizontal ? img.width() - i - 1 : + i, 2); + + if (qGreen(rgb) > 0x50) + return rgb; + } + + return img.pixel( orientation() == Qt::Horizontal ? img.width() - 2 : 2, 2); +} + +void KNewButton::show() +{ + KButton::show(); + + if (KickerSettings::firstRun()) { + TQTimer::singleShot(500,this,TQT_SLOT(slotExecMenu())); + KickerSettings::setFirstRun(false); + KickerSettings::writeConfig(); + } +} + +void KNewButton::updateMovie() +{ + m_oldPos = TQPoint( -1, -1 ); + drawEye(); + + if (!m_active && m_movie->running()) + m_movie->pause(); +} + +void KNewButton::setPopupDirection(KPanelApplet::Direction d) +{ + KButton::setPopupDirection(d); + + delete m_movie; + + switch (d) { + case KPanelApplet::Left: + setIconAlignment((Qt::AlignmentFlags)(AlignTop|AlignLeft)); + m_movie = new TQMovie(locate("data", "kicker/pics/kmenu_vertical.mng")); + break; + case KPanelApplet::Right: + setIconAlignment((Qt::AlignmentFlags)(AlignTop|AlignRight)); + m_movie = new TQMovie(locate("data", "kicker/pics/kmenu_vertical.mng")); + break; + case KPanelApplet::Up: + setIconAlignment((Qt::AlignmentFlags)(AlignTop|AlignHCenter)); + m_movie = new TQMovie(locate("data", "kicker/pics/kmenu_basic.mng")); + break; + case KPanelApplet::Down: + setIconAlignment((Qt::AlignmentFlags)(AlignBottom|AlignHCenter)); + m_movie = new TQMovie(locate("data", "kicker/pics/kmenu_flipped.mng")); + } + + m_movie->connectUpdate(this, TQT_SLOT(updateMovie())); + m_movie->connectStatus(this, TQT_SLOT(slotStatus(int))); + m_movie->connectResize(this, TQT_SLOT(slotSetSize(const TQSize&))); +} + +void KNewButton::slotSetSize(const TQSize& s) +{ + m_iconSize = s; +} + +double KNewButton::buttonScaleFactor(const TQSize& s) const +{ + double sf = 1.0; + + switch (popupDirection()) { + case KPanelApplet::Left: + case KPanelApplet::Right: +// sf = kMin(double(s.width()) / m_iconSize.height(), double(s.height()) / m_iconSize.width()); +// break; + case KPanelApplet::Up: + case KPanelApplet::Down: + sf = kMin(double(s.width()) / m_iconSize.width(), double(s.height()) / m_iconSize.height()); + break; + } + + if (sf > 0.8) sf = 1.0; + return sf; +} + +int KNewButton::widthForHeight(int height) const +{ + int r = m_iconSize.width() * buttonScaleFactor(TQSize(m_iconSize.width(), height)); + + if (!m_movie->running() && height != m_active_pixmap.height()) + { + KNewButton* that = const_cast(this); + TQTimer::singleShot(0, that, TQT_SLOT(slotStopAnimation())); + } + + return r; +} + +int KNewButton::preferredDimension(int panelDim) const +{ + return kMax(m_icon.width(), m_icon.height()); +} + +int KNewButton::heightForWidth(int width) const +{ + int r = m_iconSize.width() * buttonScaleFactor(TQSize(width, m_iconSize.height())); + if (!m_movie->running() && width != m_active_pixmap.width()) + { + KNewButton* that = const_cast(this); + TQTimer::singleShot(0, that, TQT_SLOT(slotStopAnimation())); + } + return r; +} + +bool KNewButton::eventFilter(TQObject *o, TQEvent *e) +{ + if (e->type() == TQEvent::MouseButtonRelease || + e->type() == TQEvent::MouseButtonPress || + e->type() == TQEvent::MouseButtonDblClick ) + { + TQMouseEvent *me = static_cast(e); + if (rect().contains(mapFromGlobal(me->globalPos()))) + { + if (m_pressedDuringPopup && m_popup && m_openTimer != -1 + && (me->button() & Qt::LeftButton) ) + return true; + } + } + + if (KickerSettings::kickoffDrawGeekoEye() && e->type() == TQEvent::MouseMove) + { + TQMouseEvent *me = static_cast(e); + if ((me->state() & MouseButtonMask) == NoButton) + drawEye(); + } + + return KButton::eventFilter(o, e); +} + +void KNewButton::drawEye() +{ +#define eye_x 62 +#define eye_y 13 + TQPoint mouse = TQCursor::pos(); + TQPoint me = mapToGlobal(TQPoint(eye_x, eye_y)); + double a = atan2(mouse.y() - me.y(), mouse.x() - me.x()); + int dx = int(2.1 * cos(a)); + int dy = int(2.1 * sin(a)); + + TQPoint newpos(eye_x+dx,eye_y+dy); + if (newpos!=m_oldPos) { + m_oldPos = newpos; + TQPixmap pixmap = m_active_pixmap; + + double sf = 1.0; + + if(!m_movie->framePixmap().isNull()) + { + pixmap = m_movie->framePixmap(); + pixmap.detach(); + m_iconSize = pixmap.size(); + sf = buttonScaleFactor(size()); + + if (KickerSettings::kickoffDrawGeekoEye()) { + TQPainter p(&pixmap); + p.setPen(white); + p.setBrush(white); + // p.setPen(TQColor(110,185,55)); + p.drawRect(eye_x+dx, eye_y+dy, 2, 2); + p. end(); + } + } + + TQWMatrix matrix; + switch (popupDirection()) { + case KPanelApplet::Left: + matrix.scale(sf, -sf); + matrix.rotate(90); + break; + case KPanelApplet::Up: + matrix.scale(sf, sf); + break; + case KPanelApplet::Right: + matrix.scale(sf, -sf); + matrix.rotate(90); + break; + case KPanelApplet::Down: + matrix.scale(sf, sf); + break; + } + m_active_pixmap = pixmap.xForm(matrix); + + repaint(false); + } +#undef eye_x +#undef eye_y +} + +void KNewButton::enterEvent(TQEvent* e) +{ + KButton::enterEvent(e); + + TQSize s(size()); + s *= 0.25; + s = s.expandedTo(TQSize(6,6)); + + switch (popupDirection()) { + case KPanelApplet::Left: + m_sloppyRegion = TQRect(rect().topRight() - TQPoint(s.width()-1, 0), s); + break; + case KPanelApplet::Right: + m_sloppyRegion = TQRect(rect().topLeft(), s); + break; + case KPanelApplet::Up: + m_sloppyRegion = TQRect(rect().bottomLeft() - TQPoint(0, s.height()-1), s); + break; + case KPanelApplet::Down: + m_sloppyRegion = TQRect(rect().topLeft(), s); + } + + m_active = true; + m_movie->unpause(); + m_movie->restart(); +} + +void KNewButton::rewindMovie() +{ + m_oldPos = TQPoint( -1, -1 ); + m_movie->unpause(); +} + +void KNewButton::dragEnterEvent(TQDragEnterEvent* /*e*/) +{ + if (m_hoverTimer != -1) + killTimer(m_hoverTimer); + + m_hoverTimer = startTimer(TQApplication::startDragTime()); + m_mouseInside = true; + m_drag = true; +} + +void KNewButton::dragLeaveEvent(TQDragLeaveEvent* /*e*/) +{ + m_mouseInside = false; + m_drag = false; +} + +void KNewButton::leaveEvent(TQEvent* e) +{ + m_mouseInside = false; + if (m_hoverTimer != -1) + killTimer(m_hoverTimer); + m_hoverTimer = -1; + + KButton::leaveEvent(e); +} + +void KNewButton::mouseMoveEvent(TQMouseEvent* e) +{ + KButton::mouseMoveEvent(e); + + m_mouseInside = m_sloppyRegion.contains(e->pos()); + + if ( m_sloppyRegion.contains(e->pos())) + { + if (m_hoverTimer == -1 && KickerSettings::openOnHover()) + m_hoverTimer = startTimer(kMax(200,TQApplication::doubleClickInterval()/2)); + } + else if (m_hoverTimer != -1) + { + killTimer(m_hoverTimer); + m_hoverTimer = -1; + } +} + +void KNewButton::slotStopAnimation() +{ + m_active = false; + m_movie->pause(); + m_movie->restart(); + TQTimer::singleShot(200, this, TQT_SLOT(rewindMovie())); +} + +const TQPixmap& KNewButton::labelIcon() const +{ + return m_active_pixmap; +} + +void KNewButton::slotExecMenu() +{ + if (m_openTimer != -1) + killTimer(m_openTimer); + + m_openTimer = startTimer(TQApplication::doubleClickInterval() * 3); + + if (m_active) + { + m_active = false; + m_movie->pause(); + m_movie->restart(); + } + + KButton::slotExecMenu(); + + assert(!KickerTip::tippingEnabled()); + assert(dynamic_cast(m_popup)); + + disconnect(dynamic_cast(m_popup), TQT_SIGNAL(aboutToHide()), this, + TQT_SLOT(slotStopAnimation())); + connect(dynamic_cast(m_popup), TQT_SIGNAL(aboutToHide()), + TQT_SLOT(slotStopAnimation())); + + m_popup->move(KickerLib::popupPosition(popupDirection(), m_popup, this)); + // I wish KMenu would properly done itself when it closes. But it doesn't. + + bool useEffect = true; // could be TQApplication::isEffectEnabled() + useEffect = false; // too many TQt bugs to be useful + if (m_drag) + useEffect = false; + + m_drag = false; // once is enough + + if (useEffect) + { + switch (popupDirection()) { + case KPanelApplet::Left: + qScrollEffect(m_popup, QEffects::LeftScroll); + break; + case KPanelApplet::Up: + qScrollEffect(m_popup, QEffects::UpScroll); + break; + case KPanelApplet::Right: + qScrollEffect(m_popup, QEffects::RightScroll); + break; + case KPanelApplet::Down: + qScrollEffect(m_popup, QEffects::DownScroll); + break; + } + } + else + static_cast(m_popup)->show(); +} + +void KNewButton::timerEvent(TQTimerEvent* e) +{ + if (e->timerId() == m_hoverTimer) + { + if (m_mouseInside && !isDown()) + showMenu(); + + killTimer(m_hoverTimer); + m_hoverTimer = -1; + } + if (e->timerId() == m_openTimer) + { + killTimer(m_openTimer); + m_openTimer = -1; + } +} diff --git a/kicker/kicker/buttons/knewbutton.h b/kicker/kicker/buttons/knewbutton.h new file mode 100644 index 000000000..bcb8b8743 --- /dev/null +++ b/kicker/kicker/buttons/knewbutton.h @@ -0,0 +1,98 @@ +/***************************************************************** + +Copyright (c) 2006 Stephan Binner + Stephan Kulow + Dirk Mueller + +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 __knewbutton_h__ +#define __knewbutton_h__ + +#include "kbutton.h" + +#include +#include + +/** + * Button that contains the PanelKMenu and client menu manager. + */ +class KNewButton : public KButton +{ + Q_OBJECT + +public: + KNewButton( TQWidget *parent ); + ~KNewButton(); + + static KNewButton *self() { return m_self; } + + void loadConfig( const KConfigGroup& config ); + + virtual const TQPixmap& labelIcon() const; + + virtual int widthForHeight(int height) const; + virtual int preferredDimension(int panelDim) const; + virtual int heightForWidth(int width) const; + + TQColor borderColor() const; + + virtual void setPopupDirection(KPanelApplet::Direction d); + +private slots: + void slotStatus(int); + void slotSetSize(const TQSize&); + void slotStopAnimation(); + void rewindMovie(); + void updateMovie(); + +protected: + virtual void show(); + virtual void slotExecMenu(); + virtual TQString tileName() { return "KMenu"; } + virtual TQString defaultIcon() const { return "go"; } + + virtual void enterEvent(TQEvent* e); + virtual void leaveEvent(TQEvent* e); + virtual void mouseMoveEvent(TQMouseEvent* e); + virtual void dragEnterEvent(TQDragEnterEvent*); + virtual void dragLeaveEvent(TQDragLeaveEvent*); + virtual bool eventFilter(TQObject *, TQEvent *); + void timerEvent(TQTimerEvent*); + +private: + void drawEye(); + double buttonScaleFactor(const TQSize& s) const; + + TQMovie* m_movie; + TQPixmap m_active_pixmap; + TQPoint m_oldPos; + TQSize m_iconSize; + TQRect m_sloppyRegion; + int m_hoverTimer; + int m_openTimer; + bool m_active; + bool m_mouseInside; + bool m_drag; + + static KNewButton *m_self; +}; + +#endif diff --git a/kicker/kicker/core/Makefile.am b/kicker/kicker/core/Makefile.am index 6986af604..25e7e733a 100644 --- a/kicker/kicker/core/Makefile.am +++ b/kicker/kicker/core/Makefile.am @@ -1,12 +1,14 @@ INCLUDES = -I$(srcdir)/../../libkicker -I../../libkicker \ - -I$(srcdir)/../ui -I$(srcdir)/../buttons -I$(top_srcdir)/libkonq \ - $(all_includes) + -I../ui -I$(srcdir)/../ui -I$(srcdir)/../buttons -I$(top_srcdir)/libkonq \ + $(all_includes) $(LIBBEAGLE_CFLAGS) $(GLIB_CFLAGS) noinst_LTLIBRARIES = libkicker_core.la +libkicker_core_la_COMPILE_FIRST = kmenubase.h + libkicker_core_la_SOURCES = extensionSettings.kcfgc \ main.cpp kicker.cpp kicker.skel \ - userrectsel.cpp containerarea.cpp \ + userrectsel.cpp containerarea.cpp kmenubase.ui \ applethandle.cpp container_base.cpp container_button.cpp \ container_applet.cpp container_extension.cpp extensionmanager.cpp \ menumanager.cpp pluginmanager.cpp showdesktop.cpp \ diff --git a/kicker/kicker/core/applethandle.cpp b/kicker/kicker/core/applethandle.cpp index 251cbc1bd..f5f398136 100644 --- a/kicker/kicker/core/applethandle.cpp +++ b/kicker/kicker/core/applethandle.cpp @@ -150,7 +150,7 @@ void AppletHandle::setFadeOutHandle(bool fadeOut) { if (!m_handleHoverTimer) { - m_handleHoverTimer = new TQTimer(this); + m_handleHoverTimer = new TQTimer(this, "m_handleHoverTimer"); connect(m_handleHoverTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(checkHandleHover())); m_applet->installEventFilter(this); @@ -177,11 +177,7 @@ bool AppletHandle::eventFilter(TQObject *o, TQEvent *e) m_drawHandle = true; resetLayout(); - if (m_handleHoverTimer) - { - m_handleHoverTimer->start(250); - } - break; + break; } case TQEvent::Leave: @@ -191,6 +187,11 @@ bool AppletHandle::eventFilter(TQObject *o, TQEvent *e) break; } + if (m_handleHoverTimer) + { + m_handleHoverTimer->start(250); + } + TQWidget* w = dynamic_cast(o); bool nowDrawIt = false; @@ -207,11 +208,6 @@ bool AppletHandle::eventFilter(TQObject *o, TQEvent *e) if (nowDrawIt != m_drawHandle) { - if (m_handleHoverTimer) - { - m_handleHoverTimer->stop(); - } - m_drawHandle = nowDrawIt; resetLayout(); } @@ -297,6 +293,11 @@ void AppletHandle::toggleMenuButtonOff() } m_menuButton->setDown(false); + + if (m_handleHoverTimer) + { + m_handleHoverTimer->start(250); + } } AppletHandleDrag::AppletHandleDrag(AppletHandle* parent) diff --git a/kicker/kicker/core/container_button.cpp b/kicker/kicker/core/container_button.cpp index 8ac39a47f..bb0249843 100644 --- a/kicker/kicker/core/container_button.cpp +++ b/kicker/kicker/core/container_button.cpp @@ -43,6 +43,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "desktopbutton.h" #include "extensionbutton.h" #include "kbutton.h" +#include "knewbutton.h" #include "kicker.h" #include "kickerSettings.h" #include "kickertip.h" @@ -326,14 +327,20 @@ KMenuButtonContainer::KMenuButtonContainer(const KConfigGroup& config, TQPopupMe : ButtonContainer(opMenu, parent) { checkImmutability(config); - embedButton( new KButton(this) ); + if(KickerSettings::legacyKMenu()) + embedButton( new KButton(this) ); + else + embedButton( new KNewButton(this) ); _actions = PanelAppletOpMenu::KMenuEditor; } KMenuButtonContainer::KMenuButtonContainer(TQPopupMenu *opMenu, TQWidget* parent) : ButtonContainer(opMenu, parent) { - embedButton( new KButton(this) ); + if(KickerSettings::legacyKMenu()) + embedButton( new KButton(this) ); + else + embedButton( new KNewButton(this) ); _actions = PanelAppletOpMenu::KMenuEditor; } diff --git a/kicker/kicker/core/containerarea.cpp b/kicker/kicker/core/containerarea.cpp index 7d63d562a..1bd80bc65 100644 --- a/kicker/kicker/core/containerarea.cpp +++ b/kicker/kicker/core/containerarea.cpp @@ -87,7 +87,8 @@ ContainerArea::ContainerArea(KConfig* _c, m_immutable(_c->isImmutable()), m_updateBackgroundsCalled(false), m_layout(0), - m_addAppletDialog(0) + m_addAppletDialog(0), + _autoScrollTimer(0, "ContainerArea::autoScrollTimer") { setBackgroundOrigin( WidgetOrigin ); diff --git a/kicker/kicker/core/kicker.cpp b/kicker/kicker/core/kicker.cpp index de37c609e..d91e1eaf9 100644 --- a/kicker/kicker/core/kicker.cpp +++ b/kicker/kicker/core/kicker.cpp @@ -48,6 +48,8 @@ #include "extensionmanager.h" #include "pluginmanager.h" #include "menumanager.h" +#include "k_new_mnu.h" +#include "k_mnu_stub.h" #include "k_mnu.h" #include "showdesktop.h" #include "panelbutton.h" @@ -106,6 +108,7 @@ Kicker::Kicker() KGlobal::iconLoader()->addExtraDesktopThemes(); + KGlobal::locale()->insertCatalogue("kdmgreet"); KGlobal::locale()->insertCatalogue("libkonq"); KGlobal::locale()->insertCatalogue("libdmctl"); KGlobal::locale()->insertCatalogue("libtaskbar"); @@ -212,7 +215,7 @@ bool Kicker::highlightMenuItem(const TQString &menuId) void Kicker::showKMenu() { - MenuManager::the()->showKMenu(); + MenuManager::the()->kmenuAccelActivated(); } void Kicker::popupKMenu(const TQPoint &p) diff --git a/kicker/kicker/core/kmenubase.ui b/kicker/kicker/core/kmenubase.ui new file mode 100644 index 000000000..1adb59b38 --- /dev/null +++ b/kicker/kicker/core/kmenubase.ui @@ -0,0 +1,300 @@ + +KMenuBase + + + KMenu + + + + 0 + 0 + 723 + 580 + + + + KMenu + + + MShadow + + + MShape + + + + m_search + + + + 20 + 40 + 190 + 54 + + + + + unnamed + + + 0 + + + 0 + + + + m_searchFrame + + + + 5 + 0 + 0 + 0 + + + + + 0 + 52 + + + + + 32767 + 52 + + + + StyledPanel + + + Raised + + + 0 + + + + unnamed + + + + layout18 + + + + unnamed + + + + m_searchLabel + + + ParentOrigin + + + + 14 + + + + Search: + + + + + m_kcommand + + + + 7 + 0 + 0 + 0 + + + + + + m_searchPixmap + + + + 1 + 1 + 0 + 0 + + + + + 32 + 32 + + + + + 32 + 32 + + + + PaletteBackground + + + ParentOrigin + + + true + + + + + + + + + spacer5_2 + + + Horizontal + + + Preferred + + + + 16 + 20 + + + + + + + + m_footer + + + + 20 + 110 + 407 + 34 + + + + + unnamed + + + 4 + + + 4 + + + + m_userInfo + + + + 3 + 0 + 0 + 0 + + + + User&nbsp;<b>user</b>&nbsp;on&nbsp;<b>host</b> + + + + + spacer13_2 + + + Horizontal + + + MinimumExpanding + + + + 10 + 20 + + + + + + m_branding + + + + 4 + 4 + 0 + 0 + + + + + 90 + 24 + + + + + + + image0 + + + true + + + true + + + + + spacer13 + + + Horizontal + + + Fixed + + + + 14 + 20 + + + + + + + + + + + 89504e470d0a1a0a0000000d494844520000005a0000001808060000007a38ea6500000122494441546881ed98610e83200c85c7e2a93c0087e6005c0b7f754152ea43a532ec972c512cf5f96c28d3c5183f467fbe4f0b780b66b41266b41266b412cbd302b4594348d2f5e8bd93e2e83a9a93e2ada21b397a51355e57d1794592695c954af16b08899b23e5f955f41a42a25fbbfcbe709a9ed0c9998eb25b3aa2f72e7aef4634fbdf395c3a72d3f346811c73f390b172fcac36eefc2a522172e3746fd1e8b3a2a579523e3ae7c6b9fbf434f46e764623cda18ce78cc973213950b108b99e1ee6235b398e050d44e2f2f1abb966e3f43eba5c0e466ba03df4d4fa0982b8469706b65469f9a0b57d271787beb89ab63b970da9c1b5c4ba59bf4723464b3de9eebfe0531a3de20e64ba6f1da3f50a62ca8a1e91e92a7a54cc6825cc6825cc682536f10e058bc1bcdf310000000049454e44ae426082 + + + + kmenubase.ui.h + + + init() + + + + kcombobox.h + + diff --git a/kicker/kicker/core/kmenubase.ui.h b/kicker/kicker/core/kmenubase.ui.h new file mode 100644 index 000000000..e1ed1ac25 --- /dev/null +++ b/kicker/kicker/core/kmenubase.ui.h @@ -0,0 +1,9 @@ +#include + +void KMenuBase::init() +{ + XSetWindowAttributes attrs; + attrs.override_redirect = True; + XChangeWindowAttributes( qt_xdisplay(), winId(), CWOverrideRedirect, &attrs ); + setWFlags( Qt::WType_Popup ); +} diff --git a/kicker/kicker/core/main.cpp b/kicker/kicker/core/main.cpp index 16090b9af..76e94a0e3 100644 --- a/kicker/kicker/core/main.cpp +++ b/kicker/kicker/core/main.cpp @@ -108,7 +108,7 @@ extern "C" KDE_EXPORT int kdemain( int argc, char ** argv ) appname.sprintf("kicker-screen-%d", kicker_screen_number); KAboutData aboutData( appname.data(), I18N_NOOP("KDE Panel"), - version, description, KAboutData::License_BSD, + version, description, KAboutData::License_GPL_V2, I18N_NOOP("(c) 1999-2010, The KDE Team") ); aboutData.addAuthor("Timothy Pearson", I18N_NOOP("Current maintainer"), "kb9vqf@pearsoncomputing.net"); diff --git a/kicker/kicker/core/menumanager.cpp b/kicker/kicker/core/menumanager.cpp index 908f6bdf9..61110de14 100644 --- a/kicker/kicker/core/menumanager.cpp +++ b/kicker/kicker/core/menumanager.cpp @@ -31,9 +31,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "client_mnu.h" #include "container_extension.h" #include "global.h" +#include "k_new_mnu.h" #include "k_mnu.h" +#include "k_mnu_stub.h" #include "kicker.h" #include "panelbutton.h" +#include "kickerSettings.h" #include "menumanager.h" #include "menumanager.moc" @@ -62,7 +65,11 @@ MenuManager* MenuManager::the() MenuManager::MenuManager(TQObject *parent) : TQObject(parent, "MenuManager"), DCOPObject("MenuManager") { - m_kmenu = new PanelKMenu; + if (KickerSettings::legacyKMenu()) + m_kmenu = new KMenuStub(new PanelKMenu); + else + m_kmenu = new KMenuStub(new KMenu); + kapp->dcopClient()->setNotifications(true); connect(kapp->dcopClient(), TQT_SIGNAL(applicationRemoved(const TQCString&)), this, TQT_SLOT(applicationRemoved(const TQCString&))); @@ -83,14 +90,8 @@ void MenuManager::slotSetKMenuItemActive() m_kmenu->selectFirstItem(); } -void MenuManager::showKMenu() -{ - m_kmenu->showMenu(); -} - void MenuManager::popupKMenu(const TQPoint &p) { -// kdDebug(1210) << "popupKMenu()" << endl; if (m_kmenu->isVisible()) { m_kmenu->hide(); @@ -120,7 +121,7 @@ void MenuManager::unregisterKButton(PanelPopupButton *button) m_kbuttons.remove(button); } -PanelPopupButton* MenuManager::findKButtonFor(TQPopupMenu* menu) +PanelPopupButton* MenuManager::findKButtonFor(TQWidget* menu) { KButtonList::const_iterator itEnd = m_kbuttons.constEnd(); for (KButtonList::const_iterator it = m_kbuttons.constBegin(); it != itEnd; ++it) @@ -173,7 +174,7 @@ void MenuManager::kmenuAccelActivated() const TQSize size = m_kmenu->sizeHint(); m_kmenu->resize(size.width(),size.height()); - PanelPopupButton* button = findKButtonFor(m_kmenu); + PanelPopupButton* button = findKButtonFor(m_kmenu->widget()); // let's unhide the panel while we're at it. traverse the widget // hierarchy until we find the panel, if any @@ -193,7 +194,6 @@ void MenuManager::kmenuAccelActivated() menuParent = menuParent->parent(); } - button->showMenu(); } } @@ -217,7 +217,7 @@ TQCString MenuManager::createMenu(TQPixmap icon, TQString text) void MenuManager::removeMenu(TQCString menu) { - bool iterate = true; + bool iterate = true, need_adjustSize = false; ClientMenuList::iterator it = clientmenus.begin(); for (; it != clientmenus.end(); iterate ? ++it : it) { @@ -228,15 +228,17 @@ void MenuManager::removeMenu(TQCString menu) m_kmenu->removeClientMenu(m->idInParentMenu); it = clientmenus.erase(it); iterate = false; + need_adjustSize = true; } } - m_kmenu->adjustSize(); + if (need_adjustSize) + m_kmenu->adjustSize(); } void MenuManager::applicationRemoved(const TQCString& appRemoved) { - bool iterate = true; + bool iterate = true, need_adjustSize = false; ClientMenuList::iterator it = clientmenus.begin(); for (; it != clientmenus.end(); iterate ? ++it : it) { @@ -247,9 +249,11 @@ void MenuManager::applicationRemoved(const TQCString& appRemoved) m_kmenu->removeClientMenu(m->idInParentMenu); it = clientmenus.erase(it); iterate = false; + need_adjustSize = true; } } - m_kmenu->adjustSize(); + if (need_adjustSize) + m_kmenu->adjustSize(); } bool MenuManager::process(const TQCString &fun, const TQByteArray &data, diff --git a/kicker/kicker/core/menumanager.h b/kicker/kicker/core/menumanager.h index e9d7dfef4..1139b0b61 100644 --- a/kicker/kicker/core/menumanager.h +++ b/kicker/kicker/core/menumanager.h @@ -28,7 +28,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include class PanelKMenu; +class KMenu; class KickerClientMenu; +class KMenuStub; class PanelPopupButton; typedef TQValueList KButtonList; @@ -50,13 +52,12 @@ public: bool process(const TQCString &fun, const TQByteArray &data, TQCString& replyType, TQByteArray &reply); // KMenu controls - PanelKMenu* kmenu() { return m_kmenu; } - void showKMenu(); + KMenuStub* kmenu() { return m_kmenu; } void popupKMenu(const TQPoint &p); void registerKButton(PanelPopupButton *button); void unregisterKButton(PanelPopupButton *button); - PanelPopupButton* findKButtonFor(TQPopupMenu* menu); + PanelPopupButton* findKButtonFor(TQWidget* menu); ~MenuManager(); public slots: @@ -67,7 +68,7 @@ protected slots: void applicationRemoved(const TQCString&); protected: - PanelKMenu* m_kmenu; + KMenuStub* m_kmenu; typedef TQValueList ClientMenuList; ClientMenuList clientmenus; diff --git a/kicker/kicker/core/unhidetrigger.cpp b/kicker/kicker/core/unhidetrigger.cpp index 7dcf1a127..0a5093a61 100644 --- a/kicker/kicker/core/unhidetrigger.cpp +++ b/kicker/kicker/core/unhidetrigger.cpp @@ -39,7 +39,7 @@ UnhideTrigger::UnhideTrigger() , _lastXineramaScreen( -1 ) , enabledCount( 0 ) { - _timer = new TQTimer( this ); + _timer = new TQTimer( this, "UnhideTrigger" ); connect( _timer, TQT_SIGNAL(timeout()), TQT_SLOT(pollMouse()) ); } diff --git a/kicker/kicker/interfaces/Makefile.am b/kicker/kicker/interfaces/Makefile.am new file mode 100644 index 000000000..11f3f94b9 --- /dev/null +++ b/kicker/kicker/interfaces/Makefile.am @@ -0,0 +1,12 @@ +METASOURCES = AUTO +INCLUDES= -I$(top_srcdir)/src $(all_includes) + +# The library containing the plugin base class +lib_LTLIBRARIES = libkickoffsearch_interfaces.la +libkickoffsearch_interfaces_la_SOURCES = kickoff-search-plugin.cpp kickoffsearchinterface.cpp +libkickoffsearch_interfaces_la_LDFLAGS = $(all_libraries) -version-info 0:0:0 + +kickoffsearchincludedir = $(includedir) +kickoffsearchinclude_HEADERS = kickoff-search-plugin.h kickoffsearchinterface.h + +kde_servicetypes_DATA = kickoffsearchplugin.desktop diff --git a/kicker/kicker/interfaces/kickoff-search-plugin.cpp b/kicker/kicker/interfaces/kickoff-search-plugin.cpp new file mode 100644 index 000000000..c229c85b0 --- /dev/null +++ b/kicker/kicker/interfaces/kickoff-search-plugin.cpp @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2006 by Stephan Binner * + * * + * 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 "kickoff-search-plugin.h" +#include + +KickoffSearch::Plugin::Plugin(TQObject *parent, const char* name ) + :TQObject( parent, name ) +{ +} + +KickoffSearch::Plugin::~Plugin() +{ +} + +KickoffSearch::KickoffSearchInterface* KickoffSearch::Plugin::kickoffSearchInterface() +{ + return static_cast( parent()->child( 0, "KickoffSearch::KickoffSearchInterface" ) ); +} + +#include "kickoff-search-plugin.moc" diff --git a/kicker/kicker/interfaces/kickoff-search-plugin.h b/kicker/kicker/interfaces/kickoff-search-plugin.h new file mode 100644 index 000000000..e91d079e7 --- /dev/null +++ b/kicker/kicker/interfaces/kickoff-search-plugin.h @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2006 by Stephan Binner * + * Copyright (c) 2006 Debajyoti Bera * + * * + * 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 KICKOFF_SEARCH_PLUGIN_H +#define KICKOFF_SEARCH_PLUGIN_H + +#include "kickoffsearchinterface.h" + +#include +#include +#include + +typedef enum { + ACTIONS = 0, + APPS, + BOOKMARKS, + NOTES, + MAILS, + FILES, + MUSIC, + WEBHIST, + CHATS, + FEEDS, + PICS, + VIDEOS, + DOCS, + OTHER, + num_categories +} CATEGORY; + +class HitMenuItem +{ +public: + HitMenuItem (int id, int category) + : id (id), category (category),score(0) { } /* dummy */ + HitMenuItem (TQString name, TQString info, KURL uri, TQString mimetype, int id, int category, TQString icon=TQString::null, int score = 0) + : display_name (name) + , display_info (info) + , uri (uri) + , mimetype (mimetype) + , id (id) + , category (category) + , icon (icon) + , score (score) + , service (NULL) { } + + ~HitMenuItem () { } + + bool operator< (HitMenuItem item) + { + return ((category == item.category && score > item.score) || (category == item.category && id < item.id) || + (category < item.category)); + } + + // FIXME: We dont really need to store display_name and display_info + TQString display_name; // name to display + TQString display_info; // other information to display + KURL uri; // uri to open when clicked + TQString mimetype; + int id; // id of the item in the menu + int category; + TQString icon; + int score; + KService::Ptr service; + + TQString quotedPath () const + { + return uri.path ().replace ('"', "\\\""); + } +}; + +namespace KickoffSearch { + + class Plugin : public TQObject + { + Q_OBJECT + + public: + Plugin(TQObject *parent, const char* name=0); + virtual ~Plugin(); + + virtual bool daemonRunning()=0; + virtual void query(TQString,bool)=0; + + KickoffSearchInterface * kickoffSearchInterface(); + }; +}; + +#endif /* KICKOFF_SEARCH_PLUGIN_H */ diff --git a/kicker/kicker/interfaces/kickoffsearchinterface.cpp b/kicker/kicker/interfaces/kickoffsearchinterface.cpp new file mode 100644 index 000000000..6df9d0630 --- /dev/null +++ b/kicker/kicker/interfaces/kickoffsearchinterface.cpp @@ -0,0 +1,27 @@ +/*************************************************************************** + * Copyright (C) 2006 by Stephan Binner * + * * + * 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 "kickoffsearchinterface.h" + +KickoffSearch::KickoffSearchInterface::KickoffSearchInterface( TQObject* parent, const char* name ) + :TQObject( parent, name ) +{ +} + +#include "kickoffsearchinterface.moc" diff --git a/kicker/kicker/interfaces/kickoffsearchinterface.h b/kicker/kicker/interfaces/kickoffsearchinterface.h new file mode 100644 index 000000000..91d28e7e6 --- /dev/null +++ b/kicker/kicker/interfaces/kickoffsearchinterface.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2006 by Stephan Binner * + * * + * 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 KICKOFFSEARCHINTERFACE_H +#define KICKOFFSEARCHINTERFACE_H + +#include + +class HitMenuItem; + +namespace KickoffSearch +{ + class KickoffSearchInterface :public TQObject + { + Q_OBJECT + + public: + KickoffSearchInterface( TQObject* parent, const char* name = 0); + + public: + virtual bool anotherHitMenuItemAllowed(int cat) = 0; + virtual void addHitMenuItem(HitMenuItem* item) = 0; + virtual void searchOver() = 0; + virtual void initCategoryTitlesUpdate() = 0; + virtual void updateCategoryTitles() = 0; + }; +} + +#endif /* SELECTIONINTERFACE_H */ + diff --git a/kicker/kicker/interfaces/kickoffsearchplugin.desktop b/kicker/kicker/interfaces/kickoffsearchplugin.desktop new file mode 100644 index 000000000..137d10a1d --- /dev/null +++ b/kicker/kicker/interfaces/kickoffsearchplugin.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=KickoffSearch/Plugin +Comment=A search plugin for Kickoff diff --git a/kicker/kicker/plugins/Makefile.am b/kicker/kicker/plugins/Makefile.am new file mode 100644 index 000000000..bab1011f9 --- /dev/null +++ b/kicker/kicker/plugins/Makefile.am @@ -0,0 +1,24 @@ +INCLUDES = -I$(top_srcdir)/interfaces $(all_includes) $(LIBBEAGLE_CFLAGS) $(GLIB_CFLAGS) +METASOURCES = AUTO + +# Install this plugin in the KDE modules directory +kde_module_LTLIBRARIES = kickoffsearch_beagle.la + +# Srcs for the plugin +kickoffsearch_beagle_la_SOURCES = kickoff-beagle-plugin.cpp beaglesearch.cpp + +# Libs needed by the plugin +kickoffsearch_beagle_la_LIBADD = $(LIB_KPARTS) ../interfaces/libkickoffsearch_interfaces.la \ + $(LIBBEAGLE_LIBADD) $(GLIB_LIBADD) + +# LD flags for the plugin +# -module says: this is a module, i.e. something you're going to dlopen +# so e.g. it has no version number like a normal shared lib would have. +kickoffsearch_beagle_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) + +# Install the desktop file needed to detect the plugin +kde_services_DATA = kickoffsearch_beagle.desktop + +# i18n translation messages +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/kickoffsearch_beagle.pot diff --git a/kicker/kicker/plugins/beaglesearch.cpp b/kicker/kicker/plugins/beaglesearch.cpp new file mode 100644 index 000000000..9f86c8fbd --- /dev/null +++ b/kicker/kicker/plugins/beaglesearch.cpp @@ -0,0 +1,362 @@ +/***************************************************************** + + Copyright (c) 2006 Debajyoti Bera + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +******************************************************************/ + +#include "beaglesearch.h" + +#include +#include +#include +#include +#include + +void beagle_init () +{ + g_type_init (); +} + +// ---------------- Hit --------------------------- + +Hit::Hit (BeagleHit *_hit) : processed (false) +{ + hit = beagle_hit_ref (_hit); +} + +Hit::~Hit () +{ + beagle_hit_unref (hit); + if (! processed) + return; + TQDictIterator it (property_map); + for( ; it.current(); ++it ) + ((TQStringList *)it.current())->clear (); + +} + +void Hit::processProperties () +{ + processed = true; + GSList *prop_list = beagle_hit_get_all_properties (hit); + GSList *it; + property_map.setAutoDelete (true); + for (it = prop_list; it; it = it->next) { + BeagleProperty *property = (BeagleProperty *) it->data; + TQString key = TQString::fromUtf8 (beagle_property_get_key (property)); + if (! property_map [key]) + property_map.insert (key, new TQStringList ()); + property_map [key]->append (TQString::fromUtf8 (beagle_property_get_value (property))); + } + g_slist_free (prop_list); +} + +const TQString Hit::operator[] (TQString prop_name) +{ + if (! processed) + processProperties (); + + TQStringList *prop_list = property_map [prop_name]; + if (! prop_list) + return TQString::null; + if (prop_list->count () != 1) + return TQString::null; + return (TQString)prop_list->first (); +} + +// ---------------- BeagleSearch ------------------ + +BeagleSearchResult::BeagleSearchResult(int client_id) + : client_id (client_id), total (0) +{ + hitlist = new TQPtrList; + hitlist->setAutoDelete (true); +} + + +BeagleSearchResult::~BeagleSearchResult() +{ + // everything is set to autodelete +} + +void BeagleSearchResult::addHit(BeagleHit *_hit) +{ + Hit *hit = new Hit (_hit); + hitlist->prepend (hit); +} + +const TQPtrList *BeagleSearchResult::getHits () const +{ + return hitlist; +} + + +static int total_hits; + +static void print_feed_item_hit (BeagleHit *hit) +{ + const char *text; + + if (beagle_hit_get_one_property (hit, "dc:title", &text)) + g_print ("Blog: %s\n", text); +} + +static void print_file_hit (BeagleHit *hit) +{ + g_print ("File: %s, (%s)\n", beagle_hit_get_uri (hit), beagle_hit_get_mime_type (hit)); +} + +static void print_other_hit (BeagleHit *hit) +{ + const char *text; + + g_print ("%s (%s)", beagle_hit_get_uri (hit), + beagle_hit_get_source (hit)); + if (beagle_hit_get_one_property (hit, "dc:title", &text)) + g_print ("title = %s\n", text); +} + +static void print_hit (BeagleHit *hit) +{ + if (strcmp (beagle_hit_get_type (hit), "FeedItem") == 0) { + print_feed_item_hit (hit); + } + else if (strcmp (beagle_hit_get_type (hit), "File") == 0) { + print_file_hit (hit); + } else { + print_other_hit (hit); + } +} + +// ---------------- BeagleSearchClient ------------------ + +void BeagleSearchClient::run () +{ + kdDebug () << "Starting query ..." << endl; + + TQTime query_timer; + query_timer.start (); + + g_signal_connect (query, "hits-added", + G_CALLBACK (hitsAddedSlot), + this); + g_signal_connect (query, "finished", + G_CALLBACK (finishedSlot), + this); + beagle_client_send_request_async (client, + BEAGLE_REQUEST (query), + NULL); + g_main_loop_run (main_loop); + kdDebug () << "Finished query ..." << endl; + + TQCustomEvent *ev; + if (collate_results) { + result->query_msec = query_timer.elapsed (); + + ev = new TQCustomEvent (RESULTFOUND, result); + TQApplication::postEvent (object, ev); + } + + ev = new TQCustomEvent (KILLME, this); + TQApplication::postEvent (object, ev); + +} + +void BeagleSearchClient::stopClient () +{ + if (finished ()) + return; // duh! + kdDebug () << "Query thread " << id << " not yet finished ..." << endl; + // get ready for suicide + client_mutex->lock (); + kill_me = true; + g_signal_handlers_disconnect_by_func ( + query, + (void *)hitsAddedSlot, + this); + g_signal_handlers_disconnect_by_func ( + query, + (void *)finishedSlot, + this); + g_main_loop_quit (main_loop); + client_mutex->unlock (); +} + +void BeagleSearchClient::hitsAddedSlot (BeagleQuery *query, + BeagleHitsAddedResponse *response, + BeagleSearchClient *bsclient) +{ + GSList *hits, *l; + gint i; + gint nr_hits; + + // check if we are supposed to be killed + bsclient->client_mutex->lock (); + if (bsclient->kill_me) { + kdDebug () << "Suicide time before processing" << endl; + bsclient->client_mutex->unlock (); + return; + } + bsclient->client_mutex->unlock (); + + hits = beagle_hits_added_response_get_hits (response); + + nr_hits = g_slist_length (hits); + total_hits += nr_hits; + g_print ("Found hits (%d) at %ld:\n", nr_hits, time (NULL)); + + BeagleSearchResult *search_result; + if (! bsclient->collate_results) + search_result = new BeagleSearchResult (bsclient->id); + else + search_result = bsclient->result; + search_result->total += nr_hits; + + for (l = hits, i = 1; l; l = l->next, ++i) { + //g_print ("[%d] ", i); + //print_hit (BEAGLE_HIT (l->data)); + //g_print ("\n"); + + search_result->addHit(BEAGLE_HIT (l->data));//hit); + } + g_print ("[%ld] hits adding finished \n", time (NULL)); + + // check if we are supposed to be killed + bsclient->client_mutex->lock (); + if (bsclient->kill_me) { + kdDebug () << "Suicide time before sending ..." << endl; + bsclient->client_mutex->unlock (); + if (! bsclient->collate_results) + delete search_result; + return; + } + bsclient->client_mutex->unlock (); + + // time to send back results, if user asked so + if (bsclient->collate_results) + return; + TQCustomEvent *ev = new TQCustomEvent (RESULTFOUND, search_result); + g_print ("[%ld] event notified \n", time (NULL)); + TQApplication::postEvent (bsclient->object, ev); +} + +void BeagleSearchClient::finishedSlot (BeagleQuery *query, + BeagleFinishedResponse *response, + BeagleSearchClient *bsclient) +{ + // check if we are supposed to be killed + bsclient->client_mutex->lock (); + bool should_kill = bsclient->kill_me; + TQObject* receiver = bsclient->object; + bsclient->client_mutex->unlock (); + + if (should_kill) + return; + + g_main_loop_quit (bsclient->main_loop); + + if (bsclient->collate_results) + return; // if we are collating, everything will be send from a central place + if (receiver) { + TQCustomEvent *ev = new TQCustomEvent (SEARCHOVER, bsclient); + g_print ("[%ld] query finish notified \n", time (NULL)); + TQApplication::postEvent (receiver, ev); + } +} + +// ----------------- BeagleUtil ------------------- + +BeagleQuery * +BeagleUtil::createQueryFromString (TQString query_str, + TQStringList &sources_menu, + TQStringList &types_menu, + int max_hits_per_source) +{ + BeagleQuery *beagle_query = beagle_query_new (); + beagle_query_set_max_hits (beagle_query, max_hits_per_source); // this is per source! + + kdDebug () << "Creating query from \"" << query_str << "\"" << endl; + for ( TQStringList::Iterator it = sources_menu.begin(); it != sources_menu.end(); ++it ) + beagle_query_add_source (beagle_query, g_strdup ((*it).utf8 ())); + + for ( TQStringList::Iterator it = types_menu.begin(); it != types_menu.end(); ++it ) + beagle_query_add_hit_type (beagle_query, g_strdup ((*it).utf8 ())); + + TQStringList query_terms; + TQString start_date, end_date; + TQStringList words = TQStringList::split (' ', query_str, false); + for ( TQStringList::Iterator it = words.begin(); it != words.end(); ++it ) { + TQStringList key_value_pair = TQStringList::split ('=', *it, false); + if (key_value_pair.count () == 1) + query_terms += *it; + else if (key_value_pair.count () == 2) { + TQString key = key_value_pair [0].lower (); + TQString value = key_value_pair [1]; + if (key == "mime") + beagle_query_add_mime_type (beagle_query, g_strdup (value.utf8 ())); + else if (key == "type") + beagle_query_add_hit_type (beagle_query, g_strdup (value.utf8 ())); + else if (key == "source") + beagle_query_add_source (beagle_query, g_strdup (value.utf8 ())); + else if (key == "start") + start_date = value; + else if (key == "end") + end_date = value; + else + query_terms += *it; + } else + query_terms += *it; + } + + beagle_query_add_text (beagle_query, g_strdup (query_terms.join (" ").utf8 ())); + kdDebug () << "Adding query text:" << query_terms.join (" ").utf8 () << endl; + + if (start_date.isNull () && end_date.isNull ()) + return beagle_query; + + //kdDebug () << "Handling dates ..." << endl; + BeagleQueryPartDate * date_part = beagle_query_part_date_new (); + if (! start_date.isNull ()) + beagle_query_part_date_set_start_date (date_part, timestringToBeagleTimestamp (start_date)); + if (! end_date.isNull ()) + beagle_query_part_date_set_end_date (date_part, timestringToBeagleTimestamp (end_date)); + beagle_query_add_part (beagle_query, BEAGLE_QUERY_PART (date_part)); + + return beagle_query; +} + +// timestring format allowed YYYYmmDD +BeagleTimestamp * +BeagleUtil::timestringToBeagleTimestamp(TQString timestring) +{ + //kdDebug () << "datetime string:" << timestring << endl; + // FIXME: error check timestring format + if (timestring.isNull () || timestring.stripWhiteSpace () == "" || timestring.length() != 8 ) + return beagle_timestamp_new_from_unix_time (TQDateTime::currentDateTime ().toTime_t ()); + //TQDateTime dt = TQDateTime::fromString (timestring, Qt::ISODate); + struct tm tm_time; + time_t timet_time; + time (&timet_time); + localtime_r (&timet_time, &tm_time); + strptime (timestring.ascii(), "%Y%m%d", &tm_time); + tm_time.tm_sec = tm_time.tm_min = tm_time.tm_hour = 0; + //kdDebug() << asctime (&tm_time) << endl; + timet_time = mktime (&tm_time); + return beagle_timestamp_new_from_unix_time (timet_time); +} + diff --git a/kicker/kicker/plugins/beaglesearch.h b/kicker/kicker/plugins/beaglesearch.h new file mode 100644 index 000000000..e192f6af3 --- /dev/null +++ b/kicker/kicker/plugins/beaglesearch.h @@ -0,0 +1,234 @@ +/***************************************************************** + + Copyright (c) 2006 Debajyoti Bera + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +******************************************************************/ + +#ifndef BEAGLESEARCH_H +#define BEAGLESEARCH_H + +#include +#include +#include +#include +#include + +#include +#include + +extern "C" { +#include +#include +} + +// BeagleSearchClient sends 3 types of events +// when results are to be sent as they arrive, +// - RESULTFOUND : when result is found +// - SEARCHOVER : when search is over +// - KILLME : just before thread finishes - used to cleanup the thread object +// when results are to be sent after receiving all of them +// - RESULTFOUND : when all results are obtained +// - KILLME : just before thread finishes - used to cleanup the thread object +#define RESULTFOUND (TQEvent::Type)1001 /* TQEvent::User + 1 */ +#define SEARCHOVER (TQEvent::Type)1002 /* TQEvent::User + 2 */ +#define KILLME (TQEvent::Type)1003 /* TQEvent::User + 3 */ + +class TQStringList; + +// IMPORTANT: Call this before any beagle calls +void beagle_init (); + +class Hit { +public: + Hit (BeagleHit *_hit); + ~Hit (); + + // convenience wrappers + // remember that the hit values are utf8 strings + const KURL getUri () const { return KURL (TQString::fromUtf8 (beagle_hit_get_uri (hit)));} + const TQString getType () const { return TQString::fromUtf8 (beagle_hit_get_type (hit));} + const TQString getMimeType () const { return TQString::fromUtf8 (beagle_hit_get_mime_type (hit));} + const TQString getSource () const { return TQString::fromUtf8 (beagle_hit_get_source (hit));} + const KURL getParentUri () const { return KURL (TQString::fromUtf8 (beagle_hit_get_parent_uri (hit)));} + const TQDict& getAllProperties () + { + if (! processed) + processProperties (); + return property_map; + } + const TQStringList* getProperties (TQString prop_name) + { + if (! processed) + processProperties (); + return property_map [prop_name]; + } + const TQString operator[] (TQString prop_name); + +private: + BeagleHit *hit; + TQDict property_map; + // not every hit may be used. so, do a lazy processing of property_map + bool processed; + void processProperties (); +}; + +class BeagleSearchResult{ +public: + BeagleSearchResult(int client_id); + ~BeagleSearchResult(); + void addHit (BeagleHit *hit); + TQString getHitCategory (Hit *hit); + + // id of the bsclient + int client_id; + // time taken to finish query + int query_msec; + // total number of results in this query + int total; + + const TQPtrList *getHits () const; + +private: + // lists of hits + TQPtrList *hitlist; +}; + +// caller should delete bsclient->result and bsclient +class BeagleSearchClient : public TQThread { +public: + // passing NULL for client makes bsclient create client itself and + // delete it later + BeagleSearchClient (int id, + TQObject *y, + BeagleClient *client, + BeagleQuery *query, + bool collate_results) + : id (id), kill_me (false), object (y), client (client), + query (query), destroy_client (false), collate_results (collate_results) + { + if (client == NULL) { + client = beagle_client_new (NULL); + destroy_client = true; + } + +// if (client == NULL) +// throw -1; + + main_loop = g_main_loop_new (NULL, FALSE); + if (collate_results) + result = new BeagleSearchResult (id); + + client_mutex = new TQMutex (); + } + + // It is never safe to delete BeagleSearchClient directly, the thread might still be running + ~BeagleSearchClient () + { + if (! finished ()) { + kdDebug () << "Thread " << id << " still running. Waiting.........." << endl; + wait (); + } + + if (destroy_client) + g_object_unref (client); + g_main_loop_unref (main_loop); + g_object_unref (query); + kdDebug() << "Deleting client ..." << id << endl; + delete client_mutex; + } + +private: + static void hitsAddedSlot (BeagleQuery *query, + BeagleHitsAddedResponse *response, + BeagleSearchClient *bsclient); + + static void finishedSlot (BeagleQuery *query, + BeagleFinishedResponse *response, + BeagleSearchClient *bsclient); + +public: + // run() starts the query and sends the result as follows: + // - either wait till get back all results and send it as RESULTFOUND + // - or, send results as it gets them as RESULTFOUND and + // send SEARCHOVER when finished + // collate_results controls the behaviour + virtual void run ( ); + + // after stopClient() is called, application can safely go and remove previous menu entries + // - i.e. after stopClient is called, app doesnt except the eventhandler to receive any results + // - use client_id to determine which is the current client, set it right after stopclient + // - Eventhandler checks client id, if it is current, it adds stuff to the menu + // else, it discards everything + // Once eventhandler is being processed, doQuery() wont be called and vice versa + // so no need to serialize eventhandler and doquery + // + // stopClient needs to make sure that once it is called, the thread is finished asap. Use a mutex + // to serialize actions. callbacks need to use mutex too. + // stopclient has to remove signal handlers to prevent further signal calls, set kill_me flag + // and quite main loop + // stopClient can be called at the following times: + // - Waiting for the first result: + // nothing extra + // - in hitsAddedSlot, processing results + // in callback, before processing, if killme is set, just return. + // - in hitsAddedSlot, after sending results + // before sending, if killme is set, dont send results + // (doing it twice in hitsAdded because forming BeagleSearchResult can take time) + // - Waiting for more results + // nothing extra + // - in finishedSlot, before sending finishedMsg + // if killme is set, just return + // - in finishedSlot, after sending finishedMsg + // if killme is set, just return + // in Run(), when return from mainloop, if killme is set, dont do anything more but call delete this + void stopClient (); + + // id of the client + // this is required in case applications fires many clients in rapid succession + int id; + + GMainLoop * main_loop; + BeagleSearchResult *result; + + // this is set if the client is obsolete now i.e. + // the application doesnt need the results from the client anymore + bool kill_me; +private: + // the application; need this to send events to the application + TQObject *object; + // mutex to control setting the kill_me shared variable + TQMutex *client_mutex; + BeagleClient *client; + BeagleQuery *query; + // should the client be destroyed by the client + // if the client created it, then most probably it should + bool destroy_client; + bool collate_results; +}; + +class BeagleUtil { +public: + + static BeagleQuery *createQueryFromString (TQString query_str, + TQStringList &sources, + TQStringList &types, + int max_hits_per_source = 100); + static BeagleTimestamp *timestringToBeagleTimestamp (TQString timestring); +}; + +#endif diff --git a/kicker/kicker/plugins/kickoff-beagle-plugin.cpp b/kicker/kicker/plugins/kickoff-beagle-plugin.cpp new file mode 100644 index 000000000..3cad77ca4 --- /dev/null +++ b/kicker/kicker/plugins/kickoff-beagle-plugin.cpp @@ -0,0 +1,499 @@ +/*************************************************************************** + * Copyright (C) 2006 by Stephan Binner * + * Copyright (c) 2006 Debajyoti Bera * + * * + * 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 "kickoff-beagle-plugin.h" + +#include +#include + +#include +#include +#include +#include + +TQString dc_identifier = "dc:identifier"; +TQString dc_title = "dc:title"; +TQString parent_dc_title = "parent:dc:title"; +TQString exactfilename = "beagle:ExactFilename"; +TQString fixme_name = "fixme:Name"; +TQString beagle_filename = "beagle:Filename"; +TQString fixme_attachment_title = "fixme:attachment_title"; +TQString fixme_hasattachments = "fixme:hasAttachments"; +TQString parent_prefix = "parent:"; +TQString fixme_folder = "fixme:folder"; +TQString fixme_categories = "fixme:Categories"; +TQString fixme_comment = "fixme:Comment"; +TQString fixme_width = "fixme:width"; +TQString fixme_height = "fixme:height"; +TQString fixme_from_address = "fixme:from_address"; +TQString fixme_artist = "fixme:artist"; +TQString fixme_album = "fixme:album"; +TQString dc_source = "dc:source"; +TQString dc_publisher = "dc:publisher"; +TQString digikam_tag = "digikam:Tag"; +TQString fixme_speakingto = "fixme:speakingto"; +TQString fixme_starttime = "fixme:starttime"; +TQString comma_string = ","; +TQString vCard_FN = "vCard:FN"; +TQString vCard_PREFEMAIL = "vCard:PREFEMAIL"; +TQString fixme_uid = "fixme:uid"; + +static CATEGORY getHitCategory (Hit *hit) +{ + TQString hittype = hit->getType(); + TQString hitsource = hit->getSource(); + + // if hit source is None, dont handle it. Might be anthrax-envelope :) + if (hitsource.isNull()) + return OTHER; + + if (hitsource == "documentation") + return DOCS; + + if (hittype == "IMLog") + return CHATS; + + // sure shots + if (hittype == "FeedItem") + return FEEDS; + if (hittype == "WebHistory") + return WEBHIST; + if (hittype == "MailMessage") + return MAILS; + if (hittype == "Note") + return NOTES; + + // check for applications + if (hittype == "File" && (*hit) ["beagle:FilenameExtension"] == ".desktop") + return APPS; + + // check for music + TQString hitmimetype = hit->getMimeType(); + if (hitsource == "Amarok" + || hitmimetype.startsWith ("audio") + || hitmimetype == "application/ogg") + return MUSIC; // not an exhaustive search + + // check for images from files + if (hitsource == "Files" && hitmimetype.startsWith ("image")) + return PICS; + + if (hitsource == "Files" && hitmimetype.startsWith ("video")) + return VIDEOS; + + if (hitsource == "Files") + return FILES; + + if (hitsource == "KAddressBook") + return ACTIONS; + + return OTHER; +} + +K_EXPORT_COMPONENT_FACTORY( kickoffsearch_beagle, + KGenericFactory( "kickoffsearch_beagle" ) ) + +KickoffBeaglePlugin::KickoffBeaglePlugin(TQObject *parent, const char* name, const TQStringList&) + : KickoffSearch::Plugin(parent, name ), genericTitle( true ) +{ + g_type_init (); + current_beagle_client = NULL; +} + +bool KickoffBeaglePlugin::daemonRunning() +{ + return beagle_util_daemon_is_running(); +} + +void KickoffBeaglePlugin::query(TQString term, bool _genericTitle) +{ + genericTitle = _genericTitle; + current_query_str = term; + + // Beagle search + if (current_beagle_client != NULL) { + kdDebug () << "Previous client w/id " << current_beagle_client->id << " still running ... ignoring it." << endl; + current_beagle_client->stopClient (); + } + current_beagle_client_id = KApplication::random (); + kdDebug () << "Creating client with id:" << current_beagle_client_id << endl; + + BeagleClient *beagle_client = beagle_client_new (NULL); + if (beagle_client == NULL) { + kdDebug() << "beagle service not running ..." << endl; + return; + } + + TQStringList sources, types; + BeagleQuery *beagle_query = BeagleUtil::createQueryFromString (term, sources, types, 99); // maximum 99 results, if this doesnt work, blame the stars + + current_beagle_client = new BeagleSearchClient ( + current_beagle_client_id, + this, + beagle_client, + beagle_query, + false); + current_beagle_client->start(); +// kdDebug () << "Query dispatched at " << time (NULL) << endl; +} + +void KickoffBeaglePlugin::cleanClientList () +{ + toclean_list_mutex.lock (); + BeagleSearchClient *old_client = toclean_client_list.take (0); + if (old_client != NULL) { // failsafe + kdDebug () << "Cleanup old client " << old_client->id << endl; + delete old_client; + } + toclean_list_mutex.unlock (); +} + +void KickoffBeaglePlugin::customEvent (TQCustomEvent *e) +{ + if (e->type () == RESULTFOUND) { +// kdDebug () << "Quick query thread at " << time (NULL) << " with current_id=" << current_beagle_client_id << " finished ..." << endl; + BeagleSearchResult *result = (BeagleSearchResult *) e->data (); + if (current_beagle_client_id != result->client_id) { + kdDebug () << "Stale result from " << result->client_id << endl; + delete result; + // FIXME: Should I also free e ? + } else { + kdDebug () << "Good results ...total=" << result->total << endl; + showResults (result); + } + //KPassivePopup::message( "This is the message", this ); + } else if (e->type () == SEARCHOVER) { + BeagleSearchClient *client = (BeagleSearchClient *) e->data (); + if (client == NULL) { +// kdDebug () << "Query finished event at " << time (NULL) << " but client is already deleted" << endl; + return; + } +// kdDebug () << "Query finished event at " << time (NULL) << " for id=" << client->id << endl; + if (current_beagle_client_id == client->id) { + kickoffSearchInterface()->searchOver(); + current_beagle_client = NULL; // important ! + } + } else if (e->type () == KILLME) { + BeagleSearchClient *client = (BeagleSearchClient *) e->data (); + if (client->finished ()) + delete client; + else { + // add client to cleanup list + toclean_list_mutex.lock (); + toclean_client_list.append (client); + kdDebug () << "Scheduling client to be deleted in 500ms" << endl; + toclean_list_mutex.unlock (); + TQTimer::singleShot (500, this, TQT_SLOT (cleanClientList ())); + } + } +} + +// this method decides what to display in the result list +HitMenuItem *KickoffBeaglePlugin::hitToHitMenuItem (int category, Hit *hit) +{ + TQString title, info, mimetype, icon; + int score = 0; + KURL uri; + +#if 0 + kdDebug() << "*** " << hit->getUri() << endl; + TQDict all = hit->getAllProperties(); + TQDictIterator it( all ); + for( ; it.current(); ++it ) + kdDebug() << it.currentKey() << ": " << *(it.current()) << endl; +#endif + + switch (category) { + case FILES: + { + uri = hit->getUri (); + TQString uristr = uri.path (); + title = (*hit) [exactfilename]; + int last_slash = uristr.findRev ('/', -1); + info = i18n("Folder: %1").arg(last_slash == 0 ? "/" + : uristr.section ('/', -2, -2)); + } + break; + case ACTIONS: + { + if (hit->getSource()=="KAddressBook"){ + title = i18n("Send Email to %1").arg((*hit)[vCard_FN]); + info = (*hit)[vCard_PREFEMAIL]; + uri = "mailto:"+(*hit)[vCard_PREFEMAIL]; + mimetype = hit->getMimeType (); + icon = "mail_new"; + + HitMenuItem * first_item=new HitMenuItem (title, info, uri, mimetype, 0, category, icon, score); + kickoffSearchInterface()->addHitMenuItem(first_item); + + title =i18n("Open Addressbook at %1").arg((*hit)[vCard_FN]); + uri = "kaddressbook:/"+(*hit)[fixme_uid]; + icon = "kaddressbook"; + } + break; + } + case MAILS: + { + TQString prefix = TQString::null; + bool is_attachment = ((*hit) [parent_prefix + fixme_hasattachments] == "true"); + bool has_parent = (! hit->getParentUri ().isEmpty ()); + bool parent_mbox_file = false; + if (has_parent) + parent_mbox_file = ((*hit) [parent_prefix + fixme_folder] == TQString::null); + + // Logic: + // If has_parent == false, everything is normal + // If has_parent == true, parent_mbox_file == false, everything is normal, use uri + // FIXME: If has_parent == true, parent_mbox_file == true, ??? + // If has_parent == true, is_attachment == true, hit is attach and access with prefix "parent:", use parenturi + // Else, not attachment (multipart), access with prefix "parent:", use parenturi + + if (has_parent && !parent_mbox_file) { + uri = hit->getParentUri (); + prefix = parent_prefix; + if (is_attachment) + title = (*hit) [fixme_attachment_title]; + if (title.isEmpty ()) + title = (*hit) [prefix + dc_title]; + if (title.isEmpty ()) + title = i18n("No subject"); + if (is_attachment) + title = title.prepend (i18n("(Attachment) ")); + info = (i18n("From %1").arg((*hit) [prefix + fixme_from_address])); + } else { + uri = hit->getUri (); + title = (*hit) [dc_title]; + info = (i18n("From %1").arg((*hit) [fixme_from_address])); + } + } + mimetype = "message/rfc822"; // to handle attachment results + break; + case MUSIC: + uri = hit->getUri (); + title = (*hit) [exactfilename]; + { + TQString artist = (*hit) [fixme_artist]; + TQString album = (*hit) [fixme_album]; + if (! artist.isEmpty ()) + info = (i18n("By %1").arg(artist)); + else if (! album.isEmpty ()) + info = (i18n("From Album %1").arg(album)); + else { + TQString uristr = uri.path (); + int last_slash = uristr.findRev ('/', -1); + info = i18n("Folder: %1") + .arg(last_slash == 0 ? "/" : uristr.section ('/', -2, -2)); + } + } + break; + case VIDEOS: + uri = hit->getUri (); + title = (*hit) [exactfilename]; + { + TQString uristr = uri.path (); + int last_slash = uristr.findRev ('/', -1); + info = i18n("Folder: %1").arg(last_slash == 0 ? "/" : uristr.section ('/', -2, -2)); + } + break; + case WEBHIST: + uri = hit->getUri (); + title = (*hit) [dc_title]; + title = title.replace(TQRegExp("\n")," "); + mimetype = "text/html"; + if (title.isEmpty () || title.stripWhiteSpace ().isEmpty ()) { + title = uri.prettyURL (); + } else { + info = uri.host () + uri.path (); + } + break; + case FEEDS: + { + uri = KURL ((*hit) [dc_identifier]); + title = (*hit) [dc_title]; + mimetype = "text/html"; + TQString publisher = (*hit) [dc_publisher]; + TQString source = (*hit) [dc_source]; + if (! publisher.isEmpty ()) + info = publisher; + else if (! source.isEmpty ()) + info = source; + } + break; + case PICS: + { + uri = hit->getUri (); + title = (*hit) [exactfilename]; + TQString width = (*hit) [fixme_width]; + TQString height = (*hit) [fixme_height]; + if (width.isEmpty () || height.isEmpty ()) { + TQString uristr = uri.path (); + int last_slash = uristr.findRev ('/', -1); + info = i18n("Folder: %1") + .arg(last_slash == 0 ? "/" : uristr.section ('/', -2, -2)); + break; + } + info = (TQString (" (%1x%2)").arg (width).arg (height)); + const TQStringList *tags = hit->getProperties (digikam_tag); + if (tags == NULL) + break; + TQString tags_string = tags->join (comma_string); + info += (" " + tags_string); + } + break; + case APPS: + { + uri = hit->getUri (); + title = (*hit) [dc_title]; + KDesktopFile desktopfile(uri.path(),true); + if (genericTitle && !desktopfile.readGenericName().isEmpty()) { + title = desktopfile.readGenericName(); + info = desktopfile.readName(); + } + else { + title = desktopfile.readName(); + info = desktopfile.readGenericName(); + } + icon = desktopfile.readIcon(); + TQString input = current_query_str.lower(); + TQString command = desktopfile.readEntry("Exec"); + if (command==input) + score = 100; + else if (command.find(input)==0) + score = 50; + else if (command.find(input)!=-1) + score = 10; + else if (title==input) + score = 100; + else if (title.find(input)==0) + score = 50; + else if (title.find(input)!=-1) + score = 10; + break; + } + break; + case NOTES: + { + uri = hit->getUri (); + title = (*hit) [dc_title]; + title = i18n("Title: %1").arg(title.isEmpty() ? i18n("Untitled") : title); + + if (hit->getSource()=="KNotes") + icon="knotes"; + else + icon="contents2"; + } + break; + case CHATS: + { + uri = hit->getUri (); + title = (*hit) [fixme_speakingto]; + title = i18n("Conversation With %1").arg(title.isEmpty() ? i18n("Unknown Person") : title); + TQDateTime datetime; + datetime = datetimeFromString((*hit) [fixme_starttime]); + info=i18n("Date: %1").arg(KGlobal::locale()->formatDateTime(datetime,false)); + if (hit->getMimeType()=="beagle/x-kopete-log") + icon="kopete"; + else + icon="gaim"; + } + break; + case DOCS: + { + uri = hit->getUri (); + title = (*hit) [dc_title]; + if (title.isEmpty () || title.stripWhiteSpace ().isEmpty ()) + title = uri.prettyURL (); + else { + TQString uristr = uri.path (); + int last_slash = uristr.findRev ('/', -1); + info = i18n("Folder: %1").arg(last_slash == 0 ? "/" : uristr.section ('/', + -2, -2)); + } + } + break; + default: + return NULL; + } + if (mimetype.isEmpty ()) + mimetype = hit->getMimeType (); + return new HitMenuItem (title, info, uri, mimetype, 0, category, icon, score); +} + +void KickoffBeaglePlugin::showResults(BeagleSearchResult *result) +{ + if (result->total == 0 ) { + // Dont report error from here ... + kdDebug() << "No matches found" << endl; + delete result; + return; + } + + const TQPtrList *hits = result->getHits(); + if (hits == NULL) { + kdDebug () << "Hmm... null" << endl; + delete result; + return; + } + kickoffSearchInterface()->initCategoryTitlesUpdate(); + + TQPtrListIterator it (*hits); + Hit *hit; + for (; (hit = it.current ()) != NULL; ++it) { + CATEGORY category = getHitCategory (hit); + + // if category is not handled, continue + if (category == OTHER) + continue; + + if ( category == APPS ) { + // we need to check if this is useful + KService cs( hit->getUri().path() ); + if ( cs.noDisplay() ) + continue; + } + + if (!kickoffSearchInterface()->anotherHitMenuItemAllowed(category)) + continue; + + HitMenuItem *hit_item = hitToHitMenuItem (category, hit); + + if (!hit_item) + continue; + + kickoffSearchInterface()->addHitMenuItem(hit_item); + } + + kickoffSearchInterface()->updateCategoryTitles(); + + delete result; +} + +TQDateTime KickoffBeaglePlugin::datetimeFromString( const TQString& s) +{ + int year( s.mid( 0, 4 ).toInt() ); + int month( s.mid( 4, 2 ).toInt() ); + int day( s.mid( 6, 2 ).toInt() ); + int hour( s.mid( 8, 2 ).toInt() ); + int min( s.mid( 10, 2 ).toInt() ); + int sec( s.mid( 12, 2 ).toInt() ); + return TQDateTime(TQDate(year,month,day),TQTime(hour,min,sec)); +} + +#include "kickoff-beagle-plugin.moc" diff --git a/kicker/kicker/plugins/kickoff-beagle-plugin.h b/kicker/kicker/plugins/kickoff-beagle-plugin.h new file mode 100644 index 000000000..8288ba647 --- /dev/null +++ b/kicker/kicker/plugins/kickoff-beagle-plugin.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2006 by Stephan Binner * + * Copyright (c) 2006 Debajyoti Bera * + * * + * 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 CAPITALIZEPLUGIN_H +#define CAPITALIZEPLUGIN_H + +#include "../interfaces/kickoff-search-plugin.h" +#include "beaglesearch.h" + +class KickoffBeaglePlugin :public KickoffSearch::Plugin +{ + Q_OBJECT + +public: + KickoffBeaglePlugin(TQObject *parent, const char* name, const TQStringList&); + + void query(TQString, bool); + bool daemonRunning(); + +protected slots: + // to clean beaglesearchclients + void cleanClientList (); + +private: + TQString current_query_str; + + // all beagle activity is done through the BSC object + BeagleSearchClient *current_beagle_client; + + // used to send notification from the beagle thread to the main event loop + virtual void customEvent (TQCustomEvent *e); + + TQPtrList toclean_client_list; + TQMutex toclean_list_mutex; + + // show the results + void showResults (BeagleSearchResult *); + HitMenuItem *hitToHitMenuItem (int category, Hit *hit); + + // use a different id for each bsc client, and use that to separate stale responses from current ones + int current_beagle_client_id; + + bool genericTitle; + TQDateTime datetimeFromString( const TQString& ); +}; + +#endif /* CAPITALIZEPLUGIN_H */ diff --git a/kicker/kicker/plugins/kickoffsearch_beagle.desktop b/kicker/kicker/plugins/kickoffsearch_beagle.desktop new file mode 100644 index 000000000..2904dcda6 --- /dev/null +++ b/kicker/kicker/plugins/kickoffsearch_beagle.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Beagle Search +Comment=Beagle search plugin for Kickoff search +ServiceTypes=KickoffSearch/Plugin +Type=Service +X-KDE-Library=kickoffsearch_beagle diff --git a/kicker/kicker/ui/Makefile.am b/kicker/kicker/ui/Makefile.am index 1bd54777e..9384c28f5 100644 --- a/kicker/kicker/ui/Makefile.am +++ b/kicker/kicker/ui/Makefile.am @@ -1,39 +1,45 @@ INCLUDES = -I$(srcdir)/../core -I../core -I$(srcdir)/../buttons \ -I../../libkicker -I$(srcdir)/../../libkicker \ - -I$(top_srcdir)/libkonq -I$(top_srcdir)/kdmlib $(all_includes) + -I$(top_srcdir)/libkonq -I$(top_srcdir)/kdmlib $(DBUS_INCS) $(all_includes) noinst_LTLIBRARIES = libkicker_ui.la libkicker_ui_la_SOURCES = addbutton_mnu.cpp appletitem.ui appletview.ui addapplet.cpp \ addapplet_mnu.cpp appletop_mnu.cpp \ browser_mnu.cpp client_mnu.cpp dirdrop_mnu.cpp \ - nonKDEButtonSettings.ui exe_dlg.cpp k_mnu.cpp k_mnu.skel\ - quickbrowser_mnu.cpp service_mnu.cpp \ - addextension_mnu.cpp extensionop_mnu.cpp \ - recentapps.cpp browser_dlg.cpp \ + nonKDEButtonSettings.ui exe_dlg.cpp k_new_mnu.cpp k_mnu.cpp k_mnu.skel\ + quickbrowser_mnu.cpp service_mnu.cpp kmenuitembase.ui \ + addextension_mnu.cpp extensionop_mnu.cpp k_mnu_stub.cpp \ + recentapps.cpp browser_dlg.cpp itemview.cpp kickoff_bar.cpp \ removeapplet_mnu.cpp removeextension_mnu.cpp removecontainer_mnu.cpp \ removebutton_mnu.cpp popupmenutitle.cpp hidebutton.cpp \ - addappletvisualfeedback.cpp clicklineedit.cpp + addappletvisualfeedback.cpp clicklineedit.cpp flipscrollview.cpp \ + media_watcher.cpp media_watcher.skel mykickoffsearchinterface.cpp query.cpp -libkicker_ui_la_LIBADD = $(top_builddir)/libkonq/libkonq.la $(top_builddir)/kdmlib/libdmctl.la +libkicker_ui_la_LIBADD = $(top_builddir)/libkonq/libkonq.la $(top_builddir)/kdmlib/libdmctl.la \ + $(LIB_KABC) ../interfaces/libkickoffsearch_interfaces.la libkicker_ui_la_METASOURCES = AUTO noinst_HEADERS = addapplet.h appletwidget.h addbutton_mnu.h addapplet_mnu.h appletop_mnu.h \ - browser_mnu.h client_mnu.h dirdrop_mnu.h exe_dlg.h k_mnu.h \ + browser_mnu.h client_mnu.h dirdrop_mnu.h exe_dlg.h k_new_mnu.h k_mnu.h \ quickbrowser_mnu.h service_mnu.h \ addextension_mnu.h extensionop_mnu.h \ - recentapps.h browser_dlg.h \ + recentapps.h browser_dlg.h itemview.h query.h \ removeapplet_mnu.h removeextension_mnu.h removecontainer_mnu.h \ removebutton_mnu.h popupmenutitle.h hidebutton.h \ addappletvisualfeedback.h clicklineedit.h +kicker_ui_data_DATA = default-favs +kicker_ui_datadir = $(kde_datadir)/kicker + removecontainer_mnu.lo: ../../libkicker/kickerSettings.h removeextension_mnu.lo: ../../libkicker/kickerSettings.h addextension_mnu.lo: ../core/extensionSettings.h appletop_mnu.lo: ../../libkicker/kickerSettings.h extensionop_mnu.lo: ../../libkicker/kickerSettings.h k_mnu.lo: ../../libkicker/kickerSettings.h +k_new_mnu.lo: ../../libkicker/kickerSettings.h removecontainer_mnu.lo: ../core/extensionSettings.h removeextension_mnu.lo: ../core/extensionSettings.h service_mnu.lo: ../../libkicker/kickerSettings.h diff --git a/kicker/kicker/ui/addappletvisualfeedback.cpp b/kicker/kicker/ui/addappletvisualfeedback.cpp index 38b48fe8e..acd82a066 100644 --- a/kicker/kicker/ui/addappletvisualfeedback.cpp +++ b/kicker/kicker/ui/addappletvisualfeedback.cpp @@ -51,6 +51,7 @@ AddAppletVisualFeedback::AddAppletVisualFeedback(AppletWidget* widget, m_richText(0), m_dissolveDelta(-1), m_frames(1), + m_moveTimer(0, "m_moveTimer"), m_dirty(false) { setFocusPolicy(NoFocus); diff --git a/kicker/kicker/ui/appletop_mnu.cpp b/kicker/kicker/ui/appletop_mnu.cpp index 59ca9c89e..55c181016 100644 --- a/kicker/kicker/ui/appletop_mnu.cpp +++ b/kicker/kicker/ui/appletop_mnu.cpp @@ -30,6 +30,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "appletop_mnu.h" #include "container_button.h" #include "containerarea.h" +#include "kickerSettings.h" PanelAppletOpMenu::PanelAppletOpMenu(int actions, TQPopupMenu *opMenu, const TQPopupMenu* appletsMenu, const TQString & title, const TQString &icon, @@ -159,6 +160,20 @@ PanelAppletOpMenu::PanelAppletOpMenu(int actions, TQPopupMenu *opMenu, const TQP } } + if ((actions & PanelAppletOpMenu::KMenuEditor)) + { + if (needSeparator) + { + insertSeparator(); + needSeparator = false; + } + + if (KickerSettings::legacyKMenu()) + insertItem(SmallIcon("kickoff"), i18n("Switch to Kickoff Menu Style"), this, TQT_SLOT(toggleLegacy())); + else + insertItem(SmallIcon("about_kde"), i18n("Switch to KDE Menu Style"), this, TQT_SLOT(toggleLegacy())); + } + if ((actions & PanelAppletOpMenu::KMenuEditor) && kapp->authorizeKAction("menuedit")) { if (needSeparator) @@ -205,4 +220,11 @@ void PanelAppletOpMenu::keyPressEvent(TQKeyEvent* e) TQPopupMenu::keyPressEvent(e); } +void PanelAppletOpMenu::toggleLegacy() +{ + KickerSettings::setLegacyKMenu(!KickerSettings::legacyKMenu()); + KickerSettings::writeConfig(); + Kicker::the()->restart(); +} + #include "appletop_mnu.moc" diff --git a/kicker/kicker/ui/appletop_mnu.h b/kicker/kicker/ui/appletop_mnu.h index e0b219552..4b396a83d 100644 --- a/kicker/kicker/ui/appletop_mnu.h +++ b/kicker/kicker/ui/appletop_mnu.h @@ -47,6 +47,9 @@ public: signals: void escapePressed(); +protected slots: + void toggleLegacy(); + protected: void keyPressEvent(TQKeyEvent* e); }; diff --git a/kicker/kicker/ui/browser_mnu.cpp b/kicker/kicker/ui/browser_mnu.cpp index 88ce30713..2250475b4 100644 --- a/kicker/kicker/ui/browser_mnu.cpp +++ b/kicker/kicker/ui/browser_mnu.cpp @@ -332,7 +332,7 @@ void PanelBrowserMenu::initialize() if(_mimemap.count() > 0) { if(!_mimecheckTimer) - _mimecheckTimer = new TQTimer(this); + _mimecheckTimer = new TQTimer(this, "_mimecheckTimer"); connect(_mimecheckTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotMimeCheck())); _mimecheckTimer->start(0); diff --git a/kicker/kicker/ui/default-favs b/kicker/kicker/ui/default-favs new file mode 100644 index 000000000..8cbff626d --- /dev/null +++ b/kicker/kicker/ui/default-favs @@ -0,0 +1,9 @@ +MozillaFirefox.desktop +kde-Kontact.desktop +writer.desktop +kde-amarok.desktop +kde-digikam.desktop +kde-Home.desktop +kde-KControl.desktop +kde-Help.desktop +kde-konsole.desktop diff --git a/kicker/kicker/ui/flipscrollview.cpp b/kicker/kicker/ui/flipscrollview.cpp new file mode 100644 index 000000000..ae96ebcaa --- /dev/null +++ b/kicker/kicker/ui/flipscrollview.cpp @@ -0,0 +1,324 @@ +/***************************************************************** + +Copyright (c) 2006 Will Stephenson + +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 +#include +#include +#include + +#include "itemview.h" +#include "flipscrollview.h" +#include "kickerSettings.h" + +/* Flip scroll steps, as percentage of itemview width to scroll per + * step. Assumes the itemview is scrolled in ten steps */ + +/* slow start, then fast */ +//static const double scrollSteps[] = { 0.05, 0.05, 0.1125, 0.1125, 0.1125, 0.1125, 0.1125, 0.1125, 0.1125, 0.1125 }; + +/* slow fast slow */ +//static const double scrollSteps[] = { 0.05, 0.05, 0.13, 0.13, 0.15, 0.13, 0.13, 0.13, 0.05, 0.05 }; + +/* slow veryfast slow */ +static const double scrollSteps[] = { 0.03, 0.03, 0.147, 0.147, 0.147, 0.147, 0.147, 0.147, 0.03, 0.028 }; +; + +BackFrame::BackFrame( TQWidget *parent ) + : TQFrame( parent ), mouse_inside( false ) +{ + setFrameStyle( TQFrame::NoFrame ); + if ( TQApplication::reverseLayout() ) + left_triangle.load( locate( "data", "kicker/pics/right_triangle.png" ) ); + else + left_triangle.load( locate( "data", "kicker/pics/left_triangle.png" ) ); +} + +void BackFrame::drawContents( TQPainter *p ) +{ + TQColor gray( 230, 230, 230 ); + if ( mouse_inside ) + p->fillRect( 3, 3, width() - 6, height() - 6, colorGroup().color( TQColorGroup::Highlight ) ); + else + p->fillRect( 3, 3, width() - 6, height() - 6, gray ); + p->setPen( gray.dark(110) ); + p->drawRect( 3, 3, width() - 6, height() - 6 ); + + int pixsize = ( width() - 6 ) * 3 / 5; + TQImage i = left_triangle.convertToImage().smoothScale( pixsize, pixsize ); + TQPixmap tri; + tri.convertFromImage( i ); + + p->drawPixmap( ( width() - tri.width() ) / 2, ( height() - tri.height() ) / 2, tri ); +} + +void BackFrame::enterEvent( TQEvent *e ) +{ + mouse_inside = true; + update(); +} + +void BackFrame::leaveEvent( TQEvent *e ) +{ + mouse_inside = false; + update(); +} + +void BackFrame::mousePressEvent ( TQMouseEvent * e ) +{ + emit clicked(); +} + +FlipScrollView::FlipScrollView( TQWidget * parent, const char * name ) + : TQScrollView( parent, name ), mState( StoppedLeft ), mScrollDirection( 1 ), mShowBack( false ) +{ + setVScrollBarMode( TQScrollView::AlwaysOff ); + setHScrollBarMode( TQScrollView::AlwaysOff ); + setFrameStyle( TQFrame::NoFrame ); + mLeftView = new ItemView( this, "left_view" ); + addChild( mLeftView ); + + mRightView = new ItemView( this, "right_view" ); + addChild( mRightView ); + + mTimer = new TQTimer( this, "mTimer" ); + connect( mTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotScrollTimer() ) ); + + connect( mLeftView, TQT_SIGNAL( startService(KService::Ptr) ), + TQT_SIGNAL( startService(KService::Ptr) ) ); + connect( mLeftView, TQT_SIGNAL( startURL(const TQString& ) ), + TQT_SIGNAL( startURL(const TQString& ) ) ); + connect( mLeftView, TQT_SIGNAL( rightButtonPressed(TQListViewItem*,const TQPoint&,int) ), + TQT_SIGNAL( rightButtonPressed(TQListViewItem*,const TQPoint&,int) ) ); + connect( mRightView, TQT_SIGNAL( startService(KService::Ptr) ), + TQT_SIGNAL( startService(KService::Ptr) ) ); + connect( mRightView, TQT_SIGNAL( startURL(const TQString& ) ), + TQT_SIGNAL( startURL(const TQString& ) ) ); + connect( mRightView, TQT_SIGNAL( rightButtonPressed(TQListViewItem*,const TQPoint&,int) ), + TQT_SIGNAL( rightButtonPressed(TQListViewItem*,const TQPoint&,int) ) ); + + // wild hack to make sure it has correct width + mLeftView->setVScrollBarMode( TQScrollView::AlwaysOn ); + mRightView->setVScrollBarMode( TQScrollView::AlwaysOn ); + mLeftView->setVScrollBarMode( TQScrollView::Auto ); + mRightView->setVScrollBarMode( TQScrollView::Auto ); + + mBackrow = new BackFrame( this ); + mBackrow->resize( 24, 100 ); + connect( mBackrow, TQT_SIGNAL( clicked() ), TQT_SIGNAL( backButtonClicked() ) ); +} + +ItemView* FlipScrollView::prepareRightMove() +{ + if ( mState != StoppedLeft ) + { + mTimer->stop(); + ItemView *swap = mLeftView; + mLeftView = mRightView; + mRightView = swap; + moveChild( mLeftView, 0, 0 ); + moveChild( mRightView, width(), 0 ); + mBackrow->hide(); + mRightView->resize( width(), height() ); + mLeftView->resize( width(), height() ); + setContentsPos( 0, 0 ); + } + + mState = StoppedLeft; + mRightView->clear(); + return mRightView; +} + +void FlipScrollView::showBackButton( bool enable ) +{ + kdDebug() << "FlipScrollView::showBackButton " << enable << endl; + mShowBack = enable; +} + +ItemView* FlipScrollView::prepareLeftMove(bool clear) +{ + if ( mState != StoppedRight ) + { + mTimer->stop(); + ItemView *swap = mLeftView; + mLeftView = mRightView; + mRightView = swap; + moveChild( mLeftView, 0, 0 ); + moveChild( mRightView, width(), 0 ); + mRightView->resize( width(), height() ); + mLeftView->resize( width(), height() ); + mBackrow->hide(); + setContentsPos( width(), 0 ); + } + + mState = StoppedRight; + if (clear) + mLeftView->clear(); + return mLeftView; +} + +void FlipScrollView::viewportResizeEvent ( TQResizeEvent * ) +{ + mLeftView->resize( size() ); + mRightView->resize( width() - mBackrow->width(), height() ); + mBackrow->resize( mBackrow->width(), height() ); + resizeContents( width() * 2, height() ); + moveChild( mBackrow, width(), 0 ); + moveChild( mRightView, width() + mBackrow->width(), 0 ); + setContentsPos( 0, 0 ); +} + +ItemView *FlipScrollView::currentView() const +{ + if ( mState == StoppedRight ) + return mRightView; + else + return mLeftView; +} + +ItemView *FlipScrollView::leftView() const +{ + return mLeftView; +} + +ItemView *FlipScrollView::rightView() const +{ + return mRightView; +} + +FlipScrollView::~FlipScrollView() {} + +static const int max_steps = 10; + +void FlipScrollView::slotScrollTimer() +{ + mStepsRemaining--; + assert( mStepsRemaining >= 0 && mStepsRemaining < int(sizeof( scrollSteps ) / sizeof( double )) ); + if (KickerSettings::scrollFlipView()) + scrollBy( ( int )( mScrollDirection * mLeftView->width() * scrollSteps[ mStepsRemaining ] ), 0 ); + else + scrollBy( ( int )( mScrollDirection * mLeftView->width()), 0 ); + + if ( mStepsRemaining == 0 ) + { + if ( mState == ScrollingRight ) + { + mState = StoppedRight; + setContentsPos( width(), 0 ); + } else { + mState = StoppedLeft; + setContentsPos( 0, 0 ); + } + + kdDebug() << "slotScrollTimer " << mShowBack << endl; + + if ( mShowBack ) + { + mBackrow->show(); + if ( mState == StoppedRight ) + { + + if ( TQApplication::reverseLayout() ) + moveChild( mRightView, width(), 0 ); + else + moveChild( mRightView, width() + mBackrow->width(), 0 ); + mRightView->resize( width() - mBackrow->width(), height() ); + mLeftView->resize( width(), height() ); + if ( TQApplication::reverseLayout() ) + moveChild( mBackrow, width() + mRightView->width(), 0 ); + else + moveChild( mBackrow, width(), 0 ); + moveChild( mLeftView, 0, 0 ); + } else + { + moveChild( mRightView, width(), 0 ); + mRightView->resize( width(), height() ); + mLeftView->resize( width() - mBackrow->width(), height() ); + if ( TQApplication::reverseLayout() ) + { + moveChild( mBackrow, mLeftView->width(), 0 ); + moveChild( mLeftView, 0, 0 ); + } + else + { + moveChild( mBackrow, 0, 0 ); + moveChild( mLeftView, mBackrow->width(), 0 ); + } + } + } else + mBackrow->hide(); + + if (!mSelectMenuPath.isEmpty()) { + if (mSelectMenuPath=="kicker:/goup/") { + currentView()->setSelected(currentView()->firstChild(),true); + currentView()->firstChild()->repaint(); + } + else { + TQListViewItem * child = currentView()->firstChild(); + while( child ) { + KMenuItem* kitem = dynamic_cast(child); + if (kitem && kitem->menuPath()==mSelectMenuPath) { + currentView()->setSelected(child,true); + kdDebug() << "child repaint\n"; + child->repaint(); + break; + } + child = child->nextSibling(); + } + } + } + mLeftView->setVScrollBarMode( TQScrollView::Auto ); + mRightView->setVScrollBarMode( TQScrollView::Auto ); + mTimer->stop(); + mLeftView->setMouseMoveSelects( true ); + mRightView->setMouseMoveSelects( true ); + } +} + +void FlipScrollView::flipScroll(const TQString& selectMenuPath) +{ + if ( mState == StoppedLeft ) + { + mState = ScrollingRight; + mScrollDirection = 1; + } + else + { + mState = ScrollingLeft; + mScrollDirection = -1; + } + + mLeftView->setVScrollBarMode( TQScrollView::AlwaysOff ); + mRightView->setVScrollBarMode( TQScrollView::AlwaysOff ); + if (KickerSettings::scrollFlipView()) + mStepsRemaining = max_steps; + else + mStepsRemaining = 1; + mTimer->start( 30 ); + mSelectMenuPath = selectMenuPath; + if (!mSelectMenuPath.isEmpty()) { + mLeftView->setMouseMoveSelects( false ); + mRightView->setMouseMoveSelects( false ); + } +} + +#include "flipscrollview.moc" diff --git a/kicker/kicker/ui/flipscrollview.h b/kicker/kicker/ui/flipscrollview.h new file mode 100644 index 000000000..d2de2ab5e --- /dev/null +++ b/kicker/kicker/ui/flipscrollview.h @@ -0,0 +1,118 @@ +/***************************************************************** + +Copyright (c) 2006 Will Stephenson + +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. + +******************************************************************/ + +/* + * Flip scroll menu + * Each level of the menu is a separate TQListView + * Child items are added to their own TQListView. + * When a parent is clicked, we look up its child menu and insert + * that in a TQScrollView, then scroll to it. + * + * Need to intercept TQListViewItems' parent param and instead of + * inserting directly into parent, insert into parent item's listview + * + * So need + * - adapted QLVI + * - wrap QLV and offer same interface + */ + +#ifndef FLIPSCROLLVIEW_H +#define FLIPSCROLLVIEW_H + +#include +#include +#include +#include +#include +#include +#include "service_mnu.h" + +class ItemView; + +class BackFrame : public TQFrame +{ + Q_OBJECT + +public: + BackFrame( TQWidget *parent ); + virtual void drawContents( TQPainter *p ); + + void enterEvent ( TQEvent * ); + void leaveEvent( TQEvent * ); + void mousePressEvent ( TQMouseEvent * e ); + +signals: + void clicked(); + +private: + TQPixmap left_triangle; + bool mouse_inside; +}; + +class FlipScrollView : public TQScrollView +{ + Q_OBJECT +public: + enum State{ StoppedLeft, StoppedRight, ScrollingLeft, ScrollingRight }; + FlipScrollView( TQWidget * parent = 0, const char * name = 0 ); + ~FlipScrollView(); + + ItemView *currentView() const; + ItemView *leftView() const; + ItemView *rightView() const; + ItemView *prepareLeftMove(bool clear=true); + ItemView *prepareRightMove(); + + void flipScroll(const TQString& selectMenuPath = TQString::null); + void showBackButton(bool enable); + bool showsBackButton() const {return mShowBack;} + +protected slots: + void slotScrollTimer(); + +signals: + void startService(KService::Ptr kservice); + void startURL(const TQString& u); + void rightButtonPressed(TQListViewItem*,const TQPoint&,int); + void backButtonClicked(); + +protected: + void viewportResizeEvent ( TQResizeEvent * ); + +private: + ItemView * mLeftView; + ItemView * mRightView; +// ItemView * mCurrentView; + int mStepsRemaining; + State mState; + TQTimer * mTimer; + BackFrame *mBackrow; + TQString mSelectMenuPath; + int mScrollDirection; + bool mShowBack; +}; + + + + +#endif diff --git a/kicker/kicker/ui/itemview.cpp b/kicker/kicker/ui/itemview.cpp new file mode 100644 index 000000000..dcb4760c1 --- /dev/null +++ b/kicker/kicker/ui/itemview.cpp @@ -0,0 +1,1257 @@ +/***************************************************************** + +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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client_mnu.h" +#include "container_base.h" +#include "global.h" +#include "kbutton.h" +#include "kicker.h" +#include "kickerSettings.h" +#include "konqbookmarkmanager.h" +#include "menuinfo.h" +#include "menumanager.h" +#include "popupmenutitle.h" +#include "quickbrowser_mnu.h" +#include "recentapps.h" + +#include "k_mnu.h" +#include "k_new_mnu.h" +#include "itemview.h" + +// -------------------------------------------------------------------------- + +KMenuItem::~KMenuItem() +{ + ItemView *listview = dynamic_cast( listView() ); + if ( listview && listview->m_lastOne == this) { + listview->m_lastOne = 0; + listview->m_old_contentY = -1; + } +} + +static double pointSize( double pixelSize, TQPaintDevice *w ) +{ + return pixelSize * 72. / TQPaintDevice::x11AppDpiY( w->x11Screen () ); +} + +static int pixelSize( double pixelSize, TQPaintDevice *w ) +{ + return qRound( pixelSize * TQPaintDevice::x11AppDpiY( w->x11Screen () ) / 72. ); +} + +void KMenuItem::init() +{ + setMultiLinesEnabled(true); + m_s = 0; + m_path = TQString::null; + m_icon = TQString::null; + m_menuPath = TQString::null; + setDragEnabled(true); + m_has_children = false; + m_old_width = -1; + if ( TQApplication::reverseLayout() ) + right_triangle.load( locate( "data", "kicker/pics/left_triangle.png" ) ); + else + right_triangle.load( locate( "data", "kicker/pics/right_triangle.png" ) ); +} + +void KMenuItem::setTitle(const TQString& txt) +{ + m_title = txt; + setText( 0, txt ); + setup(); +} + +void KMenuItem::setToolTip(const TQString& txt) +{ + m_tooltip = txt; +} + +void KMenuItem::setDescription(const TQString& txt) +{ + m_description = txt; + setup(); +} + +void KMenuItem::setIcon(const TQString& icon, int size) +{ + m_icon = icon; + TQListViewItem::setPixmap(0, KGlobal::iconLoader()->loadIcon(icon, KIcon::Panel, size )); +} + +void KMenuItem::setHasChildren( bool flag ) +{ + m_has_children = flag; + repaint(); +} + +void KMenuItem::setup() +{ + // if someone configured a larger generalFont than 10pt, he might have a _real_ problem with 7pt + // the 7pt could be read out of konquerorrc I guess + float min_font_size = 7. * QMAX(1., KGlobalSettings::generalFont().pointSizeFloat() / 10.); + + const int expected_height = 38; + description_font_size = QMAX( pointSize( expected_height * .3, listView() ) + KickerSettings::kickoffFontPointSizeOffset(), min_font_size ) ; + title_font_size = QMAX( pointSize( expected_height * .25, listView() ) + KickerSettings::kickoffFontPointSizeOffset(), min_font_size + 1 ); + + //kdDebug() << description_font_size << " " << title_font_size << " " << pointSize( expected_height * .25, listView() ) << endl; + TQListViewItem::setup(); + setHeight( (int)QMAX( expected_height, pixelSize( title_font_size + description_font_size * 2.3, listView()))); +} + +void KMenuItem::paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align) +{ + ItemView *listview = static_cast( listView() ); + int bottom = listView()->itemRect( this ).bottom(); + int diff = bottom - listView()->viewport()->height(); + + KPixmap pm; + pm.resize( width, height() ); + TQPainter pp( &pm ); + paintCellInter( &pp, cg, column, width, align ); + pp.end(); + + if ( diff > 0 && diff <= height() ) // cut off + { + pm.resize( width, height() - diff ); + KPixmapEffect::blend( pm, float( diff ) / height(), + cg.color( TQColorGroup::Background ), + KPixmapEffect::VerticalGradient ); + p->drawPixmap( 0, 0, pm ); + if ( listview->m_lastOne != this ) + { + listview->m_lastOne = this; + listview->m_old_contentY = -1; + } + } + else + { + p->drawPixmap( 0, 0, pm ); + if ( this == listview->m_lastOne ) { + if ( bottom < 0 ) + listview->m_lastOne = static_cast( itemAbove() ); + else + listview->m_lastOne = static_cast( itemBelow() ); + listview->m_old_contentY = -1; + repaint(); + } + } +} + +void KMenuItem::makeGradient( KPixmap &off, const TQColor &c ) +{ + KPixmap blend; + blend.resize( off.width() / 3, off.height() ); + bitBlt( &blend, 0, 0, &off, off.width() - blend.width(), 0, blend.width(), blend.height() ); + KPixmapEffect::blend( blend, 0.2, c, KPixmapEffect::HorizontalGradient ); + TQPainter p( &off ); + p.drawPixmap( off.width() - blend.width(), 0, blend ); + p.end(); +} + +void KMenuItem::paintCellInter(TQPainter* p, const TQColorGroup & cg, int column, int width, int align) +{ + const bool reverseLayout = TQApplication::reverseLayout(); + + const BackgroundMode bgmode = listView()->viewport()->backgroundMode(); + const TQColorGroup::ColorRole crole = TQPalette::backgroundRoleFromMode( bgmode ); + TQColor backg = cg.color( crole ); + + if ( isSelected() ) + backg = cg.color( TQColorGroup::Highlight ); + p->fillRect( 0, 0, width, height(), backg ); + + TQFontMetrics fm( p->fontMetrics() ); + + int pixsize = 32; + if ( height() < 36 ) + pixsize = 16; + const int left_margin = 30; + const int margin = 3; + +// p->drawText( 2, 2, left_margin - 2, height(), align, TQString::number( childCount () ) ); + + const TQPixmap * pix = pixmap( column ); + + if ( pix ) + { + TQPixmap pix32 = *pix; + + if ( pix->width() > pixsize ) + { + TQImage i = pix->convertToImage().smoothScale( pixsize, pixsize ); + pix32.convertFromImage( i ); + } + if ( reverseLayout ) + p->drawPixmap( width - ( (pixsize - pix32.width()) / 2 + left_margin ) - pix32.width(), + ( height() - pix32.height() ) / 2, pix32 ); + else + p->drawPixmap( (pixsize - pix32.width()) / 2 + left_margin, + ( height() - pix32.height() ) / 2, pix32 ); + } + + if ( m_title.isEmpty() ) + return; + + int r = left_margin + pixsize + margin * 2; + + TQFont f1 = p->font(); + f1.setPointSizeFloat( title_font_size ); + f1.setWeight( TQFont::Normal ); // TQFont::DemiBold == 63 + + TQFont f2 = p->font(); + f2.setPointSizeFloat( description_font_size ); + f2.setWeight( TQFont::Light ); + + int f1h = TQFontMetrics( f1 ).height(); + int f2h = TQFontMetrics( f2 ).height(); + + const int text_margin = 2; + int spacing = ( height() - f1h - f2h - text_margin ) / 2; + if ( m_description.isEmpty() ) + spacing = ( height() - f1h ) / 2; + + int right_triangle_size = pixelSize( 7, listView() ); + + int right_margin = listView()->verticalScrollBar()->width(); + if ( m_has_children ) + right_margin += right_triangle_size * 2; + + KPixmap off; + TQPainter pp; + + off.resize( width-text_margin-r-right_margin, height() ); + pp.begin( &off ); + pp.fillRect( 0, 0, off.width(), off.height(), backg ); + + if (isSelected()) + pp.setPen( cg.color( TQColorGroup::HighlightedText ) ); + else + pp.setPen( cg.color( TQColorGroup::Text ) ); + + pp.setFont( f1 ); + pp.drawText( 0, 0, off.width(), off.height(), align, m_title ); + pp.end(); + if ( TQFontMetrics( f1 ).width( m_title ) > off.width() ) + { + makeGradient( off, backg ); + if ( !m_description.isEmpty() ) + setToolTip( m_title + "

" + m_description ); + else + setToolTip( m_title ); + } + if ( reverseLayout ) + p->drawPixmap( width - off.width() - r, spacing, off ); + else + p->drawPixmap( r, spacing, off ); + + if ( !m_description.isEmpty() ) + { + pp.begin( &off ); + pp.fillRect( 0, 0, off.width(), off.height(), backg ); + + TQColor myColor = cg.color( TQColorGroup::Text ).light( 200 ); + if ( qGray( myColor.rgb() ) == 0 ) + myColor = TQColor( 100, 100, 110 ); + pp.setPen( myColor ); + pp.setPen( isSelected() ? cg.color( TQColorGroup::Mid ) : myColor ); + pp.setFont( f2 ); + pp.drawText( 0, 0, off.width(), off.height(), align, m_description ); + pp.end(); + if ( TQFontMetrics( f2 ).width( m_description ) > off.width() ) + { + makeGradient( off, backg ); + setToolTip( m_title + "

" + m_description ); + } + if ( reverseLayout ) + p->drawPixmap( width - off.width() - r, spacing + text_margin + f1h, off ); + else + p->drawPixmap( r, spacing + text_margin + f1h, off ); + } + + if ( m_has_children ) + { + TQImage i = right_triangle.convertToImage().smoothScale( right_triangle_size, + right_triangle_size ); + TQPixmap tri; + tri.convertFromImage( i ); + + if ( reverseLayout ) + p->drawPixmap( right_margin - tri.width(), ( height() - f1h ) / 2, tri ); + else + p->drawPixmap( listView()->width() - right_margin, ( height() - f1h ) / 2, tri ); + } + + if ( m_old_width != width ) + { + // the listview caches paint events + m_old_width = width; + repaint(); + } +} + +// -------------------------------------------------------------------------- + +KMenuItemSeparator::KMenuItemSeparator(int nId, TQListView* parent) + : KMenuItem(nId, parent), lv(parent), cached_width( 0 ) +{ + setEnabled(false); + left_margin = 15; +} + +void KMenuItemSeparator::setup() +{ + KMenuItem::setup(); + + TQFont f = TQFont(); + TQFontMetrics fm(f); + f.setPointSize( 8 + KickerSettings::kickoffFontPointSizeOffset() ); + if ( itemAbove() && !text( 0 ).isEmpty() ) + setHeight( (int)QMAX( 34., fm.height() * 1.4) ); + else + setHeight( (int)QMAX( 26., fm.height() * 1.4 ) ); +} + +void KMenuItemSeparator::setLink( const TQString &text, const TQString &url ) +{ + m_link_text = text; + m_link_url = url; + m_link_rect = TQRect(); +} + +bool KMenuItemSeparator::hitsLink( const TQPoint &pos ) +{ + return m_link_rect.contains( pos ); +} + +void KMenuItemSeparator::preparePixmap( int width ) +{ + if ( cached_width != width ) + { + pixmap.load( locate("data", "kicker/pics/menu_separator.png" ) ); + TQImage i = pixmap.convertToImage().smoothScale( width - 15 - left_margin, pixmap.height() ); + pixmap.convertFromImage( i ); + cached_width = width; + } +} + +void KMenuItemSeparator::paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align) +{ + preparePixmap(width); + + const int h = height(); + + if (text(0).isEmpty()) { + KMenuItem::paintCell(p, cg, column, width, align); + p->drawPixmap( 15 , h/2, pixmap ); + } + else { + const BackgroundMode bgmode = lv->viewport()->backgroundMode(); + const TQColorGroup::ColorRole crole = TQPalette::backgroundRoleFromMode( bgmode ); + p->fillRect( 0, 0, width, h, cg.brush( crole ) ); + + int margin = 0; + if ( itemAbove() ) { + p->drawPixmap( 15 , h/4, pixmap ); + margin = h / 4; + } + TQFont f = listView()->font(); + f.setWeight( TQFont::Normal ); + f.setPointSize( 8 + KickerSettings::kickoffFontPointSizeOffset() ); + p->setFont( f ); + TQColor myColor = cg.color( TQColorGroup::Text ).light( 200 ); + if ( qGray( myColor.rgb() ) == 0 ) + myColor = TQColor( 100, 100, 110 ); + p->setPen( myColor ); + int twidth = p->fontMetrics().width(text(0)); + int lwidth = 0; + int swidth = 0; + int fwidth = 0; + + if ( !m_link_text.isEmpty() ) + { + swidth = p->fontMetrics().width( " (" ); + lwidth = p->fontMetrics().width(m_link_text ); + fwidth = p->fontMetrics().width( ")" ); + } + int pos = int(lv->width() * 0.9 - twidth - swidth - lwidth - fwidth); + p->drawText( pos, margin + 5, + width, h - ( margin +5 ), AlignTop, text(0) ); + if ( !m_link_text.isEmpty() ) + { + pos += twidth; + p->drawText( pos, margin + 5, + width, h - ( margin +5 ), AlignTop, " (" ); + pos += swidth; + p->setPen( cg.color( TQColorGroup::Link ) ); + f.setUnderline( true ); + p->setFont( f ); + p->drawText( pos, margin + 5, + width, h - ( margin +5 ), AlignTop, m_link_text ); + m_link_rect = TQRect( pos, margin + 5, lwidth, p->fontMetrics().height() ); + pos += lwidth; + f.setUnderline( false ); + p->setFont( f ); + p->drawText( pos, margin + 5, + width, h - ( margin +5 ), AlignTop, ")" ); + } + } +} + +KMenuItemHeader::KMenuItemHeader(int nId, const TQString& relPath, TQListView* parent) + : KMenuItemSeparator(nId, parent) +{ + setEnabled( false ); + TQString path; + if (relPath.startsWith( "new/" /*"kicker:/new/"*/ )) { + paths.append( "kicker:/goup/" ); + texts.append( i18n("New Applications") ); + icons.append( "clock" ); + } + else if (relPath == "kicker:/restart/") { + texts.append( i18n("Restart Computer") ); + } + else if (relPath == "kicker:/switchuser/") { + texts.append( i18n("Switch User") ); + } + else { + KServiceGroup::Ptr subMenuRoot = KServiceGroup::group(relPath); + TQStringList items = TQStringList::split( '/', relPath ); + for ( TQStringList::ConstIterator it = items.begin(); it != items.end(); ++it ) + { + path += *it + "/"; + paths.append( "kicker:/goup/" + path ); + KServiceGroup::Ptr subMenuRoot = KServiceGroup::group(path); + TQString groupCaption = subMenuRoot->caption(); + texts.append( groupCaption ); + icons.append( subMenuRoot->icon() ); + } + } + + setPath( "kicker:/goup/" + path ); // the last wins for now + left_margin = 10; +} + +void KMenuItemHeader::setup() +{ + KMenuItem::setup(); + + TQFontMetrics fm( listView()->font() ); + setHeight( QMAX( int( texts.count() * fm.height() + ( texts.count() + 1 ) * 2 + 10 ), height()) ); + // nada +} + +void KMenuItemHeader::paintCell(TQPainter* p, const TQColorGroup & cg, int , int width, int align ) +{ + preparePixmap(width); + + const BackgroundMode bgmode = listView()->viewport()->backgroundMode(); + const TQColorGroup::ColorRole crole = TQPalette::backgroundRoleFromMode( bgmode ); + + TQBrush br = cg.brush( crole ); + if ( isSelected() ) { + br = cg.brush( TQColorGroup::Highlight ); + p->fillRect( 0, 0, width, height() - 3, br ); + } else { + p->fillRect( 0, 0, width, height(), br ); + } + + TQFontMetrics fm( p->fontMetrics() ); + const int left_margin = 10; + + const int margin = 3; + + int r = left_margin + margin * 2; + + const int min_font_size = 7; + int title_font_pixelSize = qRound( pixelSize( QMAX( pointSize( 12, listView() ) + KickerSettings::kickoffFontPointSizeOffset(), min_font_size + 1 ), listView() ) ); + + TQFont f1 = p->font(); + f1.setPixelSize( title_font_pixelSize ); + p->setFont( f1 ); + int f1h = TQFontMetrics( f1 ).height(); + + p->setPen( cg.color( TQColorGroup::Text ) ); + + const int text_margin = 2; + int spacing = ( height() - texts.count() * f1h - QMAX( texts.count() - 1, 0 ) * text_margin ) / 2; + + for ( uint i = 0; i < texts.count(); ++i ) + { + if (i==texts.count()-1) { + f1.setWeight( TQFont::DemiBold ); + p->setFont( f1 ); + f1h = TQFontMetrics( f1 ).height(); + } + + p->drawText( r, spacing, width-text_margin-r, height(), align, texts[i] ); + spacing += text_margin + f1h; + r += title_font_pixelSize; + } + + p->drawPixmap( left_margin , height() - 2, pixmap ); +} + +KMenuSpacer::KMenuSpacer(int nId, TQListView* parent) + : KMenuItem(nId, parent) +{ + setEnabled(false); +} + +void KMenuSpacer::setup() +{ + // nada +} + +void KMenuSpacer::paintCell(TQPainter* p, const TQColorGroup & cg, int , int width, int ) +{ + const BackgroundMode bgmode = listView()->viewport()->backgroundMode(); + const TQColorGroup::ColorRole crole = TQPalette::backgroundRoleFromMode( bgmode ); + TQBrush br = cg.brush( crole ); + + p->fillRect( 0, 0, width, height(), br ); +} + +void KMenuSpacer::setHeight( int i ) +{ + KMenuItem::setHeight( i ); +} + +class ItemViewTip : public TQToolTip +{ +public: + ItemViewTip( TQWidget *parent, TQListView *lv ); + + void maybeTip( const TQPoint &pos ); + +private: + TQListView *view; + +}; + +ItemViewTip::ItemViewTip( TQWidget *parent, TQListView *lv ) + : TQToolTip( parent ), view( lv ) +{ +} + +void ItemViewTip::maybeTip( const TQPoint &pos ) +{ + KMenuItem *item = dynamic_cast( view->itemAt( pos ) ); + TQPoint contentsPos = view->viewportToContents( pos ); + if ( !item ) + return; + + if ( item->toolTip().isNull() ) + return; + + TQRect r = view->itemRect( item ); + int headerPos = view->header()->sectionPos( 0 ); + r.setLeft( headerPos ); + r.setRight( headerPos + view->header()->sectionSize( 0 ) ); + tip( r, item->toolTip() ); +} + +// -------------------------------------------------------------------------- + +ItemView::ItemView(TQWidget* parent, const char* name) + : KListView(parent, name), m_spacer( 0 ), + m_mouseMoveSelects(true), m_iconSize(32) +{ + setHScrollBarMode( TQScrollView::AlwaysOff ); + setFrameStyle( TQFrame::NoFrame ); + setSelectionMode(TQListView::Single); + addColumn(""); + header()->setStretchEnabled(1, 0); + //setColumnWidthMode(0, TQListView::Maximum); + header()->hide(); + setMouseTracking(true); + setItemMargin(0); + setSorting(-1); + setTreeStepSize(38); + setFocusPolicy(TQWidget::NoFocus); + + m_lastOne = 0; + m_old_contentY = -1; + + connect(this, TQT_SIGNAL(mouseButtonClicked( int, TQListViewItem*, const TQPoint &, int )), + TQT_SLOT(slotItemClicked(int, TQListViewItem*, const TQPoint &, int))); + + connect(this, TQT_SIGNAL(returnPressed(TQListViewItem*)), TQT_SLOT(slotItemClicked(TQListViewItem*))); + connect(this, TQT_SIGNAL(spacePressed(TQListViewItem*)), TQT_SLOT(slotItemClicked(TQListViewItem*))); + + new ItemViewTip( viewport(), this ); +} + +KMenuItemHeader *ItemView::insertHeader(int id, const TQString &relpath) +{ + KMenuItemHeader *newItem = new KMenuItemHeader(id, relpath, this ); + moveItemToIndex(newItem, 1); + setBackPath( "kicker:/goup/" + relpath ); // the last wins for now + + return newItem; +} + +KMenuItem* ItemView::findItem(int nId) +{ + for (TQListViewItemIterator it(this); it.current(); ++it) + { + if(static_cast(it.current())->id() == nId) + return static_cast(it.current()); + } + + return 0L; +} + +bool ItemView::focusNextPrevChild(bool /*next*/) +{ + return false; +} + +KMenuItem* ItemView::itemAtIndex(int nIndex) +{ + if(nIndex <= 0) + return 0L; + + if(nIndex >= childCount()) + return static_cast(lastItem()); + + int i = 1; + TQListViewItemIterator it(this); + for (;it.current(); ++i, ++it) { + if(i == nIndex) + return static_cast(it.current()); + } + + return static_cast(lastItem()); +} + +KMenuItem* ItemView::insertItem( const TQString& icon, const TQString& text, const TQString& description, const + TQString& path, int nId, int nIndex, KMenuItem *parent) +{ + KMenuItem* newItem = findItem(nId); + + if(!newItem && parent) + newItem = new KMenuItem(nId, parent ); + else if ( !newItem ) + newItem = new KMenuItem(nId, this ); + + newItem->setIcon(icon, m_iconSize); + newItem->setTitle(text); + newItem->setDescription(description); + newItem->setPath(path); + + if (nIndex==-1) + nIndex=childCount(); + + moveItemToIndex(newItem, nIndex); + + return newItem; +} + +KMenuItem* ItemView::insertItem( const TQString& icon, const TQString& text, const TQString& description, + int nId, int nIndex, KMenuItem *parent) +{ + return insertItem( icon, text, description, TQString::null, nId, nIndex, parent); +} + +int ItemView::setItemEnabled(int id, bool enabled) +{ + KMenuItem* item = findItem(id); + + if(item) + item->setEnabled(enabled); + + return 0; +} + +KMenuItemSeparator *ItemView::insertSeparator(int nId, const TQString& text, int nIndex) +{ + KMenuItemSeparator *newItem = new KMenuItemSeparator(nId, this); + + newItem->setText(0, text); + + if (nIndex==-1) + nIndex=childCount(); + + moveItemToIndex(newItem, nIndex); + return newItem; +} + +void ItemView::moveItemToIndex(KMenuItem* item, int nIndex) +{ + + if (nIndex <= 0) { + takeItem(item); + KListView::insertItem(item); + } + else { + item->moveItem(itemAtIndex(nIndex)); + } +} + +void ItemView::slotMoveContent() +{ + if ( !m_spacer ) + return; + + int item_height = 0; + TQListViewItemIterator it( this ); + while ( it.current() ) { + if ( !dynamic_cast( it.current() ) && !it.current()->parent() && it.current()->isVisible() ) { + it.current()->invalidateHeight(); + item_height += it.current()->totalHeight(); + } + ++it; + } + + if ( height() > item_height ) + m_spacer->setHeight( height() - item_height ); + else + m_spacer->setHeight( 0 ); +} + +KMenuItem *ItemView::insertMenuItem(KService::Ptr& s, int nId, int nIndex, KMenuItem* parentItem, + const TQString& aliasname, const TQString & label, const TQString & categoryIcon ) +{ + if (!s) + return 0; + + TQString serviceName = aliasname.isEmpty() ? s->name() : aliasname; + + kdDebug() << "insertMenuItem " << nId << " " << nIndex << " " << s->name() << endl; + KMenuItem* newItem = 0; //findItem(nId); + if(!newItem) + newItem = parentItem ? new KMenuItem(nId, parentItem) : new KMenuItem(nId, this); + + newItem->setIcon(s->icon()=="unknown" ? categoryIcon : s->icon(), m_iconSize); + if ((KickerSettings::DescriptionAndName || KickerSettings::menuEntryFormat() + == KickerSettings::DescriptionOnly) && !s->genericName().isEmpty()) { + newItem->setTitle(s->genericName()); + newItem->setDescription(label.isEmpty() ? serviceName : label); + } + else { + newItem->setTitle(label.isEmpty() ? serviceName : label); + newItem->setDescription(s->genericName()); + } + newItem->setService(s); + + if (nIndex==-2) + return newItem; + + if (nIndex==-1) + nIndex=childCount(); + + moveItemToIndex(newItem, nIndex); + + return newItem; +} + +KMenuItem* ItemView::insertDocumentItem(const TQString& s, int nId, int nIndex, const TQStringList* /*suppressGenericNames*/, + const TQString& /*aliasname*/) +{ + KMenuItem* newItem = findItem(nId); + + if(!newItem) + newItem = new KMenuItem(nId, this); + + KMimeType::Ptr mt = KMimeType::findByURL( s ); + newItem->setIcon(KMimeType::iconForURL( s ), m_iconSize); + newItem->setTitle(s); + newItem->setDescription(mt->comment()); + newItem->setPath(s); + + if (nIndex==-1) + nIndex=childCount(); + + moveItemToIndex(newItem, nIndex); + + return newItem; +} + +KMenuItem* ItemView::insertRecentlyItem(const TQString& s, int nId, int nIndex) +{ + KDesktopFile f(s, true /* read only */); + + KMenuItem* newItem = findItem(nId); + + if(!newItem) + newItem = new KMenuItem(nId, this); + + newItem->setIcon(f.readIcon(), m_iconSize); + + // work around upstream fixed bug + TQString name=f.readName(); + if (name.isEmpty()) + name=f.readURL(); + + newItem->setTitle(name); + + TQString comment = f.readComment(); + if (comment.isEmpty()) { + KURL url(f.readURL()); + if (!url.host().isEmpty()) + comment = i18n("Host: %1").arg(url.host()); + } + + newItem->setDescription(comment); + newItem->setPath(s); + + if (nIndex==-1) + nIndex=childCount(); + + moveItemToIndex(newItem, nIndex); + + return newItem; +} + +int ItemView::insertItem(PopupMenuTitle*, int, int) +{ + return 0; +} + +KMenuItem* ItemView::insertSubItem(const TQString& icon, const TQString& caption, const TQString& description, const TQString& path, KMenuItem* parentItem) +{ +#warning FIXME + KMenuItem* newItem = parentItem ? new KMenuItem(-1, parentItem) : new KMenuItem(-1, this); + newItem->setTitle(caption); + newItem->setDescription(description); + newItem->setIcon(icon, m_iconSize); + newItem->setPath(path); + + return newItem; +} + + + +void ItemView::slotItemClicked(int button, TQListViewItem * item, const TQPoint & /*pos*/, int /*c*/ ) +{ + if (button==1) + slotItemClicked(item); +} + +void ItemView::slotItemClicked(TQListViewItem* item) +{ + KMenuItem* kitem = dynamic_cast(item); + if ( !kitem ) + return; + + if(kitem->service()) { + emit startService(kitem->service()); + } + else if(!kitem->path().isEmpty()) { + emit startURL(kitem->path()); + } +} + +void ItemView::contentsMousePressEvent ( TQMouseEvent * e ) +{ + KListView::contentsMousePressEvent( e ); + + TQPoint vp = contentsToViewport(e->pos()); + KMenuItemSeparator *si = dynamic_cast( itemAt( vp ) ); + if ( si ) + { + if ( si->hitsLink( vp - itemRect(si).topLeft() ) ) + emit startURL( si->linkUrl() ); + } +} + +void ItemView::contentsMouseMoveEvent(TQMouseEvent *e) +{ + TQPoint vp = contentsToViewport(e->pos()); + TQListViewItem * i = itemAt( vp ); + + bool link_cursor = false; + KMenuItemSeparator *si = dynamic_cast( i ); + if ( si ) + link_cursor = si->hitsLink( vp - itemRect(si).topLeft() ); + + if (i && !i->isSelectable() && !link_cursor) { + unsetCursor(); + viewport()->unsetCursor(); + return; + } + + KListView::contentsMouseMoveEvent(e); + + if (m_mouseMoveSelects) { + if(i && i->isEnabled() && !i->isSelected() && + // FIXME: This is wrong if you drag over the items. + (e->state() & (LeftButton|MidButton|RightButton)) == 0) + KListView::setSelected(i, true); + else if (!i && selectedItem()) + KListView::setSelected(selectedItem(), false); + } + + if ( link_cursor ) + setCursor( Qt::PointingHandCursor ); + else + unsetCursor(); + +} + +void ItemView::leaveEvent(TQEvent* e) +{ + KListView::leaveEvent(e); + + clearSelection(); +} + +void ItemView::resizeEvent ( TQResizeEvent * e ) +{ + KListView::resizeEvent( e ); +// if ( m_lastOne ) +// int diff = itemRect( m_lastOne ).bottom() - viewport()->height(); +} + +void ItemView::viewportPaintEvent ( TQPaintEvent * pe ) +{ + //kdDebug() << "viewportPaintEvent " << pe->rect() << " " << contentsY () << " " << m_old_contentY << endl; + KListView::viewportPaintEvent( pe ); + + if ( m_lastOne && m_old_contentY != contentsY() ) { + m_old_contentY = contentsY(); + m_lastOne->repaint(); + } +} + +void ItemView::clear() +{ + KListView::clear(); + m_lastOne = 0; + m_old_contentY = -1; + m_back_url = TQString::null; +} + +void ItemView::contentsWheelEvent(TQWheelEvent *e) +{ + KListView::contentsWheelEvent(e); + + TQPoint vp = contentsToViewport(e->pos()); + TQListViewItem * i = itemAt( vp ); + + if(i && i->isEnabled() && !i->isSelected() && + // FIXME: This is wrong if you drag over the items. + (e->state() & (LeftButton|MidButton|RightButton)) == 0) + KListView::setSelected(i, true); + else if (!i && selectedItem()) + KListView::setSelected(selectedItem(), false); +} + +TQDragObject * ItemView::dragObject() +{ + KMultipleDrag* o = 0; + TQListViewItem *item = itemAt( viewport()->mapFromGlobal(TQCursor::pos()) ); + if ( item ) { + KMenuItem* kitem = static_cast(item); + + if (dynamic_cast(item)) + return 0; + + o = new KMultipleDrag(viewport()); + TQPixmap pix = KGlobal::iconLoader()->loadIcon( kitem->icon(), KIcon::Panel, m_iconSize); + TQPixmap add = KGlobal::iconLoader()->loadIcon( "add", KIcon::Small ); + + TQPainter p( &pix ); + p.drawPixmap(pix.height()-add.height(), pix.width()-add.width(), add); + p.end(); + + TQBitmap mask; + + if (pix.mask()) + mask = *pix.mask(); + else { + mask.resize(pix.size()); + mask.fill(Qt::color1); + } + + bitBlt( &mask, pix.width()-add.width(), pix.height()-add.height(), add.mask(), 0, 0, add.width(), add.height(), OrROP ); + pix.setMask( mask ); + o->setPixmap(pix); + + if(kitem->service()) { + // If the path to the desktop file is relative, try to get the full + // path from KStdDirs. + TQString path = kitem->service()->desktopEntryPath(); + path = locate("apps", path); + o->addDragObject(new KURLDrag(KURL::List(KURL(path)), 0)); + } + else if (kitem->path().startsWith("kicker:/new") || kitem->path().startsWith("system:/") + || kitem->path().startsWith("kicker:/switchuser_") || kitem->path().startsWith("kicker:/restart_")) { + delete o; + return 0; + } + else if (kitem->hasChildren()) { + o->addDragObject(new KURLDrag(KURL::List(KURL("programs:/"+kitem->menuPath())), 0)); + return o; + } + else if(!kitem->path().isEmpty() && !kitem->path().startsWith("kicker:/") && !kitem->path().startsWith("kaddressbook:/")) { + TQString uri = kitem->path(); + + if (uri.startsWith(locateLocal("data", TQString::fromLatin1("RecentDocuments/")))) { + KDesktopFile df(uri,true); + uri=df.readURL(); + } + + o->addDragObject(new KURLDrag(KURL::List(KURL(uri)), 0)); + } + + o->addDragObject(new KMenuItemDrag(*kitem,this)); + } + return o; +} + +int ItemView::goodHeight() +{ + int item_height = 0; + TQListViewItemIterator it( this ); + while ( it.current() ) { + if ( !dynamic_cast( it.current() ) && !it.current()->parent() && it.current()->isVisible() ) { + item_height += it.current()->height(); + } + ++it; + } + + return item_height; +} + + +KMenuItemDrag::KMenuItemDrag(KMenuItem& item, TQWidget *dragSource) + : TQDragObject(dragSource, 0) +{ + TQBuffer buff(a); + buff.open(IO_WriteOnly); + TQDataStream s(&buff); + + s << item.id() << (item.service() ? item.service()->storageId() : TQString::null) + << item.title() << item.description() << item.icon() << item.path(); +} + +KMenuItemDrag::~KMenuItemDrag() +{ +} + +const char * KMenuItemDrag::format(int i) const +{ + if (i == 0) + return "application/kmenuitem"; + + return 0; +} + +TQByteArray KMenuItemDrag::encodedData(const char* mimeType) const +{ + if (TQString("application/kmenuitem") == mimeType) + return a; + + return TQByteArray(); +} + +bool KMenuItemDrag::canDecode(const TQMimeSource * e) +{ + if (e->provides( "application/kmenuitem" ) ) + return true; + + return false; +} + +bool ItemView::acceptDrag (TQDropEvent* event) const +{ + if ( !acceptDrops() ) + return false; + + if (KMenuItemDrag::canDecode(event)) + return true; + + if (TQTextDrag::canDecode(event)) { + TQString text; + TQTextDrag::decode(event,text); + return !text.startsWith("programs:/"); + } + + return itemsMovable(); +} + +bool KMenuItemDrag::decode(const TQMimeSource* e, KMenuItemInfo& item) +{ + TQByteArray a = e->encodedData("application/kmenuitem"); + + if (a.isEmpty()) { + TQStringList l; + bool ret = TQUriDrag::decodeToUnicodeUris( e, l ); + if ( ret ) + { + for ( TQStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) + { + TQString url = *it; + kdDebug () << "Url " << url << endl; + item.m_path = KURL( url ).path(); + if ( KDesktopFile::isDesktopFile( item.m_path ) ) + { + KDesktopFile df( item.m_path, true ); + item.m_description = df.readGenericName(); + item.m_icon = df.readIcon(); + item.m_title = df.readName(); + } + else + { + item.m_title = item.m_path; + item.m_icon = KMimeType::iconForURL( url ); + item.m_title = item.m_path.section( '/', -1, -1 ); + int last_slash = url.findRev ('/', -1); + if (last_slash == 0) + item.m_description = i18n("Directory: /)"); + else + item.m_description = i18n("Directory: ") + url.section ('/', -2, -2); + } + + return true; + } + } + return false; + } + + TQBuffer buff(a); + buff.open(IO_ReadOnly); + TQDataStream s(&buff); + + KMenuItemInfo i; + TQString storageId; + s >> i.m_id >> storageId >> i.m_title >> i.m_description >> i.m_icon >> i.m_path; + + i.m_s = storageId.isEmpty() ? 0 : KService::serviceByStorageId(storageId); + item = i; + + return true; +} + +FavoritesItemView::FavoritesItemView(TQWidget* parent, const char* name) + : ItemView(parent, name) +{ +} + +bool FavoritesItemView::acceptDrag (TQDropEvent* event) const +{ + if (event->source()==this->viewport()) + return true; + + if (KMenuItemDrag::canDecode(event)) { + KMenuItemInfo item; + KMenuItemDrag::decode(event,item); + TQStringList favs = KickerSettings::favorites(); + + if (item.m_s) + return favs.find(item.m_s->storageId())==favs.end(); + else { + TQStringList::Iterator it; + + TQString uri = item.m_path; + + if (uri.startsWith(locateLocal("data", TQString::fromLatin1("RecentDocuments/")))) { + KDesktopFile df(uri,true); + uri=df.readURL(); + } + + for (it = favs.begin(); it != favs.end(); ++it) { + if ((*it)[0]=='/') { + KDesktopFile df((*it),true); + if (df.readURL().replace("file://",TQString::null)==uri) + break; + } + } + return it==favs.end(); + } + } + + if (TQTextDrag::canDecode(event)) { + TQString text; + TQTextDrag::decode(event,text); + TQStringList favs = KickerSettings::favorites(); + + if (text.endsWith(".desktop")) { + KService::Ptr p = KService::serviceByDesktopPath(text.replace("file://",TQString::null)); + return (p && favs.find(p->storageId())==favs.end()); + } + else { + TQStringList::Iterator it; + for (it = favs.begin(); it != favs.end(); ++it) { + if ((*it)[0]=='/') { + KDesktopFile df((*it),true); + if (df.readURL().replace("file://",TQString::null)==text) + break; + } + } + return it==favs.end(); + } + } + + return itemsMovable(); +} + +#include "itemview.moc" + +// vim:cindent:sw=4: diff --git a/kicker/kicker/ui/itemview.h b/kicker/kicker/ui/itemview.h new file mode 100644 index 000000000..c8b95ee42 --- /dev/null +++ b/kicker/kicker/ui/itemview.h @@ -0,0 +1,260 @@ +/***************************************************************** + +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 __itemview_h__ +#define __itemview_h__ + +#include +#include +#include +#include +#include +#include +#include + +#include "kmenubase.h" +#include "kmenuitembase.h" +#include "service_mnu.h" + +class KickerClientMenu; +class KBookmarkMenu; +class KActionCollection; +class KBookmarkOwner; +class Panel; +class TQWidgetStack; +class KHistoryCombo; +class TQScrollView; +class PopupMenuTitle; +class TQWidget; +class TQVBoxLayout; +class TQTimer; +class KPixmap; + +class KMenuItem : public TQListViewItem +{ +public: + KMenuItem(int nId, TQListView* parent) : TQListViewItem(parent), m_id(nId) { init(); } + KMenuItem(int nId, TQListViewItem* parent) : TQListViewItem(parent), m_id(nId) { init(); } + ~KMenuItem(); + + void setIcon(const TQString& icon, int size); + TQString icon() const { return m_icon; } + void setTitle( const TQString& text ); + TQString title() const { return m_title; } + void setToolTip( const TQString& text ); + TQString toolTip() const { return m_tooltip; } + void setDescription(const TQString& text); + TQString description() const { return m_description; } + void setService(KService::Ptr& s) { m_s = s; } + KService::Ptr service() { return m_s; } + void setPath(const TQString& u) { m_path = u; } + TQString path() const { return m_path; } + void setMenuPath(const TQString& u) { m_menuPath = u; } + TQString menuPath() const { return m_menuPath; } + int id() const { return m_id; } + void setHasChildren(bool flag); + bool hasChildren() const { return m_has_children; } + void makeGradient(KPixmap &off, const TQColor& c); + +protected: + virtual void paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align); + virtual void paintCellInter(TQPainter* p, const TQColorGroup & cg, int column, int width, int align); + virtual void setup(); + +private: + void init(); + + int m_id; + KService::Ptr m_s; + TQString m_title; + TQString m_description; + TQString m_path; + TQString m_icon; + TQString m_tooltip; + TQString m_menuPath; + float title_font_size; + float description_font_size; + bool m_has_children; + int m_old_width; + TQPixmap right_triangle; +}; + +class KMenuItemSeparator : public KMenuItem +{ +public: + KMenuItemSeparator(int nId, TQListView* parent); + virtual void setup(); + + virtual void paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align); + void setLink(const TQString &text, const TQString &link = TQString::null ); + + TQString linkUrl() const { return m_link_url; } + + /// returns true if the cursor has to change + bool hitsLink(const TQPoint &pos); + +protected: + void preparePixmap(int width); + TQPixmap pixmap; + int left_margin; + +private: + TQListView* lv; + int cached_width; + TQString m_link_text, m_link_url; + TQRect m_link_rect; + +}; + +class KMenuItemHeader : public KMenuItemSeparator +{ +public: + KMenuItemHeader( int nId, const TQString &relpath, TQListView* parent); + virtual void setup(); + + virtual void paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align); + +private: + TQListView* lv; + TQStringList paths; + TQStringList texts; + TQStringList icons; + TQPixmap left_triangle; +}; + +class KMenuSpacer : public KMenuItem +{ +public: + KMenuSpacer(int nId, TQListView* parent); + virtual void paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align); + virtual void setup(); + + void setHeight(int); +}; + +class ItemView : public KListView +{ + friend class KMenuItem; + + Q_OBJECT +public: + ItemView(TQWidget* parent, const char* name = 0); + + KMenuItem* insertItem( const TQString& icon, const TQString& text, const TQString& description, int nId, int nIndex, KMenuItem* parentItem = 0 ); + KMenuItem* insertItem( const TQString& icon, const TQString& text, const TQString& description, const TQString& path, int nId, int nIndex, KMenuItem* parentItem = 0 ); + int insertItem( PopupMenuTitle*, int, int); + int setItemEnabled(int id, bool enabled); + KMenuItemSeparator *insertSeparator(int id, const TQString& text, int nIndex); + KMenuItemHeader *insertHeader(int id, const TQString &relpath); + KMenuItem* insertMenuItem(KService::Ptr & s, int nId, int nIndex = -1, KMenuItem* parentItem = 0, + const TQString &aliasname = TQString::null, const TQString &label = TQString::null, + const TQString &categoryIcon = TQString::null); + KMenuItem* insertRecentlyItem(const TQString& s, int nId, int nIndex = -1); + KMenuItem* insertDocumentItem(const TQString& s, int nId, int nIndex = -1 , const TQStringList* suppressGenericNames = 0, + const TQString& aliasname = TQString::null); + KMenuItem* insertSubItem(const TQString& icon, const TQString& caption, const TQString& description, const TQString& path, KMenuItem* parentItem); + KMenuItem* findItem(int nId); + + void setIconSize(int size) { m_iconSize = size; } + void setMouseMoveSelects(bool select) { m_mouseMoveSelects = select; } + void clear(); + int goodHeight(); + TQString path; + void setBackPath( const TQString &str ) { m_back_url = str; } + TQString backPath() const { return m_back_url; } + +public slots: + void slotItemClicked(TQListViewItem*); + void slotMoveContent(); + +signals: + void startService(KService::Ptr kservice); + void startURL(const TQString& u); + +protected: + void contentsMouseMoveEvent(TQMouseEvent *e); + void contentsMousePressEvent ( TQMouseEvent * e ); + void contentsWheelEvent(TQWheelEvent *e); + void leaveEvent(TQEvent *e); + virtual void resizeEvent ( TQResizeEvent * e ); + virtual void viewportPaintEvent ( TQPaintEvent * pe ); + virtual TQDragObject* dragObject (); + virtual bool acceptDrag (TQDropEvent* event) const; + virtual bool focusNextPrevChild(bool next); + +private slots: + void slotItemClicked(int button, TQListViewItem * item, const TQPoint & pos, int c ); + +private: + KMenuItem* itemAtIndex(int nIndex); + void moveItemToIndex(KMenuItem*, int); + + TQWidget* m_itemBox; + TQVBoxLayout* m_itemLayout; + KMenuItem *m_lastOne; + KMenuSpacer *m_spacer; + + TQString m_back_url; + + bool m_mouseMoveSelects; + int m_iconSize; + int m_old_contentY; +}; + +class FavoritesItemView : public ItemView +{ +public: + FavoritesItemView(TQWidget* parent, const char* name = 0); + +protected: + virtual bool acceptDrag (TQDropEvent* event) const; +}; + +class KMenuItemInfo +{ +public: + int m_id; + KService::Ptr m_s; + TQString m_title; + TQString m_description; + TQString m_path; + TQString m_icon; +}; + +class KMenuItemDrag : public TQDragObject +{ + public: + KMenuItemDrag(KMenuItem& item, TQWidget *dragSource); + ~KMenuItemDrag(); + + virtual const char * format(int i = 0) const; + virtual TQByteArray encodedData(const char *) const; + + static bool canDecode(const TQMimeSource * e); + static bool decode(const TQMimeSource* e, KMenuItemInfo& item); + + private: + TQByteArray a; +}; + +#endif diff --git a/kicker/kicker/ui/k_mnu_stub.cpp b/kicker/kicker/ui/k_mnu_stub.cpp new file mode 100644 index 000000000..b02d2bcbf --- /dev/null +++ b/kicker/kicker/ui/k_mnu_stub.cpp @@ -0,0 +1,141 @@ +/***************************************************************** + +Copyright (c) 2006 Dirk Mueller + +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 "k_mnu_stub.h" +#include "k_new_mnu.h" +#include "k_mnu.h" + +void KMenuStub::removeClientMenu(int id) +{ + if(m_type == t_KMenu) + return m_w.kmenu->removeClientMenu(id); + return m_w.panelkmenu->removeClientMenu(id); +} + +int KMenuStub::insertClientMenu(KickerClientMenu *p) +{ + if(m_type == t_KMenu) + return m_w.kmenu->insertClientMenu(p); + return m_w.panelkmenu->insertClientMenu(p); +} + +void KMenuStub::adjustSize() +{ + if(m_type == t_KMenu) + return m_w.kmenu->adjustSize(); + return m_w.panelkmenu->adjustSize(); +} + +void KMenuStub::hide() +{ + if(m_type == t_KMenu) + return m_w.kmenu->hide(); + return m_w.panelkmenu->hide(); +} + +void KMenuStub::show() +{ + if(m_type == t_KMenu) + return m_w.kmenu->show(); + return m_w.panelkmenu->show(); +} + +void KMenuStub::showMenu() +{ + if(m_type == t_KMenu) + return m_w.kmenu->showMenu(); + return m_w.panelkmenu->showMenu(); +} + +#if 0 +void KMenuStub::resize() +{ + if(m_type == t_KMenu) + return m_w.kmenu->resize(); + return m_w.panelkmenu->resize(); +} +#endif + +void KMenuStub::popup(const TQPoint &pos, int indexAtPoint) +{ + return m_type == t_KMenu ? + m_w.kmenu->popup(pos, indexAtPoint) + : m_w.panelkmenu->popup(pos, indexAtPoint); +} + +void KMenuStub::selectFirstItem() +{ + if(m_type == t_KMenu) + return m_w.kmenu->selectFirstItem(); + return m_w.panelkmenu->selectFirstItem(); +} + +void KMenuStub::resize(int w, int h) +{ + if(m_type == t_KMenu) + return m_w.kmenu->resize(w, h); + return m_w.panelkmenu->resize(w, h); +} + +TQSize KMenuStub::sizeHint() const +{ + if(m_type == t_KMenu) + return m_w.kmenu->sizeHint(); + return m_w.panelkmenu->sizeHint(); +} + +bool KMenuStub::highlightMenuItem( const TQString &menuId ) +{ + if(m_type == t_KMenu) + return m_w.kmenu->highlightMenuItem(menuId); + return m_w.panelkmenu->highlightMenuItem(menuId); +} + +void KMenuStub::clearRecentMenuItems() +{ + if(m_type == t_KMenu) + return m_w.kmenu->clearRecentAppsItems(); + return m_w.panelkmenu->clearRecentMenuItems(); +} + +void KMenuStub::initialize() +{ + if(m_type == t_KMenu) + return m_w.kmenu->initialize(); + return m_w.panelkmenu->initialize(); +} + +bool KMenuStub::isVisible() const +{ + if(m_type == t_KMenu) + return m_w.kmenu->isVisible(); + return m_w.panelkmenu->isVisible(); +} + +TQWidget* KMenuStub::widget() +{ + if(m_type == t_KMenu) + return m_w.kmenu; + return m_w.panelkmenu; +} + diff --git a/kicker/kicker/ui/k_mnu_stub.h b/kicker/kicker/ui/k_mnu_stub.h new file mode 100644 index 000000000..362d132b2 --- /dev/null +++ b/kicker/kicker/ui/k_mnu_stub.h @@ -0,0 +1,72 @@ +/***************************************************************** + +Copyright (c) 2006 Dirk Mueller + +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 __k_mnu_stub_h__ +#define __k_mnu_stub_h__ + +#include +#include + +class KickerClientMenu; +class KMenu; +class PanelKMenu; + + + + +class KMenuStub +{ +public: + KMenuStub(KMenu* _kmenu) + : m_type(t_KMenu) { m_w.kmenu = _kmenu; } + KMenuStub(PanelKMenu* _panelkmenu) + : m_type(t_PanelKMenu) { m_w.panelkmenu = _panelkmenu; } + ~KMenuStub() {} + + void removeClientMenu(int id); + int insertClientMenu(KickerClientMenu *p); + void adjustSize(); + void hide(); + void show(); + void showMenu(); + void resize(); + void popup(const TQPoint &pos, int indexAtPoint = -1); + void selectFirstItem(); + void resize(int, int); + TQSize sizeHint() const; + bool highlightMenuItem( const TQString &menuId ); + void clearRecentMenuItems(); + void initialize(); + + TQWidget* widget(); + + bool isVisible() const; +private: + enum {t_PanelKMenu, t_KMenu} m_type; + union { + KMenu* kmenu; + PanelKMenu* panelkmenu; + } m_w; +}; + +#endif diff --git a/kicker/kicker/ui/k_new_mnu.cpp b/kicker/kicker/ui/k_new_mnu.cpp new file mode 100644 index 000000000..34b11790b --- /dev/null +++ b/kicker/kicker/ui/k_new_mnu.cpp @@ -0,0 +1,3779 @@ +/***************************************************************** + + Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + Copyright (c) 2006 Debajyoti Bera + Copyright (c) 2006 Dirk Mueller + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +******************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client_mnu.h" +#include "container_base.h" +#include "global.h" +#include "knewbutton.h" +#include "kicker.h" +#include "kickerSettings.h" +#include "konqbookmarkmanager.h" +#include "menuinfo.h" +#include "menumanager.h" +#include "popupmenutitle.h" +#include "quickbrowser_mnu.h" +#include "recentapps.h" +#include "flipscrollview.h" +#include "itemview.h" +#include +#include +#include + +#include "media_watcher.h" +#include "k_mnu.h" +#include "k_new_mnu.h" +#include "k_new_mnu.moc" +#include "kickoff_bar.h" + +#define WAIT_BEFORE_QUERYING 700 + +#define IDS_PER_CATEGORY 20 +#define ACTIONS_ID_BASE 10 +#define APP_ID_BASE 10 + IDS_PER_CATEGORY +#define BOOKMARKS_ID_BASE 10 + (IDS_PER_CATEGORY * 2) +#define NOTES_ID_BASE 10 + (IDS_PER_CATEGORY * 3) +#define MAIL_ID_BASE 10 + (IDS_PER_CATEGORY * 4) +#define FILE_ID_BASE 10 + (IDS_PER_CATEGORY * 5) +#define MUSIC_ID_BASE 10 + (IDS_PER_CATEGORY * 6) +#define WEBHIST_ID_BASE 10 + (IDS_PER_CATEGORY * 7) +#define CHAT_ID_BASE 10 + (IDS_PER_CATEGORY * 8) +#define FEED_ID_BASE 10 + (IDS_PER_CATEGORY * 9) +#define PIC_ID_BASE 10 + (IDS_PER_CATEGORY * 10) +#define VIDEO_ID_BASE 10 + (IDS_PER_CATEGORY * 11) +#define DOC_ID_BASE 10 + (IDS_PER_CATEGORY * 12) +#define OTHER_ID_BASE 10 + (IDS_PER_CATEGORY * 13) + +static TQString calculate(const TQString &exp) +{ + TQString result, cmd; + const TQString bc = KStandardDirs::findExe("bc"); + if ( !bc.isEmpty() ) + cmd = TQString("echo %1 | %2").arg(KProcess::quote(exp), KProcess::quote(bc)); + else + cmd = TQString("echo $((%1))").arg(exp); + FILE *fs = popen(TQFile::encodeName(cmd).data(), "r"); + if (fs) + { + TQTextStream ts(fs, IO_ReadOnly); + result = ts.read().stripWhiteSpace(); + pclose(fs); + } + return result; +} + +static TQString workaroundStringFreeze(const TQString& str) +{ + TQString s = str; + + s.replace("","&"); + TQRegExp re("<[^>]+>"); + re.setMinimal(true); + re.setCaseSensitive(false); + + s.replace(re, ""); + s = s.simplifyWhiteSpace(); + + return s; +} + +int base_category_id[] = {ACTIONS_ID_BASE, APP_ID_BASE, BOOKMARKS_ID_BASE, NOTES_ID_BASE, MAIL_ID_BASE, + FILE_ID_BASE, MUSIC_ID_BASE, WEBHIST_ID_BASE, CHAT_ID_BASE, FEED_ID_BASE, + PIC_ID_BASE, VIDEO_ID_BASE, DOC_ID_BASE, OTHER_ID_BASE}; + +#include + +static int used_size( TQLabel *label, int oldsize ) +{ + TQSimpleRichText st( label->text(), KGlobalSettings::toolBarFont() ); + st.setWidth( oldsize ); + return QMAX( st.widthUsed(), oldsize ); +} + +KMenu::KMenu() + : KMenuBase(0, "SUSE::Kickoff::KMenu") + , m_sloppyTimer(0, "KNewMenu::sloppyTimer"), m_mediaFreeTimer(0, "KNewMenu::mediaFreeTimer"), + m_iconName(TQString::null), m_orientation(UnDetermined), m_search_plugin( 0 ) +{ + setMouseTracking(true); + connect(&m_sloppyTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotSloppyTimeout())); + + // set the first client id to some arbitrarily large value. + client_id = 10000; + // Don't automatically clear the main menu. + actionCollection = new KActionCollection(this); + + connect(Kicker::the(), TQT_SIGNAL(configurationChanged()), + this, TQT_SLOT(configChanged())); + + KUser * user = new KUser(); + + char hostname[256]; + hostname[0] = '\0'; + if (!gethostname( hostname, sizeof(hostname) )) + hostname[sizeof(hostname)-1] = '\0'; + + m_userInfo->setText( i18n( "User %1 on %2" ) + .arg( user->loginName() ).arg( hostname ) ); + setupUi(); + + m_userInfo->setBackgroundMode( PaletteBase ); + TQColor userInfoColor = TQApplication::palette().color( TQPalette::Normal, TQColorGroup::Mid ); + if ( qGray( userInfoColor.rgb() ) > 120 ) + userInfoColor = userInfoColor.dark( 200 ); + else + userInfoColor = userInfoColor.light( 200 ); + m_userInfo->setPaletteForegroundColor( userInfoColor ); + + m_tabBar = new KickoffTabBar(this, "m_tabBar"); + connect(m_tabBar, TQT_SIGNAL(tabClicked(TQTab*)), TQT_SLOT(tabClicked(TQTab*))); + + const int tab_icon_size = 32; + + m_tabs[FavoriteTab] = new TQTab; + m_tabBar->addTab(m_tabs[FavoriteTab]); + m_tabBar->setToolTip(FavoriteTab, "" + i18n( "Most commonly used applications and documents" ) + "" ); + m_tabs[ApplicationsTab] = new TQTab; + m_tabBar->addTab(m_tabs[ApplicationsTab]); + m_tabBar->setToolTip(ApplicationsTab, "" + i18n( "List of installed applications" ) + + "" ); + + m_tabs[ComputerTab] = new TQTab; + m_tabBar->addTab(m_tabs[ComputerTab]); + m_tabBar->setToolTip(ComputerTab, "" + i18n( "Information and configuration of your " + "system, access to personal files, network resources and connected disk drives") + + ""); +#if 0 + m_tabs[SearchTab] = new TQTab; + m_tabBar->addTab(m_tabs[SearchTab]); +#endif + m_tabs[HistoryTab] = new TQTab; + m_tabBar->addTab(m_tabs[HistoryTab]); + m_tabBar->setToolTip(HistoryTab, "" + i18n( "Recently used applications and documents" ) + + "" ); + m_tabs[LeaveTab] = new TQTab; + m_tabBar->addTab(m_tabs[LeaveTab]); + m_tabBar->setToolTip(LeaveTab, i18n("Logout, switch user, switch off or reset," + " suspend of the system" ) + "" ); + + if (KickerSettings::kickoffTabBarFormat() != KickerSettings::IconOnly) { + m_tabs[FavoriteTab]->setText(workaroundStringFreeze(i18n("

Favorites

"))); + m_tabs[HistoryTab]->setText(workaroundStringFreeze(i18n("

History

"))); + m_tabs[ComputerTab]->setText( + workaroundStringFreeze(i18n("

Computer

"))); + m_tabs[ApplicationsTab]->setText(workaroundStringFreeze(i18n("

Applications

"))); + m_tabs[LeaveTab]->setText( + workaroundStringFreeze(i18n("

Leave

"))); + } + + if (KickerSettings::kickoffTabBarFormat() != KickerSettings::LabelOnly) { + m_tabs[FavoriteTab]->setIconSet(BarIcon("bookmark", tab_icon_size)); + m_tabs[HistoryTab]->setIconSet(BarIcon("recently_used", tab_icon_size)); + m_tabs[ComputerTab]->setIconSet(BarIcon("system", tab_icon_size)); + m_tabs[ApplicationsTab]->setIconSet(BarIcon("player_playlist", tab_icon_size)); + m_tabs[LeaveTab]->setIconSet(BarIcon("leave", tab_icon_size)); + } + + connect(m_tabBar, TQT_SIGNAL(selected(int)), m_stacker, TQT_SLOT(raiseWidget(int))); + connect(m_stacker, TQT_SIGNAL(aboutToShow(int)), m_tabBar, TQT_SLOT(setCurrentTab(int))); + + m_favoriteView = new FavoritesItemView (m_stacker, "m_favoriteView"); + m_favoriteView->setAcceptDrops(true); + m_favoriteView->setItemsMovable(true); + m_stacker->addWidget(m_favoriteView, FavoriteTab); + + m_recentlyView = new ItemView (m_stacker, "m_recentlyView"); + m_stacker->addWidget(m_recentlyView, HistoryTab); + + m_systemView = new ItemView(m_stacker, "m_systemView"); + m_stacker->addWidget(m_systemView, ComputerTab ); + + m_browserView = new FlipScrollView(m_stacker, "m_browserView"); + m_stacker->addWidget(m_browserView, ApplicationsTab); + connect( m_browserView, TQT_SIGNAL( backButtonClicked() ), TQT_SLOT( slotGoBack() ) ); + + m_exitView = new FlipScrollView(m_stacker, "m_exitView"); + m_stacker->addWidget(m_exitView, LeaveTab); + connect( m_exitView, TQT_SIGNAL( backButtonClicked() ), TQT_SLOT( slotGoExitMainMenu() ) ); + + m_searchWidget = new TQVBox (m_stacker, "m_searchWidget"); + m_searchWidget->setSpacing(0); + m_stacker->addWidget(m_searchWidget, 5); + + // search provider icon + TQPixmap icon; + KURIFilterData data; + TQStringList list; + data.setData( TQString("some keyword") ); + list << "kurisearchfilter" << "kuriikwsfilter"; + + if ( KURIFilter::self()->filterURI(data, list) ) { + TQString iconPath = locate("cache", KMimeType::favIconForURL(data.uri()) + ".png"); + if ( iconPath.isEmpty() ) + icon = SmallIcon("enhanced_browsing"); + else + icon = TQPixmap( iconPath ); + } + else + icon = SmallIcon("enhanced_browsing"); + + m_searchResultsWidget = new ItemView (m_searchWidget, "m_searchResultsWidget"); + m_searchResultsWidget->setItemMargin(4); + m_searchResultsWidget->setIconSize(16); + m_searchActions = new ItemView (m_searchWidget, "m_searchActions"); + m_searchActions->setFocusPolicy(TQWidget::NoFocus); + m_searchActions->setItemMargin(4); + m_searchInternet = new TQListViewItem(m_searchActions, i18n("Search Internet")); + m_searchInternet->setPixmap(0,icon); + setTabOrder(m_kcommand, m_searchResultsWidget); + + m_kerryInstalled = !KStandardDirs::findExe(TQString::fromLatin1("kerry")).isEmpty(); + m_isShowing = false; + + if (!m_kerryInstalled) { + m_searchIndex = 0; + m_searchActions->setMaximumHeight(5+m_searchInternet->height()); + } + else { + m_searchIndex = new TQListViewItem(m_searchActions, i18n("Search Index")); + m_searchIndex->setPixmap(0,SmallIcon("kerry")); + m_searchActions->setMaximumHeight(5+m_searchIndex->height()*2); + } + connect(m_searchActions, TQT_SIGNAL(clicked(TQListViewItem*)), TQT_SLOT(searchActionClicked(TQListViewItem*))); + connect(m_searchActions, TQT_SIGNAL(returnPressed(TQListViewItem*)), TQT_SLOT(searchActionClicked(TQListViewItem*))); + connect(m_searchActions, TQT_SIGNAL(spacePressed(TQListViewItem*)), TQT_SLOT(searchActionClicked(TQListViewItem*))); + + connect(m_searchResultsWidget, TQT_SIGNAL(startService(KService::Ptr)), TQT_SLOT(slotStartService(KService::Ptr))); + connect(m_searchResultsWidget, TQT_SIGNAL(startURL(const TQString&)), TQT_SLOT(slotStartURL(const TQString&))); + connect(m_searchResultsWidget, TQT_SIGNAL(rightButtonPressed( TQListViewItem*, const TQPoint &, int )), TQT_SLOT(slotContextMenuRequested( TQListViewItem*, const TQPoint &, int ))); + + connect(m_recentlyView, TQT_SIGNAL(startService(KService::Ptr)), TQT_SLOT(slotStartService(KService::Ptr))); + connect(m_recentlyView, TQT_SIGNAL(startURL(const TQString&)), TQT_SLOT(slotStartURL(const TQString&))); + connect(m_recentlyView, TQT_SIGNAL(rightButtonPressed( TQListViewItem*, const TQPoint &, int )), TQT_SLOT(slotContextMenuRequested( TQListViewItem*, const TQPoint &, int ))); + + connect(m_favoriteView, TQT_SIGNAL(startService(KService::Ptr)), TQT_SLOT(slotStartService(KService::Ptr))); + connect(m_favoriteView, TQT_SIGNAL(startURL(const TQString&)), TQT_SLOT(slotStartURL(const TQString&))); + connect(m_favoriteView, TQT_SIGNAL(rightButtonPressed( TQListViewItem*, const TQPoint &, int )), TQT_SLOT(slotContextMenuRequested( TQListViewItem*, const TQPoint &, int ))); + connect(m_favoriteView, TQT_SIGNAL(moved(TQListViewItem*, TQListViewItem*, TQListViewItem*)), TQT_SLOT(slotFavoritesMoved( TQListViewItem*, TQListViewItem*, TQListViewItem* ))); + + connect(m_systemView, TQT_SIGNAL(startURL(const TQString&)), TQT_SLOT(slotStartURL(const TQString&))); + connect(m_systemView, TQT_SIGNAL(startService(KService::Ptr)), TQT_SLOT(slotStartService(KService::Ptr))); + connect(m_systemView, TQT_SIGNAL(rightButtonPressed( TQListViewItem*, const TQPoint &, int )), TQT_SLOT(slotContextMenuRequested( TQListViewItem*, const TQPoint &, int ))); + + connect(m_browserView, TQT_SIGNAL(startURL(const TQString&)), TQT_SLOT(slotGoSubMenu(const TQString&))); + connect(m_browserView, TQT_SIGNAL(startService(KService::Ptr)), TQT_SLOT(slotStartService(KService::Ptr))); + connect(m_browserView, TQT_SIGNAL(rightButtonPressed( TQListViewItem*, const TQPoint &, int )), TQT_SLOT(slotContextMenuRequested( TQListViewItem*, const TQPoint &, int ))); + + connect(m_exitView, TQT_SIGNAL(startURL(const TQString&)), TQT_SLOT(slotStartURL(const TQString&))); + connect(m_exitView, TQT_SIGNAL(rightButtonPressed( TQListViewItem*, const TQPoint &, int )), TQT_SLOT(slotContextMenuRequested( TQListViewItem*, const TQPoint &, int ))); + + m_kcommand->setDuplicatesEnabled( false ); + m_kcommand->setLineEdit(new KLineEdit(m_kcommand, "m_kcommand-lineedit")); + m_kcommand->setCompletionMode( KGlobalSettings::CompletionAuto ); + connect(m_kcommand, TQT_SIGNAL(cleared()), TQT_SLOT(clearedHistory())); + connect(m_kcommand->lineEdit(), TQT_SIGNAL(returnPressed()), TQT_SLOT(searchAccept())); + connect(m_kcommand->lineEdit(), TQT_SIGNAL(textChanged(const TQString &)), TQT_SLOT(searchChanged(const TQString &))); + + // URI Filter meta object... + m_filterData = new KURIFilterData(); + + max_category_id = new int [num_categories]; + categorised_hit_total = new int [num_categories]; + + input_timer = new TQTimer (this, "input_timer"); + connect( input_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(doQuery()) ); + + init_search_timer = new TQTimer (this, "init_search_timer"); + connect( init_search_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(initSearch()) ); + init_search_timer->start(2000, true); + + connect( m_favoriteView, TQT_SIGNAL( dropped (TQDropEvent *, TQListViewItem * ) ), + TQT_SLOT( slotFavDropped( TQDropEvent *, TQListViewItem * ) ) ); + + this->installEventFilter(this); + m_tabBar->installEventFilter(this); + m_favoriteView->installEventFilter(this); + m_recentlyView->installEventFilter(this); + m_browserView->leftView()->installEventFilter(this); + m_browserView->rightView()->installEventFilter(this); + m_systemView->installEventFilter(this); + m_exitView->leftView()->installEventFilter(this); + m_exitView->rightView()->installEventFilter(this); + m_kcommand->lineEdit()->installEventFilter(this); + m_searchLabel->installEventFilter(this); + m_searchPixmap->installEventFilter(this); + m_stacker->installEventFilter(this); + + emailRegExp = TQRegExp("^([\\w\\-]+\\.)*[\\w\\-]+@([\\w\\-]+\\.)*[\\w\\-]+$"); + authRegExp = TQRegExp("^[a-zA-Z]+://\\w+(:\\w+)?@([\\w\\-]+\\.)*[\\w\\-]+(:\\d+)?(/.*)?$"); + uriRegExp = TQRegExp("^[a-zA-Z]+://([\\w\\-]+\\.)*[\\w\\-]+(:\\d+)?(/.*)?$"); + uri2RegExp = TQRegExp("^([\\w\\-]+\\.)+[\\w\\-]+(:\\d+)?(/.*)?$"); + + m_resizeHandle = new TQLabel(this); + m_resizeHandle->setBackgroundOrigin( TQLabel::ParentOrigin ); + m_resizeHandle->setScaledContents(true); + m_resizeHandle->setFixedSize( 16, 16 ); + m_searchFrame->stackUnder( m_resizeHandle ); + m_isresizing = false; + + m_searchPixmap->setPixmap( BarIcon( "find", 32 ) ); + + TQFont f = font(); + f.setPointSize( kMax( 7, (f.pointSize() * 4 / 5 ) + KickerSettings::kickoffFontPointSizeOffset() ) ); + m_tabBar->setFont ( f ); + f.setPointSize( kMax( 7, (f.pointSize() * 3 / 2 ) + KickerSettings::kickoffFontPointSizeOffset() ) ); + m_searchLabel->setFont( f ); + + static_cast(m_kcommand->lineEdit())->setClickMessage(i18n( "Applications, Contacts and Documents" ) ); + + bookmarkManager = 0; + m_addressBook = 0; + m_popupMenu = 0; + + main_border_tl.load( locate("data", "kicker/pics/main_corner_tl.png" ) ); + main_border_tr.load( locate("data", "kicker/pics/main_corner_tr.png" ) ); + + search_tab_left.load( locate("data", "kicker/pics/search-tab-left.png" ) ); + search_tab_right.load( locate("data", "kicker/pics/search-tab-right.png" ) ); + search_tab_center.load( locate("data", "kicker/pics/search-tab-center.png" ) ); + + search_tab_top_left.load( locate("data", "kicker/pics/search-tab-top-left.png" ) ); + search_tab_top_right.load( locate("data", "kicker/pics/search-tab-top-right.png" ) ); + search_tab_top_center.load( locate("data", "kicker/pics/search-tab-top-center.png" ) ); +} + +void KMenu::setupUi() +{ + m_stacker = new TQWidgetStack( this, "m_stacker" ); + m_stacker->setGeometry( TQRect( 90, 260, 320, 220 ) ); + m_stacker->setSizePolicy( TQSizePolicy( (TQSizePolicy::SizeType)3, (TQSizePolicy::SizeType)3, 1, 1, m_stacker->sizePolicy().hasHeightForWidth() ) ); + m_stacker->setPaletteBackgroundColor( TQColor( 255, 255, 255 ) ); + // m_stacker->setFocusPolicy( TQWidget::StrongFocus ); + m_stacker->setLineWidth( 0 ); + m_stacker->setFocusPolicy(TQWidget::NoFocus); + connect(m_stacker, TQT_SIGNAL(aboutToShow(TQWidget*)), TQT_SLOT(stackWidgetRaised(TQWidget*))); + + m_kcommand->setName("m_kcommand"); +} + +KMenu::~KMenu() +{ + saveConfig(); + + clearSubmenus(); + delete m_filterData; +} + +bool KMenu::eventFilter ( TQObject * receiver, TQEvent* e) +{ +//kdDebug() << "eventFilter receiver=" << receiver->name() << " type=" << e->type() << endl; + TQWidget* raiseWidget = 0; + TQRect raiseRect; + + if (e->type() == TQEvent::KeyPress || + e->type() == TQEvent::MouseButtonPress || + e->type() == TQEvent::MouseMove + || e->type() == TQEvent::FocusIn + || e->type() == TQEvent::Wheel) { + TQPoint p; + + if (e->type() == TQEvent::MouseMove || e->type() == TQEvent::MouseButtonPress) { + TQMouseEvent* me = static_cast(e); + p = me->globalPos(); + } + else if (e->type() == TQEvent::Wheel) { + TQWheelEvent* we = static_cast(e); + p = we->globalPos(); + } + + while (receiver) { + if (receiver == m_tabBar && (e->type()!=TQEvent::MouseMove || KickerSettings::kickoffSwitchTabsOnHover() ) ) { + TQTab* s = m_tabBar->selectTab(m_tabBar->mapFromGlobal(p)); + if (s && s->identifier() == ApplicationsTab) + raiseWidget = m_browserView; + if (s && s->identifier() == FavoriteTab) + raiseWidget = m_favoriteView; + if (s && s->identifier() == HistoryTab) + raiseWidget = m_recentlyView; + if (s && s->identifier() == ComputerTab) + raiseWidget = m_systemView; + if (s && s->identifier() == LeaveTab) + raiseWidget = m_exitView; + + if (raiseWidget) + raiseRect = TQRect( m_tabBar->mapToGlobal(s->rect().topLeft()), + s->rect().size()); + } + + /* we do not want hover activation for the search line edit as this can be + * pretty disturbing */ + if ( (receiver == m_searchPixmap || + ( ( receiver == m_searchLabel || receiver==m_kcommand->lineEdit() ) && + ( e->type() == TQEvent::KeyPress || e->type() == TQEvent::Wheel + || e->type() == TQEvent::MouseButtonPress ) ) ) && + !m_isShowing) { + raiseWidget = m_searchWidget; + raiseRect = TQRect( m_searchFrame->mapToGlobal(m_searchFrame->rect().topLeft()), + m_searchFrame->size()); + } + + if(raiseWidget) + break; + if(receiver->isWidgetType()) + receiver = static_cast(receiver)->parentWidget(true); + else + break; + } + + if (e->type() == TQEvent::FocusIn && receiver && raiseWidget) { + m_searchResultsWidget->setFocusPolicy(TQWidget::StrongFocus); + m_searchActions->setFocusPolicy(raiseWidget == m_searchWidget ? + TQWidget::StrongFocus : TQWidget::NoFocus); + setTabOrder(raiseWidget, m_searchResultsWidget); + if (raiseWidget != m_stacker->visibleWidget() + && static_cast(receiver)->focusPolicy() == TQWidget::NoFocus + && m_stacker->id(raiseWidget) >= 0) { + + m_stacker->raiseWidget(raiseWidget); + return true; + } + + if (raiseWidget->focusPolicy() != TQWidget::NoFocus) + return false; + } + + if (m_sloppyRegion.contains(p)) { + if (e->type() == TQEvent::MouseButtonPress /*&& m_sloppyTimer.isActive()*/) + m_sloppySourceClicked = true; + + if (!m_sloppyTimer.isActive() || m_sloppySource != raiseRect) { + int timeout= style().styleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay); + if (m_sloppySourceClicked) + timeout = 3000; + m_sloppyTimer.start(timeout); + } + + m_sloppyWidget = raiseWidget; + m_sloppySource = raiseRect; + return false; + } + } + + if(e->type() == TQEvent::Enter && receiver->isWidgetType()) { + static_cast(receiver)->setMouseTracking(true); + TQToolTip::hide(); + } + + if ( ( e->type() == TQEvent::DragEnter || e->type() == TQEvent::DragMove ) && + raiseWidget == m_favoriteView ) + { + m_stacker->raiseWidget(m_favoriteView); + + return false; + } + + // This is a nightmare of a hack, look away. Logic needs + // to be moved to the stacker and all widgets in the stacker + // must have focusNextPrevChild() overwritten to do nothing + if (e->type() == TQEvent::KeyPress && !raiseRect.isNull()) { + ItemView* view; + if (m_browserView==m_stacker->visibleWidget()) + view = m_browserView->currentView(); + else if (m_exitView==m_stacker->visibleWidget()) + view = m_exitView->currentView(); + else + view = dynamic_cast(m_stacker->visibleWidget()); + + if (view) + { + bool handled = true; + switch (static_cast(e)->key()) { + case Key_Up: + if (view->selectedItem()) { + view->setSelected(view->selectedItem()->itemAbove(),true); + } + else { + view->setSelected(view->lastItem(),true); + } + break; + case Key_Down: + if (view->selectedItem()) { + view->setSelected(view->selectedItem()->itemBelow(),true); + } + else { + if (view->firstChild() && view->firstChild()->isSelectable()) + view->setSelected(view->firstChild(),true); + else if (view->childCount()>2) + view->setSelected(view->firstChild()->itemBelow(),true); + } + break; + case Key_Right: + if (view->selectedItem() && !static_cast(view->selectedItem())->hasChildren()) + break; + // nobreak + case Key_Enter: + case Key_Return: + if (view->selectedItem()) + view->slotItemClicked(view->selectedItem()); + + break; + case Key_Left: + if (m_browserView == m_stacker->visibleWidget() || m_exitView == m_stacker->visibleWidget()) { + FlipScrollView* flip = dynamic_cast(m_stacker->visibleWidget()); + if (flip->showsBackButton()) { + if (m_browserView == m_stacker->visibleWidget()) + goSubMenu( m_browserView->currentView()->backPath(), true ); + else + view->slotItemClicked(view->firstChild()); + } + break; + } + // nobreak + case Key_Backspace: + if (m_browserView == m_stacker->visibleWidget() || m_exitView == m_stacker->visibleWidget()) { + FlipScrollView* flip = dynamic_cast(m_stacker->visibleWidget()); + if (flip->showsBackButton()) { + if (m_browserView == m_stacker->visibleWidget()) + goSubMenu( m_browserView->currentView()->backPath(), true ); + else + view->slotItemClicked(view->firstChild()); + } + } + + break; + default: + handled = false; + } + + if (handled) + view->ensureItemVisible(view->selectedItem()); + + return handled; + } + } + + bool r = KMenuBase::eventFilter(receiver, e); + + if (!r && raiseWidget) + m_stacker->raiseWidget(raiseWidget); + + if (e->type() == TQEvent::Wheel && raiseWidget ) + { + // due to an ugly TQt bug we have to kill wheel events + // that cause focus switches + r = true; + } + + if (e->type() == TQEvent::Enter && receiver == m_stacker) + { + TQRect r(m_stacker->mapToGlobal(TQPoint(-8,-32)), m_stacker->size()); + r.setSize(r.size()+TQSize(16,128)); + + m_sloppyRegion = TQRegion(r); + } + + // redo the sloppy region + if (e->type() == TQEvent::MouseMove && !r && raiseWidget) + { + TQPointArray points(4); + + // hmm, eventually this should be mouse position + 10px, not + // just worst case. but worst case seems to work fine enough. + TQPoint edge(raiseRect.topLeft()); + edge.setX(edge.x()+raiseRect.center().x()); + + if (m_orientation == BottomUp) + { + points.setPoint(0, m_stacker->mapToGlobal(m_stacker->rect().bottomLeft())); + points.setPoint(1, m_stacker->mapToGlobal(m_stacker->rect().bottomRight())); + + edge.setY(edge.y()+raiseRect.height()); + points.setPoint(2, edge+TQPoint(+raiseRect.width()/4,0)); + points.setPoint(3, edge+TQPoint(-raiseRect.width()/4,0)); + } + else + { + points.setPoint(0, m_stacker->mapToGlobal(m_stacker->rect().topLeft())); + points.setPoint(1, m_stacker->mapToGlobal(m_stacker->rect().topRight())); + points.setPoint(2, edge+TQPoint(-raiseRect.width()/4,0)); + points.setPoint(3, edge+TQPoint(+raiseRect.width()/4,0)); + } + + m_sloppyRegion = TQRegion(points); + } + + return r; +} + +void KMenu::slotSloppyTimeout() +{ + if (m_sloppyRegion.contains(TQCursor::pos()) && !m_sloppySource.isNull()) + { + if ( m_sloppySource.contains(TQCursor::pos())) + { + m_stacker->raiseWidget(m_sloppyWidget); + + m_sloppyWidget = 0; + m_sloppySource = TQRect(); + m_sloppyRegion = TQRegion(); + m_sloppySourceClicked = false; + } + } + m_sloppyTimer.stop(); +} + +void KMenu::paintSearchTab( bool active ) +{ + TQPixmap canvas( m_searchFrame->size() ); + TQPainter p( &canvas ); + + TQPixmap pix; + + if ( m_orientation == BottomUp ) + pix.load( locate("data", "kicker/pics/search-gradient.png" ) ); + else + pix.load( locate("data", "kicker/pics/search-gradient-topdown.png" ) ); + + pix.convertFromImage( pix.convertToImage().scale(pix.width(), m_searchFrame->height())); + p.drawTiledPixmap( 0, 0, m_searchFrame->width(), m_searchFrame->height(), pix ); + + if ( active ) { + + m_tabBar->deactivateTabs(true); + + p.setBrush( Qt::white ); + p.setPen( Qt::NoPen ); + + if ( m_orientation == BottomUp ) { + search_tab_center.convertFromImage( search_tab_center.convertToImage().scale(search_tab_center.width(), m_searchFrame->height())); + p.drawTiledPixmap( search_tab_left.width(), 0, m_searchFrame->width()-search_tab_left.width()-search_tab_right.width(), m_searchFrame->height(), search_tab_center ); + + search_tab_left.convertFromImage( search_tab_left.convertToImage().scale(search_tab_left.width(), m_searchFrame->height())); + p.drawPixmap( 0, 0, search_tab_left ); + + search_tab_right.convertFromImage( search_tab_right.convertToImage().scale(search_tab_right.width(), m_searchFrame->height())); + p.drawPixmap( m_searchFrame->width()-search_tab_right.width(), 0, search_tab_right ); + } + else { + search_tab_top_center.convertFromImage( search_tab_top_center.convertToImage().scale(search_tab_top_center.width(), m_searchFrame->height())); + p.drawTiledPixmap( search_tab_top_left.width(), 0, m_searchFrame->width()-search_tab_top_left.width()-search_tab_top_right.width(), m_searchFrame->height(), search_tab_top_center ); + + search_tab_top_left.convertFromImage( search_tab_top_left.convertToImage().scale(search_tab_top_left.width(), m_searchFrame->height())); + p.drawPixmap( 0, 0, search_tab_top_left ); + + search_tab_top_right.convertFromImage( search_tab_top_right.convertToImage().scale(search_tab_top_right.width(), m_searchFrame->height())); + p.drawPixmap( m_searchFrame->width()-search_tab_top_right.width(), 0, search_tab_top_right ); + } + } + else + m_tabBar->deactivateTabs(false); + + p.end(); + m_searchFrame->setPaletteBackgroundPixmap( canvas ); +} + +void KMenu::stackWidgetRaised(TQWidget* raiseWidget) +{ + paintSearchTab(raiseWidget == m_searchWidget); + + if (raiseWidget == m_browserView) { + if ( m_tabBar->currentTab() == ApplicationsTab) + slotGoSubMenu(TQString::null); + if (m_browserDirty ) { + createNewProgramList(); + m_browserView->prepareRightMove(); + m_browserView->currentView()->clear(); + fillSubMenu(TQString::null, m_browserView->currentView()); + m_browserDirty = false; + } + } + else if (raiseWidget == m_recentlyView) { + if (m_recentDirty) + updateRecent(); + } + else if (raiseWidget == m_exitView) { + if (m_tabBar->currentTab() == LeaveTab) + slotGoExitMainMenu(); + } + + +#warning TQtab fixme +#if 0 + else if (raiseWidget == m_systemView) + frame = m_system; + else if (raiseWidget == m_favoriteView) + frame = m_btnFavorites; + if (!frame) + return; + + if ( m_activeTab == frame ) + return; + + paintTab( m_activeTab, false ); + paintTab( frame, true ); + + // if (dynamic_cast(raiseWidget)) + // m_activeTab->setFocusProxy(static_cast(raiseWidget)->viewport()); + + if (0 && /*raiseWidget == m_stacker->visibleWidget() &&*/ !raiseWidget->hasFocus()) { + + if (dynamic_cast(raiseWidget)) + static_cast(raiseWidget)->viewport()->setFocus(); + else + raiseWidget->setFocus(); + } + + m_activeTab = frame; + + m_sloppyRegion = TQRegion(); + m_sloppyTimer.stop(); + + ItemView* view; + if (raiseWidget == m_browserView) + view = m_browserView->currentView(); + else if (raiseWidget == m_exitView) + view = m_exitView->currentView(); + else + view = dynamic_cast(m_stacker->visibleWidget()); + if (view && !view->selectedItem()) { + if (view->firstChild() && view->firstChild()->isSelectable()) { + view->setSelected(view->firstChild(),true); + } + else if (view->childCount()>1) { + view->setSelected(view->firstChild()->itemBelow(),true); + } + } +#endif +} + +void KMenu::paletteChanged() +{ +} + +void KMenu::tabClicked(TQTab* t) +{ + if (t==m_tabs[ApplicationsTab]) + slotGoSubMenu(TQString::null); + else if (t==m_tabs[LeaveTab]) + slotGoExitMainMenu(); +} + +void KMenu::slotGoBack() +{ + goSubMenu( m_browserView->currentView()->backPath() ); +} + +void KMenu::slotGoExitMainMenu() +{ + if (m_exitView->currentView()==m_exitView->rightView()) { + m_exitView->prepareLeftMove(false); + m_exitView->showBackButton(false); + m_exitView->flipScroll(TQString::null); + } +} + +void KMenu::slotGoExitSubMenu(const TQString& url) +{ + m_exitView->prepareRightMove(); + m_exitView->showBackButton(true); + + int nId = serviceMenuEndId() + 1; + int index = 1; + + if (url=="kicker:/restart/") { + TQStringList rebootOptions; + int def, cur; + if ( DM().bootOptions( rebootOptions, def, cur ) ) + { + if ( cur == -1 ) + cur = def; + + int boot_index = 0; + TQStringList::ConstIterator it = rebootOptions.begin(); + for (; it != rebootOptions.end(); ++it, ++boot_index) + { + + TQString option = i18n( "Start '%1'" ).arg( *it ); + if (boot_index == cur) + option = i18n("Start '%1' (current)").arg( *it ); + m_exitView->rightView()->insertItem( "reload", option, + i18n( "Restart and boot directly into '%1'").arg( *it ), + TQString( "kicker:/restart_%1" ).arg( boot_index ), nId++, index++ ); + } + m_exitView->rightView()->insertHeader( nId++, "kicker:/restart/" ); + } + } + else /*if (url=="kicker:/switchuser/") */{ + m_exitView->rightView()->insertItem( "switchuser", i18n( "Start New Session" ), + i18n( "Start a parallel session" ), "kicker:/switchuser", nId++, index++ ); + + m_exitView->rightView()->insertItem( "lock", i18n( "Lock Current && Start New Session").replace("&&","&"), + i18n( "Lock screen and start a parallel session" ), "kicker:/switchuserafterlock", nId++, index++ ); + + SessList sess; + if (DM().localSessions( sess )) { + if (sess.count()>1) + m_exitView->rightView()->insertSeparator( nId++, TQString::null, index++ ); + for (SessList::ConstIterator it = sess.begin(); it != sess.end(); ++it) { + if ((*it).vt && !(*it).self) { + TQString user, loc; + DM().sess2Str2( *it, user, loc ); + TQStringList list = TQStringList::split(":", user); + m_exitView->rightView()->insertItem( "switchuser", i18n( "Switch to Session of User '%1'").arg(list[0]), + i18n("Session: %1").arg(list[1].mid(1)+", "+loc) , TQString("kicker:/switchuser_%1").arg((*it).vt), nId++, index++ ); + } + } + } + + m_exitView->rightView()->insertHeader( nId++, "kicker:/switchuser/" ); + } + m_exitView->flipScroll(TQString::null); +} + +void KMenu::slotGoSubMenu(const TQString& relPath) +{ + goSubMenu(relPath); +} + +void KMenu::goSubMenu(const TQString& relPath, bool keyboard) +{ + if ( relPath.startsWith( "kicker:/goup/" ) ) + { + TQString rel = relPath.mid( strlen( "kicker:/goup/" ) ); + int index = rel.length() - 1; + if ( rel.endsWith( "/" ) ) + index--; + index = rel.findRev( '/', index ); + kdDebug() << "goup, rel '" << rel << "' " << index << endl; + TQString currel = rel; + rel = rel.left( index + 1 ); + if ( rel == "/" ) + rel = TQString::null; + + kdDebug() << "goup, rel '" << rel << "' " << rel.isEmpty() << endl; + fillSubMenu( rel, m_browserView->prepareLeftMove() ); + m_browserView->flipScroll(keyboard ? currel : TQString::null); + return; + } else if (relPath.isEmpty()) + { + if (m_browserView->currentView()->path.isEmpty()) + return; + fillSubMenu( relPath, m_browserView->prepareLeftMove() ); + } else if ( relPath.startsWith( "kicker:/new/" ) ) + { + ItemView* view = m_browserView->prepareRightMove(); + m_browserView->showBackButton( true ); + + int nId = serviceMenuEndId() + 1; + view->insertHeader( nId++, "new/" ); + int index = 2; + for (TQStringList::ConstIterator it = m_newInstalledPrograms.begin(); + it != m_newInstalledPrograms.end(); ++it) { + KService::Ptr p = KService::serviceByStorageId((*it)); + view->insertMenuItem(p, nId++, index++); + } + } else + { + //m_browserView->clear(); + fillSubMenu(relPath, m_browserView->prepareRightMove()); + } + m_browserView->flipScroll(keyboard ? "kicker:/goup/": TQString::null); +} + +void KMenu::fillSubMenu(const TQString& relPath, ItemView *view) +{ + kdDebug() << "fillSubMenu() " << relPath << endl; + KServiceGroup::Ptr root = KServiceGroup::group(relPath); + Q_ASSERT( root ); + + KServiceGroup::List list = root->entries(true, true, true, KickerSettings:: + menuEntryFormat() == KickerSettings::DescriptionAndName || KickerSettings::menuEntryFormat() + == KickerSettings::DescriptionOnly); + + int nId = serviceMenuStartId(); + m_browserView->showBackButton( !relPath.isEmpty() ); + if ( !relPath.isEmpty() ) + { + view->insertHeader( nId++, relPath ); + } + else if ( m_newInstalledPrograms.count() ) { + KMenuItem *item = view->insertItem( "clock", i18n( "New Applications" ), + TQString::null, "kicker:/new/", nId++, -1 ); + item->setHasChildren( true ); + view->insertSeparator( nId++, TQString::null, -1 ); + } + + view->path = relPath; + + fillMenu (root, list, relPath, view, nId); +} + +void KMenu::fillMenu(KServiceGroup::Ptr& +#ifdef KDELIBS_SUSE + _root +#endif + , KServiceGroup::List& _list, + const TQString& _relPath, + ItemView* view, + int& id) +{ + bool separatorNeeded = false; + KServiceGroup::List::ConstIterator it = _list.begin(); +#ifdef KDELIBS_SUSE + KSortableValueList,TQCString> slist; + KSortableValueList,TQCString> glist; + TQMap specialTitle; + TQMap categoryIcon; + TQMap shortenedMenuPath; +#endif + + for (; it != _list.end(); ++it) + { + KSycocaEntry * e = *it; + + if (e->isType(KST_KServiceGroup)) + { + KServiceGroup::Ptr g(static_cast(e)); +#ifdef KDELIBS_SUSE + if ( true /*KickerSettings::reduceMenuDepth()*/ && g->SuSEshortMenu() ){ + KServiceGroup::List l = g->entries(true, true /*excludeNoDisplay_*/ ); + if ( l.count() == 1 ) { + // the special case, we want to short the menu. + // TOFIX? : this works only for one level + KServiceGroup::List::ConstIterator _it=l.begin(); + KSycocaEntry *_e = *_it; + if (_e->isType(KST_KService)) { + KService::Ptr s(static_cast(_e)); + TQString key; + if ( g->SuSEgeneralDescription() ) { + // we use the application name + key = s->name(); + } + else { + // we use the normal menu description + key = s->name(); + if( !s->genericName().isEmpty() && g->caption()!=s->genericName()) { + if (KickerSettings::menuEntryFormat() == KickerSettings::NameAndDescription) + key = s->name() + " (" + g->caption() + ")"; + else if (KickerSettings::menuEntryFormat() == KickerSettings::DescriptionAndName) + key = g->caption() + " (" + s->name() + ")"; + else if (KickerSettings::menuEntryFormat() == KickerSettings::DescriptionOnly) + key = g->caption(); + } + } + specialTitle.insert( _e->name(), key ); + categoryIcon.insert( _e->name(), g->icon() ); + slist.insert( key.local8Bit(), _e ); + shortenedMenuPath.insert( _e->name(), g->relPath() ); + // and escape from here + continue; + } + } + } + glist.insert( g->caption().local8Bit(), e ); + }else if( e->isType(KST_KService)) { + KService::Ptr s(static_cast(e)); + slist.insert( s->name().local8Bit(), e ); + } else + slist.insert( e->name().local8Bit(), e ); + } + + _list = _root->SuSEsortEntries( slist, glist, true /*excludeNoDisplay_*/, true ); + it = _list.begin(); + + for (; it != _list.end(); ++it) { + + KSycocaEntry * e = *it; + + if (e->isType(KST_KServiceGroup)) { + + KServiceGroup::Ptr g(static_cast(e)); + if ( true /*KickerSettings::reduceMenuDepth()*/ && g->SuSEshortMenu() ){ + KServiceGroup::List l = g->entries(true, true /*excludeNoDisplay_*/ ); + if ( l.count() == 1 ) { + continue; + } + } + // standard sub menu +#endif + TQString groupCaption = g->caption(); + + // Avoid adding empty groups. + KServiceGroup::Ptr subMenuRoot = KServiceGroup::group(g->relPath()); + + int nbChildCount = subMenuRoot->childCount(); + if (nbChildCount == 0 && !g->showEmptyMenu()) + { + continue; + } + + bool is_description = KickerSettings::menuEntryFormat() == KickerSettings::DescriptionAndName || + KickerSettings::menuEntryFormat() == KickerSettings::DescriptionOnly; + + TQString inlineHeaderName = g->showInlineHeader() ? groupCaption : ""; + + if ( nbChildCount == 1 && g->allowInline() && g->inlineAlias()) + { + KServiceGroup::Ptr element = KServiceGroup::group(g->relPath()); + if ( element ) + { + //just one element + + KServiceGroup::List listElement = element->entries(true, true, true, is_description ); + KSycocaEntry * e1 = *( listElement.begin() ); + if ( e1->isType( KST_KService ) ) + { + KService::Ptr s(static_cast(e1)); + view->insertMenuItem(s, id++, -1, 0); + continue; + } + } + } + + if (g->allowInline() && ((nbChildCount <= g->inlineValue() ) || (g->inlineValue() == 0))) + { + //inline all entries + KServiceGroup::Ptr rootElement = KServiceGroup::group(g->relPath()); + + if (!rootElement || !rootElement->isValid()) + { + break; + } + + + KServiceGroup::List listElement = rootElement->entries(true, true, true, is_description ); + +#if 0 + if ( !g->inlineAlias() && !inlineHeaderName.isEmpty() ) + { + int mid = view->insertItem(new PopupMenuTitle(inlineHeaderName, font()), id++, id, 0); + m_browserView->setItemEnabled( mid, false ); + } +#endif + + fillMenu( rootElement, listElement, g->relPath(), 0, id ); + continue; + } + + // Ignore dotfiles. + if ((g->name().at(0) == '.')) + { + continue; + } + + KMenuItem *item = view->insertItem(g->icon(), groupCaption, TQString::null, g->relPath(), id++, -1); + item->setMenuPath(g->relPath()); + item->setHasChildren( true ); + +#warning FIXME +#if 0 + PanelServiceMenu * m = + newSubMenu(g->name(), g->relPath(), this, g->name().utf8(), inlineHeaderName); + m->setCaption(groupCaption); + + TQIconSet iconset = KickerLib::menuIconSet(g->icon()); + + if (separatorNeeded) + { + insertSeparator(); + separatorNeeded = false; + } + + int newId = insertItem(iconset, groupCaption, m, id++); + entryMap_.insert(newId, static_cast(g)); + // We have to delete the sub menu our selves! (See TQt docs.) + subMenus.append(m); +#endif + } + if (e->isType(KST_KService)) + { + KService::Ptr s(static_cast(e)); + if (_relPath.isEmpty()) { + TQStringList favs = KickerSettings::favorites(); + if (favs.find(s->storageId())!=favs.end()) + continue; + } +#ifdef KDELIBS_SUSE + KMenuItem *item = view->insertMenuItem(s, id++, -1, 0, TQString::null, specialTitle[s->name()], categoryIcon[s->name()] ); + if (shortenedMenuPath[s->name()].isEmpty()) + item->setMenuPath(_relPath+s->menuId()); + else + item->setMenuPath(shortenedMenuPath[s->name()]+s->menuId()); +#else + KMenuItem *item = view->insertMenuItem(s, id++, -1); + item->setMenuPath(_relPath+s->menuId()); +#endif + } + else if (e->isType(KST_KServiceSeparator)) + { + separatorNeeded = true; + } + } + + view->slotMoveContent(); +} + +void KMenu::initialize() +{ + static bool m_initialized=false; + if (m_initialized) + return; + m_initialized = true; + + kdDebug(1210) << "KMenu::initialize()" << endl; + + // in case we've been through here before, let's disconnect + disconnect(kapp, TQT_SIGNAL(kdisplayPaletteChanged()), + this, TQT_SLOT(paletteChanged())); + connect(kapp, TQT_SIGNAL(kdisplayPaletteChanged()), + this, TQT_SLOT(paletteChanged())); + + /* + If the user configured ksmserver to + */ + KConfig ksmserver("ksmserverrc", false, false); + ksmserver.setGroup("General"); + connect( m_branding, TQT_SIGNAL(clicked()), TQT_SLOT(slotOpenHomepage())); + m_tabBar->setTabEnabled(LeaveTab, kapp->authorize("logout")); + + // load search field history + TQStringList histList = KickerSettings::history(); + int maxHistory = KickerSettings::historyLength(); + + bool block = m_kcommand->signalsBlocked(); + m_kcommand->blockSignals( true ); + m_kcommand->setMaxCount( maxHistory ); + m_kcommand->setHistoryItems( histList ); + m_kcommand->blockSignals( block ); + + TQStringList compList = KickerSettings::completionItems(); + if( compList.isEmpty() ) + m_kcommand->completionObject()->setItems( histList ); + else + m_kcommand->completionObject()->setItems( compList ); + + KCompletionBox* box = m_kcommand->completionBox(); + if (box) + box->setActivateOnSelect( false ); + + m_finalFilters = KURIFilter::self()->pluginNames(); + m_finalFilters.remove("kuriikwsfilter"); + + m_middleFilters = m_finalFilters; + m_middleFilters.remove("localdomainurifilter"); + + TQStringList favs = KickerSettings::favorites(); + if (favs.isEmpty()) { + TQFile f(locate("data", "kicker/default-favs")); + if (f.open(IO_ReadOnly)) { + TQTextStream is(&f); + + while (!is.eof()) + favs << is.readLine(); + + f.close(); + } + KickerSettings::setFavorites(favs); + KickerSettings::writeConfig(); + } + + int nId = serviceMenuEndId() + 1; + int index = 1; + for (TQStringList::ConstIterator it = favs.begin(); it != favs.end(); ++it) + { + if ((*it)[0]=='/') { + KDesktopFile df((*it),true); + TQString url = df.readURL(); + if (!KURL(url).isLocalFile() || TQFile::exists(url.replace("file://",TQString::null))) + m_favoriteView->insertItem(df.readIcon(),df.readName(),df.readGenericName(), url, nId++, index++); + } + else { + KService::Ptr p = KService::serviceByStorageId((*it)); + m_favoriteView->insertMenuItem(p, nId++, index++); + } + } + + //nId = m_favoriteView->insertSeparator( nId, TQString::null, index++ ); +// m_favoriteView->insertDocument(KURL("help:/khelpcenter/userguide/index.html"), nId++); + + insertStaticItems(); + + m_stacker->raiseWidget (m_favoriteView); +} + +void KMenu::insertStaticExitItems() +{ + int nId = serviceMenuEndId() + 1; + int index = 1; + + m_exitView->leftView()->insertSeparator( nId++, i18n("Session"), index++ ); + if (kapp->authorize("logout")) + m_exitView->leftView()->insertItem( "undo", i18n( "Logout" ), + i18n( "End session" ), "kicker:/logout", nId++, index++ ); + if (kapp->authorize("lock_screen")) + m_exitView->leftView()->insertItem( "lock", i18n( "Lock" ), + i18n( "Lock screen" ), "kicker:/lock", nId++, index++ ); + + KConfig ksmserver("ksmserverrc", false, false); + ksmserver.setGroup("General"); + if (ksmserver.readEntry( "loginMode" ) == "restoreSavedSession") + { + m_exitView->leftView()->insertItem("filesave", i18n("Save Session"), + i18n("Save current Session for next login"), + "kicker:/savesession", nId++, index++ ); + } + if (DM().isSwitchable() && kapp->authorize("switch_user")) + { + KMenuItem *switchuser = m_exitView->leftView()->insertItem( "switchuser", i18n( "Switch User" ), + i18n( "Manage parallel sessions" ), "kicker:/switchuser/", nId++, index++ ); + switchuser->setHasChildren(true); + } + + bool maysd = false; + if (ksmserver.readBoolEntry( "offerShutdown", true ) && DM().canShutdown()) + maysd = true; + + if ( maysd ) + { + m_exitView->leftView()->insertSeparator( nId++, i18n("System"), index++ ); + m_exitView->leftView()->insertItem( "exit", i18n( "Shutdown Computer" ), + i18n( "Turn off computer" ), "kicker:/shutdown", nId++, index++ ); + + m_exitView->leftView()->insertItem( "reload", i18n( "&Restart Computer" ).replace("&",""), + i18n( "Restart and boot the default system" ), + "kicker:/restart", nId++, index++ ); + + insertSuspendOption(nId, index); + + int def, cur; + TQStringList dummy_opts; + if ( DM().bootOptions( dummy_opts, def, cur ) ) + { + + KMenuItem *restart = m_exitView->leftView()->insertItem( "reload", i18n( "Start Operating System" ), + i18n( "Restart and boot another operating system" ), + "kicker:/restart/", nId++, index++ ); + restart->setHasChildren(true); + } + } +} + +void KMenu::insertStaticItems() +{ + insertStaticExitItems(); + + int nId = serviceMenuEndId() + 10; + int index = 1; + + m_systemView->insertSeparator( nId++, i18n("Applications"), index++); + + KService::Ptr p = KService::serviceByStorageId("/usr/share/applications/YaST.desktop"); + m_systemView->insertMenuItem(p, nId++, index++); + + m_systemView->insertItem( "info", i18n( "System Information" ), + "sysinfo:/", "sysinfo:/", nId++, index++ ); + + m_systemView->insertSeparator( nId++, i18n("System Folders"), index++ ); + + m_systemView->insertItem( "folder_home", i18n( "Home Folder" ), + TQDir::homeDirPath(), "file://"+TQDir::homeDirPath(), nId++, index++ ); + + if ( KStandardDirs::exists( KGlobalSettings::documentPath() + "/" ) ) + { + TQString documentPath = KGlobalSettings::documentPath(); + if ( documentPath.endsWith( "/" ) ) + documentPath = documentPath.left( documentPath.length() - 1 ); + if (documentPath!=TQDir::homeDirPath()) + m_systemView->insertItem( "folder_man", i18n( "My Documents" ), documentPath, documentPath, nId++, index++ ); + } + + m_systemView->insertItem( "network", i18n( "Network Folders" ), + "remote:/", "remote:/", nId++, index++ ); + + m_mediaWatcher = new MediaWatcher( this ); + connect( m_mediaWatcher, TQT_SIGNAL( mediumChanged() ), TQT_SLOT( updateMedia() ) ); + m_media_id = 0; + + connect(&m_mediaFreeTimer, TQT_SIGNAL(timeout()), TQT_SLOT( updateMedia())); +} + +int KMenu::insertClientMenu(KickerClientMenu *) +{ +#if 0 + int id = client_id; + clients.insert(id, p); + return id; +#endif + return 0; +} + +void KMenu::removeClientMenu(int) +{ +#if 0 + clients.remove(id); + slotClear(); +#endif +} + +extern int kicker_screen_number; + +void KMenu::slotLock() +{ + kdDebug() << "slotLock " << endl; + accept(); + TQCString appname( "kdesktop" ); + if ( kicker_screen_number ) + appname.sprintf("kdesktop-screen-%d", kicker_screen_number); + kapp->dcopClient()->send(appname, "KScreensaverIface", "lock()", ""); +} + +void KMenu::slotOpenHomepage() +{ + accept(); + kapp->invokeBrowser("http://opensuse.org"); +} + +void KMenu::slotLogout() +{ + kapp->requestShutDown(); +} + +void KMenu::slotPopulateSessions() +{ + int p = 0; + DM dm; + + sessionsMenu->clear(); + if (kapp->authorize("start_new_session") && (p = dm.numReserve()) >= 0) + { + if (kapp->authorize("lock_screen")) + sessionsMenu->insertItem(/*SmallIconSet("lockfork"),*/ i18n("Lock Current && Start New Session"), 100 ); + sessionsMenu->insertItem(SmallIconSet("fork"), i18n("Start New Session"), 101 ); + if (!p) { + sessionsMenu->setItemEnabled( 100, false ); + sessionsMenu->setItemEnabled( 101, false ); + } + sessionsMenu->insertSeparator(); + } + SessList sess; + if (dm.localSessions( sess )) + for (SessList::ConstIterator it = sess.begin(); it != sess.end(); ++it) { + int id = sessionsMenu->insertItem( DM::sess2Str( *it ), (*it).vt ); + if (!(*it).vt) + sessionsMenu->setItemEnabled( id, false ); + if ((*it).self) + sessionsMenu->setItemChecked( id, true ); + } +} + +void KMenu::slotSessionActivated( int ent ) +{ + if (ent == 100) + doNewSession( true ); + else if (ent == 101) + doNewSession( false ); + else if (!sessionsMenu->isItemChecked( ent )) + DM().lockSwitchVT( ent ); +} + +void KMenu::doNewSession( bool lock ) +{ + int result = KMessageBox::warningContinueCancel( + kapp->desktop()->screen(kapp->desktop()->screenNumber(this)), + i18n("

You have chosen to open another desktop session.
" + "The current session will be hidden " + "and a new login screen will be displayed.
" + "An F-key is assigned to each session; " + "F%1 is usually assigned to the first session, " + "F%2 to the second session and so on. " + "You can switch between sessions by pressing " + "Ctrl, Alt and the appropriate F-key at the same time. " + "Additionally, the KDE Panel and Desktop menus have " + "actions for switching between sessions.

") + .arg(7).arg(8), + i18n("Warning - New Session"), + KGuiItem(i18n("&Start New Session"), "fork"), + ":confirmNewSession", + KMessageBox::PlainCaption | KMessageBox::Notify); + + if (result==KMessageBox::Cancel) + return; + + if (lock) + slotLock(); + + DM().startReserve(); +} + +void KMenu::searchAccept() +{ + TQString cmd = m_kcommand->currentText().stripWhiteSpace(); + + bool logout = (cmd == "logout"); + bool lock = (cmd == "lock"); + + addToHistory(); + + if ( !logout && !lock ) + { + // first try if we have any search action + if (m_searchResultsWidget->currentItem()) { + m_searchResultsWidget->slotItemClicked(m_searchResultsWidget->currentItem()); + return; + } + } + + accept(); + saveConfig(); + + if ( logout ) + { + kapp->propagateSessionManager(); + kapp->requestShutDown(); + } + if ( lock ) + { + TQCString 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()", ""); + } +} + +bool KMenu::runCommand() +{ + kdDebug() << "runCommand() " << m_kcommand->lineEdit()->text() << endl; + // Ignore empty commands... + if ( m_kcommand->lineEdit()->text().isEmpty() ) + return true; + + accept(); + + if (input_timer->isActive ()) + input_timer->stop (); + + // Make sure we have an updated data + parseLine( true ); + + bool block = m_kcommand->signalsBlocked(); + m_kcommand->blockSignals( true ); + m_kcommand->clearEdit(); + m_kcommand->setFocus(); + m_kcommand->reset(); + m_kcommand->blockSignals( block ); + + + TQString cmd; + KURL uri = m_filterData->uri(); + if ( uri.isLocalFile() && !uri.hasRef() && uri.query().isEmpty() ) + cmd = uri.path(); + else + cmd = uri.url(); + + TQString exec; + + switch( m_filterData->uriType() ) + { + case KURIFilterData::LOCAL_FILE: + case KURIFilterData::LOCAL_DIR: + case KURIFilterData::NET_PROTOCOL: + case KURIFilterData::HELP: + { + // No need for kfmclient, KRun does it all (David) + (void) new KRun( m_filterData->uri(), parentWidget()); + return false; + } + case KURIFilterData::EXECUTABLE: + { + if( !m_filterData->hasArgsAndOptions() ) + { + // Look for desktop file + KService::Ptr service = KService::serviceByDesktopName(cmd); + if (service && service->isValid() && service->type() == "Application") + { + notifyServiceStarted(service); + KRun::run(*service, KURL::List()); + return false; + } + } + } + // fall-through to shell case + case KURIFilterData::SHELL: + { + if (kapp->authorize("shell_access")) + { + exec = cmd; + + if( m_filterData->hasArgsAndOptions() ) + cmd += m_filterData->argsAndOptions(); + + break; + } + else + { + KMessageBox::sorry( this, i18n("
%1
\n" + "You do not have permission to execute " + "this command.") + .arg( TQStyleSheet::convertFromPlainText(cmd) )); + return true; + } + } + case KURIFilterData::UNKNOWN: + case KURIFilterData::ERROR: + default: + { + // Look for desktop file + KService::Ptr service = KService::serviceByDesktopName(cmd); + if (service && service->isValid() && service->type() == "Application") + { + notifyServiceStarted(service); + KRun::run(*service, KURL::List(), this); + return false; + } + + service = KService::serviceByName(cmd); + if (service && service->isValid() && service->type() == "Application") + { + notifyServiceStarted(service); + KRun::run(*service, KURL::List(), this); + return false; + } + + KMessageBox::sorry( this, i18n("
%1
\n" + "Could not run the specified command.") + .arg( TQStyleSheet::convertFromPlainText(cmd) )); + return true; + } + } + + if ( KRun::runCommand( cmd, exec, m_iconName ) ) + return false; + + KMessageBox::sorry( this, i18n("
%1
\n" + "The specified command does not exist.").arg(cmd) ); + return true; // Let the user try again... +} + +void KMenu::show() +{ + m_isShowing = true; + emit aboutToShow(); + + initialize(); + + PanelPopupButton *kButton = MenuManager::the()->findKButtonFor( this ); + if (kButton) + { + TQPoint center = kButton->center(); + TQRect screen = TQApplication::desktop()->screenGeometry( center ); + setOrientation((center.y()-screen.y()raiseWidget(FavoriteTab); + m_kcommand->clear(); + current_query.clear(); + m_kcommand->setFocus(); + + // we need to reenable it + m_toolTipsEnabled = TQToolTip::isGloballyEnabled(); + TQToolTip::setGloballyEnabled(KickerSettings::showToolTips()); + + KMenuBase::show(); + m_isShowing = false; +} + +void KMenu::setOrientation(MenuOrientation orientation) +{ + if (m_orientation == orientation) + return; + + m_orientation=orientation; + + m_resizeHandle->setCursor(m_orientation == BottomUp ? Qt::sizeBDiagCursor : Qt::sizeFDiagCursor); + + TQPixmap pix; + if ( m_orientation == BottomUp ) + pix.load( locate("data", "kicker/pics/search-gradient.png" ) ); + else + pix.load( locate("data", "kicker/pics/search-gradient-topdown.png" ) ); + + pix.convertFromImage( pix.convertToImage().scale(pix.width(), m_searchFrame->height())); + m_search->mainWidget()->setPaletteBackgroundPixmap( pix ); + m_resizeHandle->setPaletteBackgroundPixmap( pix ); + + m_tabBar->setShape( m_orientation == BottomUp + ? TQTabBar::RoundedBelow : TQTabBar::RoundedAbove); + + TQPixmap respix = TQPixmap( locate("data", "kicker/pics/resize_handle.png" ) ); + if ( m_orientation == TopDown ) { + TQWMatrix m; + m.rotate( 90.0 ); + respix=respix.xForm(m); + } + m_resizeHandle->setPixmap(respix); + + { + TQWidget *footer = m_footer->mainWidget(); + TQPixmap pix( 64, footer->height() ); + TQPainter p( &pix ); + p.fillRect( 0, 0, 64, footer->height(), m_branding->colorGroup().brush( TQColorGroup::Base ) ); + p.fillRect( 0, m_orientation == BottomUp ? footer->height() - 2 : 0, + 64, 3, KNewButton::self()->borderColor() ); + p.end(); + footer->setPaletteBackgroundPixmap( pix ); + } + + resizeEvent(new TQResizeEvent(sizeHint(), sizeHint())); +} + +void KMenu::showMenu() +{ + kdDebug() << "KMenu::showMenu()" << endl; + PanelPopupButton *kButton = MenuManager::the()->findKButtonFor(this); + if (kButton) + { + adjustSize(); + kButton->showMenu(); + } + else + { + show(); + } + kdDebug() << "end KMenu::showMenu()" << endl; +} + +void KMenu::hide() +{ + //kdDebug() << "KMenu::hide() from " << kdBacktrace() << endl; + + // TODO: hide popups + + emit aboutToHide(); + + if (m_popupMenu) { + m_popupMenu->deleteLater(); + m_popupMenu=0; + } + m_mediaFreeTimer.stop(); + + m_isresizing = false; + + KickerSettings::setKMenuWidth(width()); + KickerSettings::setKMenuHeight(height()); + KickerSettings::writeConfig(); + + TQToolTip::setGloballyEnabled(m_toolTipsEnabled); + + // remove focus from lineedit again, otherwise it doesn't kill its timers + m_stacker->raiseWidget(FavoriteTab); + + TQWidget::hide(); +} + +void KMenu::paintEvent(TQPaintEvent * e) +{ + KMenuBase::paintEvent(e); + + TQPainter p(this); + p.setClipRegion(e->region()); + + const BackgroundMode bgmode = backgroundMode(); + const TQColorGroup::ColorRole crole = TQPalette::backgroundRoleFromMode( bgmode ); + p.setBrush( colorGroup().brush( crole ) ); + + p.drawRect( 0, 0, width(), height() ); + int ypos = m_search->mainWidget()->geometry().bottom(); + + p.drawPixmap( 0, ypos, main_border_tl ); + p.drawPixmap( width() - main_border_tr.width(), ypos, main_border_tr ); + // p.drawPixmap( 0, ->y(), button_box_left ); +} + + +void KMenu::configChanged() +{ + RecentlyLaunchedApps::the().m_bNeedToUpdate = false; + RecentlyLaunchedApps::the().configChanged(); + + m_exitView->leftView()->clear(); + insertStaticExitItems(); +} + +// create and fill "recent" section at first +void KMenu::createRecentMenuItems() +{ + RecentlyLaunchedApps::the().init(); + + if (!KickerSettings::numVisibleEntries()) + KickerSettings::setNumVisibleEntries(5); + + int nId = serviceMenuEndId() + 1; + m_recentlyView->insertSeparator( nId++, i18n( "Applications" ), -1 ); + + TQStringList RecentApps; + + if (!KickerSettings::recentVsOften()) { + KickerSettings::setRecentVsOften(true); + RecentlyLaunchedApps::the().configChanged(); + RecentlyLaunchedApps::the().getRecentApps(RecentApps); + KickerSettings::setRecentVsOften(false); + RecentlyLaunchedApps::the().configChanged(); + } + else + RecentlyLaunchedApps::the().getRecentApps(RecentApps); + + + if (RecentApps.count() > 0) + { +// bool bSeparator = KickerSettings::showMenuTitles(); + int nIndex = 0; + + for (TQValueList::ConstIterator it = + RecentApps.begin(); it!=RecentApps.end(); ++it) + { + KService::Ptr s = KService::serviceByStorageId(*it); + if (!s) + { + RecentlyLaunchedApps::the().removeItem(*it); + } + else + m_recentlyView->insertMenuItem(s, nIndex++); + } + + } + + m_recentlyView->insertSeparator( nId++, i18n( "Documents" ), -1 ); + + TQStringList fileList = KRecentDocument::recentDocuments(); + kdDebug() << "createRecentMenuItems=" << fileList << endl; + for (TQStringList::ConstIterator it = fileList.begin(); + it != fileList.end(); + ++it) + m_recentlyView->insertRecentlyItem(*it, nId++); + +} + +void KMenu::clearSubmenus() +{ + // we don't need to delete these on the way out since the libloader + // handles them for us + if (TQApplication::closingDown()) + { + return; + } + + for (PopupMenuList::const_iterator it = dynamicSubMenus.constBegin(); + it != dynamicSubMenus.constEnd(); + ++it) + { + delete *it; + } + dynamicSubMenus.clear(); +} + +void KMenu::updateRecent() +{ + m_recentlyView->clear(); + + createRecentMenuItems(); + + m_recentDirty = false; +} + +void KMenu::popup(const TQPoint&, int) +{ + showMenu(); +} + +void KMenu::clearRecentAppsItems() +{ + RecentlyLaunchedApps::the().clearRecentApps(); + RecentlyLaunchedApps::the().save(); + RecentlyLaunchedApps::the().m_bNeedToUpdate = true; + updateRecent(); +} + +void KMenu::clearRecentDocsItems() +{ + KRecentDocument::clear(); + updateRecent(); +} + +void KMenu::searchChanged(const TQString & text) +{ + if (!text.isEmpty()) { + const TQColor on = TQColor( 244, 244, 244 ); + const TQColor off = TQColor( 181, 181, 181 ); + m_stacker->raiseWidget(m_searchWidget); + paintSearchTab(true); + } + + m_searchActions->clearSelection(); + m_searchResultsWidget->clearSelection(); + + if (input_timer->isActive ()) + input_timer->stop (); + input_timer->start (WAIT_BEFORE_QUERYING, TRUE); +} + +bool KMenu::dontQueryNow (const TQString& str) +{ + if (str.isEmpty ()) + return true; + if (str == current_query.get()) + return true; + int length = str.length (); + int last_whitespace = str.findRev (' ', -1); + if (last_whitespace == length-1) + return false; // if the user typed a space, search + if (last_whitespace >= length-2) + return true; // dont search if the user only typed one character + TQChar lastchar = str[length-1]; + if (lastchar == ":" || lastchar == "=") + return true; + return false; +} + +void KMenu::createNewProgramList() +{ + m_seenProgramsChanged = false; + m_seenPrograms = KickerSettings::firstSeenApps(); + m_newInstalledPrograms.clear(); + + m_currentDate = TQDate::currentDate().toString(Qt::ISODate); + + bool initialize = (m_seenPrograms.count() == 0); + + createNewProgramList(TQString::null); + + if (initialize) { + for (TQStringList::Iterator it = m_seenPrograms.begin(); it != m_seenPrograms.end(); ++it) + *(++it)="-"; + + m_newInstalledPrograms.clear(); + } + + if (m_seenProgramsChanged) { + KickerSettings::setFirstSeenApps(m_seenPrograms); + KickerSettings::writeConfig(); + } +} + +void KMenu::createNewProgramList(TQString relPath) +{ + KServiceGroup::Ptr group = KServiceGroup::group(relPath); + if (!group || !group->isValid()) + return; + + KServiceGroup::List list = group->entries(); + if (list.isEmpty()) + return; + + KServiceGroup::List::ConstIterator it = list.begin(); + for(; it != list.end(); ++it) { + KSycocaEntry *e = *it; + + if(e != 0) { + if(e->isType(KST_KServiceGroup)) { + KServiceGroup::Ptr g(static_cast(e)); + if(!g->noDisplay()) + createNewProgramList(g->relPath()); + } else if(e->isType(KST_KService)) { + KService::Ptr s(static_cast(e)); + if(s->type() == "Application" && !s->noDisplay() ) { + TQString shortStorageId = s->storageId().replace(".desktop",TQString::null); + TQStringList::Iterator it_find = m_seenPrograms.begin(); + TQStringList::Iterator it_end = m_seenPrograms.end(); + bool found = false; + for (; it_find != it_end; ++it_find) { + if (*(it_find)==shortStorageId) { + found = true; + break; + } + ++it_find; + } + if (!found) { + m_seenProgramsChanged=true; + m_seenPrograms+=shortStorageId; + m_seenPrograms+=m_currentDate; + if (m_newInstalledPrograms.find(s->storageId())==m_newInstalledPrograms.end()) + m_newInstalledPrograms+=s->storageId(); + } + else { + ++it_find; + if (*(it_find)!="-") { + TQDate date = TQDate::fromString(*(it_find),Qt::ISODate); + if (date.daysTo(TQDate::currentDate())<3) { + if (m_newInstalledPrograms.find(s->storageId())==m_newInstalledPrograms.end()) + m_newInstalledPrograms+=s->storageId(); + } + else { + m_seenProgramsChanged=true; + (*it_find)="-"; + } + } + } + } + } + } + } +} + +void KMenu::searchProgramList(TQString relPath) +{ + KServiceGroup::Ptr group = KServiceGroup::group(relPath); + if (!group || !group->isValid()) + return; + + KServiceGroup::List list = group->entries(); + if (list.isEmpty()) + return; + + KServiceGroup::List::ConstIterator it = list.begin(); + for(; it != list.end(); ++it) { + KSycocaEntry *e = *it; + + if(e != 0) { + if(e->isType(KST_KServiceGroup)) { + KServiceGroup::Ptr g(static_cast(e)); + if(!g->noDisplay()) + searchProgramList(g->relPath()); + } else if(e->isType(KST_KService)) { + KService::Ptr s(static_cast(e)); + if(s->type() == "Application" && !s->noDisplay() && !checkUriInMenu(s->desktopEntryPath())) { + if (!current_query.matches(s->name()+' '+s->genericName()+' '+s->exec()+' '+ + s->keywords().join(",")+' '+s->comment()+' '+group->caption()+' '+ + s->categories().join(",")) || !anotherHitMenuItemAllowed(APPS)) + continue; + + TQString input = current_query.get(); + int score = 0; + if (s->exec()==input) + score = 100; + else if (s->exec().find(input)==0) + score = 50; + else if (s->exec().find(input)!=-1) + score = 10; + else if (s->name().lower()==input) + score = 100; + else if (s->genericName().lower()==input) + score = 100; + else if (s->name().lower().find(input)==0) + score = 50; + else if (s->genericName().lower().find(input)==0) + score = 50; + else if (s->name().lower().find(input)!=-1) + score = 10; + else if (s->genericName().lower().find(input)!=-1) + score = 10; + + if (s->exec().find(' ')==-1) + score+=1; + + if (s->substituteUid()) + score-=1; + + if (s->noDisplay()) + score -= 100; + else if (s->terminal()) + score -= 50; + else + score += kMin(10, s->initialPreference()); + + TQString firstLine, secondLine; + if ((KickerSettings::DescriptionAndName || KickerSettings::menuEntryFormat() == KickerSettings::DescriptionOnly) && !s->genericName().isEmpty()) { + firstLine = s->genericName(); + secondLine = s->name(); + } + else { + firstLine = s->name(); + secondLine = s->genericName(); + } + + HitMenuItem *hit_item = new HitMenuItem (firstLine, secondLine, + s->desktopEntryPath(), TQString::null, 0, APPS, s->icon(), score); + if (hit_item == NULL) + continue; + + hit_item->service = s; + insertSearchResult(hit_item); + + TQString exe = s->exec(); + int pos = exe.find(' '); + if (pos>0) + exe=exe.left(pos); + m_programsInMenu+=KGlobal::dirs()->findExe(exe); + } + } + } + } +} + +void KMenu::searchBookmarks(KBookmarkGroup group) +{ + KBookmark bookmark = group.first(); + while(!bookmark.isNull()) { + if (bookmark.isGroup()) { + searchBookmarks(bookmark.toGroup()); + } else if (!bookmark.isSeparator() && !bookmark.isNull()) { + if (!current_query.matches(bookmark.fullText()+' '+bookmark.url().url()) || !anotherHitMenuItemAllowed(BOOKMARKS)) { + bookmark = group.next(bookmark); + continue; + } + + HitMenuItem *hit_item = new HitMenuItem (bookmark.fullText(), bookmark.fullText(), + bookmark.url(), TQString::null, 0, BOOKMARKS, bookmark.icon()); + + insertSearchResult(hit_item); + } + bookmark = group.next(bookmark); + } +} + +void KMenu::initSearch() +{ + if (!m_addressBook && KickerSettings::kickoffSearchAddressBook()) + m_addressBook = KABC::StdAddressBook::self( false ); + + if (!bookmarkManager) + bookmarkManager = KBookmarkManager::userBookmarksManager(); + + if (!m_search_plugin) { + m_search_plugin_interface = new TQObject( this, "m_search_plugin_interface" ); + new MyKickoffSearchInterface( this, m_search_plugin_interface, "kickoffsearch interface" ); + KTrader::OfferList offers = KTrader::self()->query("KickoffSearch/Plugin"); + + KService::Ptr service = *offers.begin(); + if (service) { + int errCode = 0; + m_search_plugin = KParts::ComponentFactory::createInstanceFromService + ( service, m_search_plugin_interface, 0, TQStringList(), &errCode); + } + } +} + +void KMenu::searchAddressbook() +{ + if (!KickerSettings::kickoffSearchAddressBook()) + return; + + if (!m_addressBook) + m_addressBook = KABC::StdAddressBook::self( false ); + + KABC::AddressBook::ConstIterator it = m_addressBook->begin(); + while (it!=m_addressBook->end()) { + if (!current_query.matches((*it).assembledName()+' '+(*it).fullEmail())) { + it++; + continue; + } + + HitMenuItem *hit_item; + TQString realName = (*it).realName(); + if (realName.isEmpty()) + realName=(*it).preferredEmail(); + + if (!(*it).preferredEmail().isEmpty()) { + if (!anotherHitMenuItemAllowed(ACTIONS)) { + it++; + continue; + } + + hit_item = new HitMenuItem (i18n("Send Email to %1").arg(realName), (*it).preferredEmail(), + "mailto:"+(*it).preferredEmail(), TQString::null, 0, ACTIONS, "mail_new"); + + insertSearchResult(hit_item); + } + + if (!anotherHitMenuItemAllowed(ACTIONS)) { + it++; + continue; + } + + hit_item = new HitMenuItem (i18n("Open Addressbook at %1").arg(realName), (*it).preferredEmail(), + "kaddressbook:/"+(*it).uid(), TQString::null, 0, ACTIONS, "kaddressbook"); + + insertSearchResult(hit_item); + + it++; + } +} + +TQString KMenu::insertBreaks(const TQString& text, TQFontMetrics fm, int width, TQString leadInsert) +{ + TQString result, line; + TQStringList words = TQStringList::split(' ', text); + + for(TQStringList::Iterator it = words.begin(); it != words.end(); ++it) { + if (fm.width(line+' '+*it) >= width) { + if (!result.isEmpty()) + result = result + '\n'; + result = result + line; + line = leadInsert + *it; + } + else + line = line + ' ' + *it; + } + if (!result.isEmpty()) + result = result + '\n'; + + return result + line; +} + +void KMenu::clearSearchResults(bool showHelp) +{ + m_searchResultsWidget->clear(); + m_searchResultsWidget->setFocusPolicy(showHelp ? TQWidget::NoFocus : TQWidget::StrongFocus); + setTabOrder(m_kcommand, m_searchResultsWidget); + + if (showHelp) { + const int width = m_searchResultsWidget->width()-10; + TQFontMetrics fm = m_searchResultsWidget->fontMetrics(); + + TQListViewItem* item; + item = new TQListViewItem( m_searchResultsWidget, insertBreaks(i18n("- Add ext:type to specify a file extension."), fm, width, " ") ); + item->setSelectable(false); + item->setMultiLinesEnabled(true); + item = new TQListViewItem( m_searchResultsWidget, insertBreaks(i18n("- When searching for a phrase, add quotes."), fm, width, " " ) ); + item->setSelectable(false); + item->setMultiLinesEnabled(true); + item = new TQListViewItem( m_searchResultsWidget, insertBreaks(i18n("- To exclude search terms, use the minus symbol in front."), fm, width, " " ) ); + item->setSelectable(false); + item->setMultiLinesEnabled(true); + item = new TQListViewItem( m_searchResultsWidget, insertBreaks(i18n("- To search for optional terms, use OR."), fm, width, " ") ); + item->setSelectable(false); + item->setMultiLinesEnabled(true); + item = new TQListViewItem( m_searchResultsWidget, insertBreaks(i18n("- You can use upper and lower case."), fm, width, " ") ); + item->setSelectable(false); + item->setMultiLinesEnabled(true); + item = new TQListViewItem( m_searchResultsWidget, i18n("Search Quick Tips")); + item->setSelectable(false); + } + + for (int i=0; ilineEdit()->text ().simplifyWhiteSpace (); + if (! return_pressed && dontQueryNow (query_str)) { + if (query_str.length()<3) + clearSearchResults(); + else { + if (m_searchResultsWidget->firstChild() && m_searchResultsWidget->firstChild()->isSelectable()) { + m_searchResultsWidget->setSelected(m_searchResultsWidget->firstChild(),true); + } + else if (m_searchResultsWidget->childCount()>1) { + m_searchResultsWidget->setSelected(m_searchResultsWidget->firstChild()->itemBelow(),true); + } + } + return; + } + kdDebug() << "Querying for [" << query_str << "]" << endl; + current_query.set(query_str); + + // reset search results + HitMenuItem *hit_item; + while ((hit_item = m_current_menu_items.take ()) != NULL) { + //kndDebug () << " (" << hit_item->id << "," << hit_item->category << ")" << endl; + delete hit_item; + } + + clearSearchResults(false); + m_searchPixmap->setMovie(TQMovie(locate( "data", "kicker/pics/search-running.mng" ))); + + resetOverflowCategory(); + + initCategoryTitlesUpdate(); + + // calculate ? + TQString cmd = query_str.stripWhiteSpace(); + if (!cmd.isEmpty() && (cmd[0].isNumber() || (cmd[0] == '(')) && + (TQRegExp("[a-zA-Z\\]\\[]").search(cmd) == -1)) + { + TQString result = calculate(cmd); + if (!result.isEmpty()) + { + categorised_hit_total[ACTIONS] ++; + HitMenuItem *hit_item = new HitMenuItem (i18n("%1 = %2").arg(query_str, result), TQString::null, + "kcalc", TQString::null, (++max_category_id [ACTIONS]), ACTIONS, "kcalc"); + int index = getHitMenuItemPosition (hit_item); + m_searchResultsWidget->insertItem(iconForHitMenuItem(hit_item), hit_item->display_name, + hit_item->display_info, KGlobal::dirs()->findExe("kcalc"), max_category_id [ACTIONS], index); + } + } + + // detect email address + if (emailRegExp.exactMatch(query_str)) { + categorised_hit_total[ACTIONS] ++; + HitMenuItem *hit_item = new HitMenuItem (i18n("Send Email to %1").arg(query_str), TQString::null, + "mailto:"+query_str, TQString::null, (++max_category_id [ACTIONS]), ACTIONS, "mail_new"); + int index = getHitMenuItemPosition (hit_item); + m_searchResultsWidget->insertItem(iconForHitMenuItem(hit_item), hit_item->display_name, hit_item->display_info, "mailto:"+query_str, max_category_id [ACTIONS], index); + } + + // quick own application search + m_programsInMenu.clear(); + searchProgramList(TQString::null); + + KURIFilterData filterData; + filterData.setData(query_str); + filterData.setCheckForExecutables(true); + + if (KURIFilter::self()->filterURI(filterData)) { + + TQString description; + TQString exe; + + switch (filterData.uriType()) { + case KURIFilterData::LOCAL_FILE: + description = i18n("Open Local File: %1").arg(filterData.uri().url()); + break; + case KURIFilterData::LOCAL_DIR: + description = i18n("Open Local Dir: %1").arg(filterData.uri().url()); + break; + case KURIFilterData::NET_PROTOCOL: + description = i18n("Open Remote Location: %1").arg(filterData.uri().url()); + break; + case KURIFilterData::SHELL: + case KURIFilterData::EXECUTABLE: + { + exe = KGlobal::dirs()->findExe(filterData.uri().url()); +#ifdef KDELIBS_SUSE + bool gimp_hack = false; + if (exe.endsWith("/bin/gimp")) { + TQStringList::ConstIterator it = m_programsInMenu.begin(); + for (; it != m_programsInMenu.end(); ++it) + if ((*it).find("bin/gimp-remote-")!=-1) { + gimp_hack = true; + break; + } + } +#endif + if (m_programsInMenu.find(exe)!=m_programsInMenu.end() +#ifdef KDELIBS_SUSE + || gimp_hack +#endif + ) + exe = TQString::null; + else if (kapp->authorize("shell_access")) + { + if( filterData.hasArgsAndOptions() ) + exe += filterData.argsAndOptions(); + + description = i18n("Run '%1'").arg(exe); + exe = "kicker:/runcommand"; + } + } + default: + break; + } + + if (!description.isEmpty()) { + categorised_hit_total[ACTIONS] ++; + HitMenuItem *hit_item = new HitMenuItem (description, TQString::null, + exe.isEmpty() ? filterData.uri() : exe, TQString::null, + (++max_category_id [ACTIONS]), ACTIONS, exe.isEmpty() ? "fileopen": "run"); + int index = getHitMenuItemPosition (hit_item); + m_searchResultsWidget->insertItem(iconForHitMenuItem(hit_item), hit_item->display_name, + hit_item->display_info, + exe.isEmpty() ? filterData.uri().url() : exe, max_category_id [ACTIONS], index); + } + } + + // search Konqueror bookmarks; + if (!bookmarkManager) + bookmarkManager = KBookmarkManager::userBookmarksManager(); + + if (query_str.length()>=3) + searchBookmarks(bookmarkManager->root()); + + // search KDE addressbook + if (query_str.length()>=3) + searchAddressbook(); + + updateCategoryTitles(); + + if (m_searchResultsWidget->childCount()>1) + m_searchResultsWidget->setSelected(m_searchResultsWidget->firstChild()->itemBelow(),true); + m_searchActions->clearSelection(); + + if (!m_search_plugin) + initSearch(); + + // start search plugin only with at least 3 characters + if (query_str.length()<3 || !m_search_plugin || (m_search_plugin && !m_search_plugin->daemonRunning()) ) { + m_searchPixmap->setPixmap( BarIcon( "find", 32 ) ); + fillOverflowCategory(); + if (query_str.length()>2 && m_current_menu_items.isEmpty()) + reportError (i18n("No matches found")); + return; + } + + if (m_search_plugin) { + m_search_plugin->query(current_query.get(), KickerSettings::DescriptionAndName || KickerSettings::menuEntryFormat() == KickerSettings::DescriptionOnly); + } +} + +bool KMenu::anotherHitMenuItemAllowed(int cat, bool count) +{ + // update number of hits in this category + if (count) + categorised_hit_total [cat] ++; + + // if number of hits in this category is more than allowed, dont process this + if (max_category_id [cat] - base_category_id [cat] < max_items(cat)) + return true; + + if (m_overflowCategoryState==None || (m_overflowCategoryState==Filling && m_overflowCategory==cat && + max_category_id [cat] + m_overflowList.count() - base_category_id [cat] < max_items(cat) * 2.0)) + return true; + + return false; +} + +void KMenu::addHitMenuItem(HitMenuItem* item) +{ + if (checkUriInMenu(item->uri)) + return; + + // if number of hits in this category is more than allowed, dont process this + if (!anotherHitMenuItemAllowed(item->category, false)) + return; + + insertSearchResult(item); +} + +void KMenu::insertSearchResult(HitMenuItem* item) +{ + if (m_overflowCategoryState==None) { + m_overflowCategoryState = Filling; + m_overflowCategory = item->category; + } + else if (m_overflowCategoryState==Filling && m_overflowCategory!=item->category) + m_overflowCategoryState = NotNeeded; + + if (max_category_id [item->category] - base_category_id [item->category] < max_items(item->category)) { + max_category_id [item->category]++; + item->id=max_category_id [item->category]; + + int index = getHitMenuItemPosition (item); + + kdDebug () << "Adding " << item->uri + << "(" << item->mimetype << ") with id=" + << max_category_id [item->category] << " at " << index << endl; + + KMenuItem *hit_item = m_searchResultsWidget->insertItem(iconForHitMenuItem(item), item->display_name, item->display_info, item->uri.url(), max_category_id [item->category], index); + hit_item->setService(item->service); + + kdDebug () << "Done inserting ... " << endl; + } + else if (m_overflowCategoryState==Filling && m_overflowCategory==item->category && + max_category_id [item->category] - base_category_id [item->category] < max_items(item->category) * 2) + m_overflowList.append(item); +} + +void KMenu::searchOver() +{ + m_searchPixmap->setPixmap( BarIcon( "find", 32 ) ); + fillOverflowCategory(); + if (m_current_menu_items.isEmpty()) { + kdDebug() << "No matches found" << endl; + reportError (i18n("No matches found")); + } + if (!m_searchResultsWidget->selectedItem() && !m_searchActions->selectedItem() && m_searchResultsWidget->childCount()>1) { + m_searchResultsWidget->setSelected(m_searchResultsWidget->firstChild()->itemBelow(),true); + } +} + +void KMenu::initCategoryTitlesUpdate() +{ + // Need to know if each category was updated with hits or had the first hit + // That way we know if we need to changetitle or inserttitle + already_added = new bool [num_categories]; + for (int i=0; iinsertSeparator(base_category_id [i], title, index); + kdDebug () << "Inserting heading with id=" << base_category_id[i] << " for " << categories[i] << " at " << index << endl; + } else { + // something was already displayed in this category + // update the title to reflect the total + sep = dynamic_cast( m_searchResultsWidget->findItem(base_category_id [i]) ); + if ( !sep ) + continue; + kdDebug () << "Changing heading of id=" << base_category_id[i] << " for " << categories[i] << endl; + } + + int max = max_items(i); + if (m_overflowCategoryState == Filling && m_overflowCategory == i) + max *= 2; + + if ( categorised_hit_total [i] > max ) { + if (m_kerryInstalled) + sep->setLink( i18n( "top %1 of %2" ).arg( max ).arg( categorised_hit_total [i] ), TQString( "kerry:/%1" ).arg( i ) ); + else + sep->setText( 0, i18n( "%1 (top %2 of %3)" ).arg( i18n(categories [i].utf8()) ).arg( max ).arg( categorised_hit_total [i] ) ); + } + else { + sep->setLink( TQString::null ); + } + } + delete[] already_added; + already_added = 0; +} + +TQString KMenu::iconForHitMenuItem(HitMenuItem *hit_item) +{ + // get the icon + if (!hit_item->icon.isEmpty()) + return hit_item->icon; + + if (hit_item->category == WEBHIST) { + TQString favicon = KMimeType::favIconForURL (hit_item->uri); + if (! favicon.isEmpty ()) + return favicon; + } + + if (mimetype_iconstore.contains (hit_item->mimetype)) + return (mimetype_iconstore [hit_item->mimetype]); + else { + KMimeType::Ptr mimetype_ptr = KMimeType::mimeType (hit_item->mimetype); + TQString mimetype_icon = mimetype_ptr->icon(TQString::null, FALSE); + mimetype_iconstore [hit_item->mimetype] = mimetype_icon; + return mimetype_icon; + } + return TQString::null; +} + +void KMenu::slotStartService(KService::Ptr ptr) +{ + accept(); + + addToHistory(); + KApplication::startServiceByDesktopPath(ptr->desktopEntryPath(), + TQStringList(), 0, 0, 0, "", true); + updateRecentlyUsedApps(ptr); +} + + +void KMenu::slotStartURL(const TQString& u) +{ + if ( u == "kicker:/goup/" ) { + // only m_exitView is connected to this slot, not m_browserView + slotGoExitMainMenu(); + return; + } + + if ( u == "kicker:/restart/" || u=="kicker:/switchuser/") { + slotGoExitSubMenu(u); + return; + } + + accept(); + + if ( u == "kicker:/lock" ) { + slotLock(); + } + else if ( u == "kicker:/logout" ) { +#ifdef KDELIBS_SUSE + TQByteArray params; + TQDataStream stream(params, IO_WriteOnly); + stream << 0 << -1 << ""; + + kapp->dcopClient()->send("ksmserver", "default", "logoutTimed(int,int,TQString)", params); +#else + DCOPRef mediamanager("ksmserver", "ksmserver"); + DCOPReply reply = mediamanager.call( "logoutTimed", (int)KApplication::ShutdownTypeNone, (int)KApplication::ShutdownModeDefault ); + if (!reply.isValid() && KMessageBox::Continue==KMessageBox::warningContinueCancel(this, i18n("Do you really want to end the session?"), + i18n("Logout Confirmation"), KGuiItem(i18n("Logout"),"undo"))) + kapp->requestShutDown( KApplication::ShutdownConfirmNo, + KApplication::ShutdownTypeNone, + KApplication::ShutdownModeDefault ); + +#endif + } + else if ( u == "kicker:/runcommand" ) + { + runCommand(); + } + else if ( u == "kicker:/shutdown" ) { +#ifdef KDELIBS_SUSE + TQByteArray params; + TQDataStream stream(params, IO_WriteOnly); + stream << 2 << -1 << ""; + + kapp->dcopClient()->send("ksmserver", "default", "logoutTimed(int,int,TQString)", params); +#else + if (KMessageBox::Continue==KMessageBox::warningContinueCancel(this, i18n("Do you really want to turn off the computer?"), + i18n("Shutdown Confirmation"), KGuiItem(i18n("Shutdown"),"exit"))) + kapp->requestShutDown( KApplication::ShutdownConfirmNo, + KApplication::ShutdownTypeHalt, + KApplication::ShutdownModeDefault ); +#endif + } + else if ( u == "kicker:/restart" ) { +#ifdef KDELIBS_SUSE + TQByteArray params; + TQDataStream stream(params, IO_WriteOnly); + stream << 1 << -1 << TQString::null; + + kapp->dcopClient()->send("ksmserver", "default", "logoutTimed(int,int,TQString)", params); +#else + if (KMessageBox::Continue==KMessageBox::warningContinueCancel(this, i18n("Do you really want to reset the computer and boot (another operating system)?"), + i18n("Restart Confirmation"), KGuiItem(i18n("Restart"),"reload"))) + kapp->requestShutDown( KApplication::ShutdownConfirmNo, + KApplication::ShutdownTypeReboot, + KApplication::ShutdownModeDefault ); +#endif + } +#ifdef KDELIBS_SUSE + else if ( u == "kicker:/suspend_disk" ) { + slotSuspend( 1 ); + } + else if ( u == "kicker:/suspend_ram" ) { + slotSuspend( 2 ); + } + else if ( u == "kicker:/standby" ) { + slotSuspend( 3 ); + } +#endif + else if ( u == "kicker:/savesession" ) { + TQByteArray data; + kapp->dcopClient()->send( "ksmserver", "default", + "saveCurrentSession()", data ); + } + else if ( u == "kicker:/switchuser" ) { + DM().startReserve(); + } + else if ( u == "kicker:/switchuserafterlock" ) { + slotLock(); + DM().startReserve(); + } + else if ( u.startsWith("kicker:/switchuser_") ) + DM().lockSwitchVT( u.mid(19).toInt() ); + else if ( u.startsWith("kicker:/restart_") ) { +#ifdef KDELIBS_SUSE + TQStringList rebootOptions; + int def, cur; + DM().bootOptions( rebootOptions, def, cur ); + + TQByteArray params; + TQDataStream stream(params, IO_WriteOnly); + stream << 1 << -1 << rebootOptions[u.mid(16).toInt()]; + + kapp->dcopClient()->send("ksmserver", "default", "logoutTimed(int,int,TQString)", params); +#else + KMessageBox::error( this, TQString( "Sorry, not implemented." )); +#endif + } +#warning restart entry not supported +#if 0 + else if ( u == "kicker:/restart_windows" ) { + if (KMessageBox::Continue==KMessageBox::warningContinueCancel(this, i18n("Do you really want to reset the computer and boot Microsoft Windows"), i18n("Start Windows Confirmation"), KGuiItem(i18n("Start Windows"),"reload"))) + KMessageBox::error( this, TQString( "kicker:/restart_windows is not yet implemented " ) ); + } +#endif + else if ( u.startsWith("kerry:/")) + { + TQByteArray data; + TQDataStream arg(data, IO_WriteOnly); + arg << m_kcommand->currentText() << kerry_categories[u.mid(7).toInt()]; + if (ensureServiceRunning("kerry")) + kapp->dcopClient()->send("kerry","search","search(TQString,TQString)", data); + } + else { + addToHistory(); + if (u.startsWith("kaddressbook:/")) { + KProcess *proc = new KProcess; + *proc << "kaddressbook" << "--uid" << u.mid(14); + proc->start(); + accept(); + return; + } else if (u.startsWith("note:/")) { + KProcess *proc = new KProcess; + *proc << "tomboy"; + *proc << "--open-note" << u; + if (!proc->start()) + KMessageBox::error(0,i18n("Could not start Tomboy.")); + return; + } + else if (u.startsWith("knotes:/") ) { + if (ensureServiceRunning("knotes")) { + TQByteArray data; + TQDataStream arg(data, IO_WriteOnly); + arg << u.mid(9,22); + + kapp->dcopClient()->send("knotes","KNotesIface","showNote(TQString)", data); + } + return; + } + + kapp->propagateSessionManager(); + (void) new KRun( u, parentWidget()); + } +} + +void KMenu::slotContextMenuRequested( TQListViewItem * item, const TQPoint & pos, int /*col*/ ) +{ + const TQObject* source = sender(); + + if (!item) + return; + + KMenuItem* kitem = dynamic_cast(item); + if (!kitem) + return; + + KFileItemList _items; + _items.setAutoDelete(true); + + if (dynamic_cast(item)) + return; + + m_popupService = kitem->service(); + m_popupPath.menuPath = kitem->menuPath(); + if (!m_popupService) { + m_popupPath.title = kitem->title(); + m_popupPath.description = kitem->description(); + m_popupPath.path = kitem->path(); + m_popupPath.icon = kitem->icon(); + + if (m_popupPath.path.startsWith(locateLocal("data", TQString::fromLatin1("RecentDocuments/")))) { + KDesktopFile df(m_popupPath.path,true); + m_popupPath.path=df.readURL(); + } + } + + m_popupMenu = new KPopupMenu(this); + connect(m_popupMenu, TQT_SIGNAL(activated(int)), TQT_SLOT(slotContextMenu(int))); + bool hasEntries = false; + + m_popupMenu->insertTitle(SmallIcon(kitem->icon()),kitem->title()); + + if (source==m_favoriteView) + { + hasEntries = true; + m_popupMenu->insertItem(SmallIconSet("remove"), + i18n("Remove From Favorites"), RemoveFromFavorites); + } + else if (!kitem->hasChildren() && !m_popupPath.path.startsWith("system:/") && + !m_popupPath.path.startsWith("kicker:/switchuser_") && !m_popupPath.path.startsWith("kicker:/restart_")) + { + hasEntries = true; + int num = m_popupMenu->insertItem(SmallIconSet("bookmark_add"), + i18n("Add to Favorites"), AddToFavorites); + + TQStringList favs = KickerSettings::favorites(); + if (m_popupService && favs.find(m_popupService->storageId())!=favs.end()) + m_popupMenu->setItemEnabled(num, false); + else { + TQStringList::Iterator it; + for (it = favs.begin(); it != favs.end(); ++it) + { + if ((*it)[0]=='/') + { + KDesktopFile df((*it),true); + if (df.readURL().replace("file://",TQString::null)==m_popupPath.path) + break; + } + } + if (it!=favs.end()) + m_popupMenu->setItemEnabled(num, false); + } + } + + if (source!=m_exitView) { + if (m_popupService || (!m_popupPath.path.startsWith("kicker:/") && !m_popupPath.path.startsWith("system:/") && !m_popupPath.path.startsWith("kaddressbook:/"))) { + if (hasEntries) + m_popupMenu->insertSeparator(); + + if (kapp->authorize("editable_desktop_icons") ) + { + hasEntries = true; + if (m_popupPath.menuPath.endsWith("/")) + m_popupMenu->insertItem(SmallIconSet("desktop"), + i18n("Add Menu to Desktop"), AddMenuToDesktop); + else + m_popupMenu->insertItem(SmallIconSet("desktop"), + i18n("Add Item to Desktop"), AddItemToDesktop); + } + if (kapp->authorizeKAction("kicker_rmb") && !Kicker::the()->isImmutable()) + { + hasEntries = true; + if (m_popupPath.menuPath.endsWith("/")) + m_popupMenu->insertItem(SmallIconSet("kicker"), + i18n("Add Menu to Main Panel"), AddMenuToPanel); + else + m_popupMenu->insertItem(SmallIconSet("kicker"), + i18n("Add Item to Main Panel"), AddItemToPanel); + } + if (kapp->authorizeKAction("menuedit") && !kitem->menuPath().isEmpty()) + { + hasEntries = true; + if (kitem->menuPath().endsWith("/")) + m_popupMenu->insertItem(SmallIconSet("kmenuedit"), i18n("Edit Menu"), EditMenu); + else + m_popupMenu->insertItem(SmallIconSet("kmenuedit"), i18n("Edit Item"), EditItem); + } + if (kapp->authorize("run_command") && (m_popupService || (!m_popupPath.menuPath.isEmpty() && !m_popupPath.menuPath.endsWith("/")))) + { + hasEntries = true; + m_popupMenu->insertItem(SmallIconSet("run"), + i18n("Put Into Run Dialog"), PutIntoRunDialog); + } + } + if (source==m_searchResultsWidget || ((source==m_favoriteView || source==m_recentlyView || source == m_systemView) && !m_popupService && !m_popupPath.path.startsWith("kicker:/")) ) { + TQString uri; + if (m_popupService) + uri = locate("apps", m_popupService->desktopEntryPath()); + else + uri = m_popupPath.path; + + TQString mimetype = TQString::null; + if ( m_popupPath.path.startsWith( "system:/media/" ) ) + mimetype = media_mimetypes[m_popupPath.path]; + + KFileItem* item = new KFileItem(uri, mimetype, KFileItem::Unknown); + _items.append( item ); + + const KURL kurl(uri); + KActionCollection act(this); + + KonqPopupMenu * konqPopupMenu = new KonqPopupMenu( KonqBookmarkManager::self(), _items, + kurl, act, (KNewMenu*)NULL, this, + item->isLocalFile() ? KonqPopupMenu::ShowProperties : KonqPopupMenu::NoFlags, + KParts::BrowserExtension::DefaultPopupItems ); + + if (konqPopupMenu->count()) { + if (hasEntries) { + m_popupMenu->insertSeparator(); + m_popupMenu->insertItem(SmallIconSet("add"),i18n("Advanced"), konqPopupMenu); + } + else { + delete m_popupMenu; + m_popupMenu = (KPopupMenu*)konqPopupMenu; + m_popupMenu->insertTitle(SmallIcon(kitem->icon()),kitem->title(),-1,0); + } + hasEntries = true; + } + } + } + + if (source==m_recentlyView) { + m_popupMenu->insertSeparator(); + if (m_popupService) + m_popupMenu->insertItem(SmallIconSet("history_clear"), + i18n("Clear Recently Used Applications"), ClearRecentlyUsedApps); + else + m_popupMenu->insertItem(SmallIconSet("history_clear"), + i18n("Clear Recently Used Documents"), ClearRecentlyUsedDocs); + } + + if (hasEntries) { + m_isShowing = true; + m_popupMenu->exec(pos); + m_isShowing = false; + } + + delete m_popupMenu; + m_popupMenu = 0; +} + +void KMenu::slotContextMenu(int selected) +{ + KServiceGroup::Ptr g; + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + + KURL src,dest; + KIO::CopyJob *job; + + KProcess *proc; + + TQStringList favs = KickerSettings::favorites(); + + switch (selected) { + case AddItemToDesktop: + accept(); + if (m_popupService) { + src.setPath( KGlobal::dirs()->findResource( "apps", m_popupService->desktopEntryPath() ) ); + dest.setPath( KGlobalSettings::desktopPath() ); + dest.setFileName( src.fileName() ); + + job = KIO::copyAs( src, dest ); + job->setDefaultPermissions( true ); + } + else { + KDesktopFile* df = new KDesktopFile( newDesktopFile(KURL(m_popupPath.path), KGlobalSettings::desktopPath() ) ); + df->writeEntry("GenericName", m_popupPath.description); + df->writeEntry( "Icon", m_popupPath.icon ); + df->writePathEntry( "URL", m_popupPath.path ); + df->writeEntry( "Name", m_popupPath.title ); + df->writeEntry( "Type", "Link" ); + df->sync(); + delete df; + } + accept(); + break; + + case AddItemToPanel: + accept(); + if (m_popupService) + kapp->dcopClient()->send("kicker", "Panel", "addServiceButton(TQString)", m_popupService->desktopEntryPath()); + else +#warning FIXME special RecentDocuments/foo.desktop handling + kapp->dcopClient()->send("kicker", "Panel", "addURLButton(TQString)", m_popupPath.path); + accept(); + break; + + case EditItem: + case EditMenu: + accept(); + proc = new KProcess(this); + *proc << KStandardDirs::findExe(TQString::fromLatin1("kmenuedit")); + *proc << "/"+m_popupPath.menuPath.section('/',-200,-2) << m_popupPath.menuPath.section('/', -1); + proc->start(); + break; + + case PutIntoRunDialog: + accept(); + if (m_popupService) + kapp->dcopClient()->send("kdesktop", "default", "popupExecuteCommand(TQString)", m_popupService->exec()); + else +#warning FIXME special RecentDocuments/foo.desktop handling + kapp->dcopClient()->send("kdesktop", "default", "popupExecuteCommand(TQString)", m_popupPath.path); + accept(); + break; + + case AddMenuToDesktop: { + accept(); + KDesktopFile *df = new KDesktopFile( newDesktopFile(KURL("programs:/"+m_popupPath.menuPath),KGlobalSettings::desktopPath())); + df->writeEntry( "Icon", m_popupPath.icon ); + df->writePathEntry( "URL", "programs:/"+m_popupPath.menuPath ); + df->writeEntry( "Name", m_popupPath.title ); + df->writeEntry( "Type", "Link" ); + df->sync(); + delete df; + + break; + } + case AddMenuToPanel: + accept(); + ds << "foo" << m_popupPath.menuPath; + kapp->dcopClient()->send("kicker", "Panel", "addServiceMenuButton(TQString,TQString)", ba); + break; + + case AddToFavorites: + if (m_popupService) { + if (favs.find(m_popupService->storageId())==favs.end()) { + KService::Ptr p = KService::serviceByStorageId(m_popupService->storageId()); + m_favoriteView->insertMenuItem(p, serviceMenuEndId()+favs.count()+1); + favs+=m_popupService->storageId(); + } + } + else { + TQStringList::Iterator it; + for (it = favs.begin(); it != favs.end(); ++it) { + if ((*it)[0]=='/') { + KDesktopFile df((*it),true); + if (df.readURL().replace("file://",TQString::null)==m_popupPath.path) + break; + } + } + if (it==favs.end()) { + TQString file = KickerLib::newDesktopFile(m_popupPath.path); + KDesktopFile df(file); + df.writeEntry("Encoding", "UTF-8"); + df.writeEntry("Type","Link"); + df.writeEntry("Name", m_popupPath.title); + df.writeEntry("GenericName", m_popupPath.description); + df.writeEntry("Icon", m_popupPath.icon); + df.writeEntry("URL", m_popupPath.path); + + m_favoriteView->insertItem(m_popupPath.icon, m_popupPath.title, m_popupPath.description, + m_popupPath.path, serviceMenuEndId()+favs.count()+1, -1); + + favs+=file; + } + } + KickerSettings::setFavorites(favs); + KickerSettings::writeConfig(); + m_browserDirty=true; + m_stacker->raiseWidget(FavoriteTab); + break; + + case RemoveFromFavorites: + if (m_popupService) { + favs.erase(favs.find(m_popupService->storageId())); + + for (TQListViewItemIterator it(m_favoriteView); it.current(); ++it) { + KMenuItem* kitem = static_cast(it.current()); + if (kitem->service() && kitem->service()->storageId() == m_popupService->storageId()) { + delete it.current(); + break; + } + } + } + else { + for (TQStringList::Iterator it = favs.begin(); it != favs.end(); ++it) { + if ((*it)[0]=='/') { + KDesktopFile df((*it),true); + if (df.readURL().replace("file://",TQString::null)==m_popupPath.path) { + TQFile::remove((*it)); + favs.erase(it); + break; + } + } + } + for (TQListViewItemIterator it(m_favoriteView); it.current(); ++it) { + KMenuItem* kitem = static_cast(it.current()); + if (!kitem->service() && kitem->path() == m_popupPath.path) { + delete it.current(); + break; + } + } + } + m_favoriteView->slotMoveContent(); + KickerSettings::setFavorites(favs); + KickerSettings::writeConfig(); + m_browserDirty=true; + m_stacker->raiseWidget(FavoriteTab); + break; + + case ClearRecentlyUsedApps: + clearRecentAppsItems(); + break; + + case ClearRecentlyUsedDocs: + clearRecentDocsItems(); + break; + + default: + break; + } +} + +void KMenu::resizeEvent ( TQResizeEvent * e ) +{ + //kdDebug() << "resizeEvent " << size() << endl; + KMenuBase::resizeEvent(e); + int ypos = 0; + // this is the height remaining to fill + int left_height = height(); + + if ( m_orientation == BottomUp ) + { + m_resizeHandle->move( e->size().width() - 19, 3); + + // put the search widget at the top of the menu and give it its desired + // height + m_search->mainWidget()->setGeometry( 0, ypos, width(), + m_search->minimumSize().height() ); + left_height -= m_search->minimumSize().height(); + ypos += m_search->minimumSize().height(); + + // place the footer widget at the bottom of the menu and give it its desired + // height + m_footer->mainWidget()->setGeometry( 0, height() - m_footer->minimumSize().height(), + width(), m_footer->minimumSize().height() ); + left_height -= m_footer->minimumSize().height(); + + // place the button box above the footer widget, horizontal placement + // has the width of the edge graphics subtracted + m_tabBar->setGeometry(button_box_left.width(), + height() - m_footer->minimumSize().height() - + m_tabBar->sizeHint().height(), + width() - button_box_left.width(), + m_tabBar->sizeHint().height() ); + left_height -= m_tabBar->sizeHint().height(); + + // place the main (stacker) widget below the search widget, + // in the remaining vertical space + m_stacker->setGeometry(0, ypos, + width(), + left_height ); + + } + else // TopDown orientation + { + // place the 'footer' widget at the top of the menu and give it + // its desired height + m_footer->mainWidget()->setGeometry( 0, + ypos /*height() - m_footer->minimumSize().height()*/, + width(), + m_footer->minimumSize().height() ); + ypos += m_footer->minimumSize().height(); + left_height -= m_footer->minimumSize().height(); + + // place the button box next at the top of the menu. + // has the width of the edge graphics subtracted + m_tabBar->setGeometry(button_box_left.width(), ypos, width() - button_box_left.width(), + m_tabBar->sizeHint().height()); + + ypos += m_tabBar->sizeHint().height(); + left_height -= m_tabBar->sizeHint().height(); + + // put the search widget above the footer widget + // height + m_search->mainWidget()->setGeometry( 0, + height() - m_search->minimumSize().height(), + width(), + m_search->minimumSize().height() + ); + left_height -= m_search->minimumSize().height(); + + // place the main (stacker) widget below the button box, + // in the remaining vertical space + m_stacker->setGeometry(0, ypos, + width(), + left_height ); + m_resizeHandle->move( e->size().width() - 19, e->size().height() - 19); + } + paintSearchTab( false ); +} + +void KMenu::mousePressEvent ( TQMouseEvent * e ) +{ + if ( m_orientation == BottomUp ) { + if (e->x() > width() - m_resizeHandle->width() && + e->y() < m_resizeHandle->height() ) + { + m_isresizing = true; + } + } + else { + if (e->x() > width() - m_resizeHandle->width() && + e->y() > height() - m_resizeHandle->height() ) + { + m_isresizing = true; + } + } + KMenuBase::mousePressEvent(e); +} + +void KMenu::mouseReleaseEvent ( TQMouseEvent * /*e*/ ) +{ + m_isresizing = false; +} + +void KMenu::mouseMoveEvent ( TQMouseEvent * e ) +{ + if ( hasMouseTracking() && m_isresizing ) { + m_stacker->setMinimumSize( TQSize(0, 0) ); + m_stacker->setMaximumSize( TQSize(32000, 32000) ); + int newWidth = QMAX( e->x() - x(), minimumSizeHint().width() ); + if ( m_orientation == BottomUp ) { + int newHeight = QMAX( height() - e->y(), minimumSizeHint().height() + 10 ); + int newY = y() + height() - newHeight; + setGeometry( x(), newY, newWidth, newHeight); + } + else { + setGeometry( x(), y(), newWidth, QMAX( e->y(), minimumSizeHint().height() + 10 )); + } + } +} + +void KMenu::clearedHistory() +{ + saveConfig(); +} + +void KMenu::saveConfig() +{ + KickerSettings::setHistory( m_kcommand->historyItems() ); + KickerSettings::setCompletionItems( m_kcommand->completionObject()->items() ); + KickerSettings::writeConfig(); +} + +void KMenu::notifyServiceStarted(KService::Ptr service) +{ + // Inform other applications (like the quickstarter applet) + // that an application was started + TQByteArray params; + TQDataStream stream(params, IO_WriteOnly); + stream << "minicli" << service->storageId(); + kdDebug() << "minicli appLauncher dcop signal: " << service->storageId() << endl; + KApplication::kApplication()->dcopClient()->emitDCOPSignal("appLauncher", + "serviceStartedByStorageId(TQString,TQString)", params); +} + +void KMenu::parseLine( bool final ) +{ + TQString cmd = m_kcommand->currentText().stripWhiteSpace(); + m_filterData->setData( cmd ); + + if( final ) + KURIFilter::self()->filterURI( *(m_filterData), m_finalFilters ); + else + KURIFilter::self()->filterURI( *(m_filterData), m_middleFilters ); + + m_iconName = m_filterData->iconName(); + + kdDebug (1207) << "Command: " << m_filterData->uri().url() << endl; + kdDebug (1207) << "Arguments: " << m_filterData->argsAndOptions() << endl; +} + +// report error as a title in the menu +void KMenu::reportError (TQString error) +{ + int index = 1000; //getHitMenuItemPosition (new HitMenuItem (base_category_id[0], 0)); + kndDebug () << "Inserting error:" << error << " at position " << index << endl; + m_searchResultsWidget->insertSeparator(OTHER_ID_BASE + 120, error, index); +} + +int KMenu::getHitMenuItemPosition ( HitMenuItem *hit_item) +{ + TQPtrListIterator it (m_current_menu_items); + const HitMenuItem *cur_item; + int pos = 0; + while ((cur_item = it.current ()) != NULL) { + ++it; + if ((cur_item->category!=hit_item->category || !cur_item->display_name.isEmpty()) && (*hit_item) < (*cur_item)) + break; + pos++; + } + m_current_menu_items.insert (pos, hit_item); + + return pos + 1; +} + +bool KMenu::checkUriInMenu( const KURL &uri) +{ + TQPtrListIterator it (m_current_menu_items); + const HitMenuItem *cur_item; + while ((cur_item = it.current ()) != NULL) { + ++it; + if (cur_item->uri == uri ) + return true; + } + + return false; +} + +void KMenu::searchActionClicked(TQListViewItem* item) +{ + accept(); + + addToHistory(); + if (item==m_searchIndex) { + TQByteArray data; + TQDataStream arg(data, IO_WriteOnly); + arg << m_kcommand->currentText(); + + if (ensureServiceRunning("kerry")) + kapp->dcopClient()->send("kerry","search","search(TQString)", data); + } + else { + KURIFilterData data; + TQStringList list; + data.setData( m_kcommand->currentText() ); + list << "kurisearchfilter" << "kuriikwsfilter"; + + if( !KURIFilter::self()->filterURI(data, list) ) { + KDesktopFile file("searchproviders/google.desktop", true, "services"); + data.setData(file.readEntry("Query").replace("\\{@}", m_kcommand->currentText())); + } + + (void) new KRun( data.uri(), parentWidget()); + } +} + +void KMenu::addToHistory() +{ + TQString search = m_kcommand->currentText().stripWhiteSpace(); + + if (search.length()<4) + return; + + m_kcommand->addToHistory( search ); +} + +TQString KMenu::newDesktopFile(const KURL& url, const TQString &directory) +{ + TQString base = url.fileName(); + if (base.endsWith(".desktop")) + base.truncate(base.length()-8); + TQRegExp r("(.*)(?=-\\d+)"); + if (r.search(base) > -1) + base = r.cap(1); + + TQString file = base + ".desktop"; + + for(int n = 1; ++n; ) + { + if (!TQFile::exists(directory+file)) + break; + + file = TQString("%2-%1.desktop").arg(n).arg(base); + } + return directory+file; +} + +void KMenu::updateRecentlyUsedApps(KService::Ptr &service) +{ + TQString strItem(service->desktopEntryPath()); + + // don't add an item from root kmenu level + if (!strItem.contains('/')) + { + return; + } + + // add it into recent apps list + RecentlyLaunchedApps::the().appLaunched(strItem); + RecentlyLaunchedApps::the().save(); + RecentlyLaunchedApps::the().m_bNeedToUpdate = true; +} + +TQSize KMenu::sizeHint() const +{ +#warning FIXME + // this should be only for the inner area so layout changes do not break it + const int width = kMin(KickerSettings::kMenuWidth(), TQApplication::desktop()->screen()->width()-50); + + const int height = kMin(KickerSettings::kMenuHeight(), TQApplication::desktop()->screen()->height()-50); + TQSize wanted(width, height); + kdDebug() << "show " << minimumSizeHint() << " " << m_stacker->minimumSizeHint() << " " + << m_searchFrame->minimumSizeHint() << " " << wanted << endl; + bool isDefault = wanted.isNull(); + wanted = wanted.expandedTo(minimumSizeHint()); + if ( isDefault ) + wanted.setHeight( wanted.height() + ( m_favoriteView->goodHeight() - m_stacker->minimumSizeHint().height() ) ); + + return wanted; +} + +TQSize KMenu::minimumSizeHint() const +{ + TQSize minsize; + minsize.setWidth( minsize.width() + m_tabBar->sizeHint().width() ); + minsize.setWidth( QMAX( minsize.width(), + m_search->minimumSize().width() ) ); + minsize.setWidth( QMAX( minsize.width(), + m_search->minimumSize().width() ) ); + + minsize.setHeight( minsize.height() + + m_search->minimumSize().height() + + m_footer->minimumSize().height() + + 180 ); // 180 is a very rough guess for 32 icon size + return minsize; +} + +void KMenu::slotFavoritesMoved( TQListViewItem* item, TQListViewItem* /*afterFirst*/, TQListViewItem* afterNow) +{ + KMenuItem* kitem = dynamic_cast(item); + KMenuItem* kafterNow = dynamic_cast(afterNow); + + TQStringList favs = KickerSettings::favorites(); + TQStringList::Iterator it; + TQString addFav = TQString::null; + + // remove at old position + if (kitem->service()) + { + favs.erase(favs.find(kitem->service()->storageId())); + addFav = kitem->service()->storageId(); + } + else + { + for (it = favs.begin(); it != favs.end(); ++it) + { + if ((*it)[0]=='/') + { + KDesktopFile df((*it),true); + if (df.readURL().replace("file://",TQString::null)==kitem->path()) + { + addFav = *it; + favs.erase(it); + break; + } + } + } + } + + if (addFav.isEmpty()) + return; + + if (!kafterNow || dynamic_cast(afterNow)) + { + favs.prepend(addFav); + } + else + { + // add at new position + for (it = favs.begin(); it != favs.end(); ++it) + { + if ((*it)[0]=='/' && !kafterNow->service()) + { + KDesktopFile df((*it),true); + if (df.readURL().replace("file://",TQString::null)==kafterNow->path()) + { + kdDebug() << "insert after " << kafterNow->path() << endl; + favs.insert(++it,addFav); + break; + } + } + else if (kafterNow->service() && *it==kafterNow->service()->storageId()) + { + kdDebug() << "insert after service " << kafterNow->service() << endl; + favs.insert(++it,addFav); + break; + } + } + } + kdDebug() << "favs " << favs << endl; + + KickerSettings::setFavorites(favs); + KickerSettings::writeConfig(); + + m_favoriteView->slotMoveContent(); +} + +void KMenu::updateMedia() +{ + TQStringList devices = m_mediaWatcher->devices(); + if ( devices.isEmpty() ) + return; + + int nId = serviceMenuStartId(); + if ( m_media_id ) { + for ( int i = m_media_id + 1 ;; ++i ) + { + KMenuItem *item = m_systemView->findItem( i ); + if ( !item ) + break; + if ( !item->path().startsWith( "system:/" ) ) + break; + media_mimetypes.remove(item->path()); + delete item; + } + nId = m_media_id + 1; + } else { + m_media_id = nId; + m_systemView->insertSeparator( nId++, i18n("Media"), -1); + } + + for ( TQStringList::ConstIterator it = devices.constBegin(); it != devices.constEnd(); ++it ) + { + TQString id = ( *it ); + TQString name = *++it; + TQString label = *++it; + TQString userLabel = ( *++it ); + bool mountable = ( *++it == "true" ); // bool + ( void )mountable; + TQString deviceNode = ( *++it ); + TQString mountPoint = ( *++it ); + TQString fsType = ( *++it ); + bool mounted = ( *++it == "true" ); // bool + TQString baseURL = ( *++it ); + TQString mimeType = ( *++it ); + TQString iconName = ( *++it ); + + media_mimetypes["system:/media/"+name] = mimeType; + + if ( iconName.isEmpty() ) // no user icon, query the MIME type + { + KMimeType::Ptr mime = KMimeType::mimeType( mimeType ); + iconName = mime->icon( TQString::null, false ); + } + + TQString descr = deviceNode; + if ( mounted ) + { + descr = mountPoint; + // calc the free/total space + struct statfs sfs; + if ( statfs( TQFile::encodeName( mountPoint ), &sfs ) == 0 ) + { + uint64_t total = ( uint64_t )sfs.f_blocks * sfs.f_bsize; + uint64_t avail = ( uint64_t )( getuid() ? sfs.f_bavail : sfs.f_bfree ) * sfs.f_bsize; + if ( avail < total && avail > 1024 ) { + label += " " + i18n( "(%1 available)" ).arg( KIO::convertSize(avail) ); + } + } + } + m_systemView->insertItem( iconName, userLabel.isEmpty() ? label : userLabel, + descr, "system:/media/" + name, nId++, -1 ); + + ++it; // skip separator + } +} + +bool KMenu::ensureServiceRunning(const TQString & service) +{ + TQStringList URLs; + TQByteArray data, replyData; + TQCString replyType; + TQDataStream arg(data, IO_WriteOnly); + arg << service << URLs; + + if ( !kapp->dcopClient()->call( "klauncher", "klauncher", "start_service_by_desktop_name(TQString,TQStringList)", + data, replyType, replyData) ) { + qWarning( "call to klauncher failed."); + return false; + } + TQDataStream reply(replyData, IO_ReadOnly); + + if ( replyType != "serviceResult" ) + { + qWarning( "unexpected result '%s' from klauncher.", replyType.data()); + return false; + } + int result; + TQCString dcopName; + TQString error; + reply >> result >> dcopName >> error; + if (result != 0) + { + qWarning("Error starting: %s", error.local8Bit().data()); + return false; + } + return true; +} + +void KMenu::slotFavDropped(TQDropEvent * ev, TQListViewItem *after ) +{ + TQStringList favs = KickerSettings::favorites(); + KMenuItem *newItem = 0; + + if (KMenuItemDrag::canDecode(ev)) + { + KMenuItemInfo item; + KMenuItemDrag::decode(ev,item); + + if (item.m_s) + { + if (favs.find(item.m_s->storageId())==favs.end()) + { + newItem = m_favoriteView->insertMenuItem(item.m_s, serviceMenuEndId()+favs.count()+1); + favs += item.m_s->storageId(); + } + } + else + { + TQString uri = item.m_path; + if (uri.startsWith(locateLocal("data", TQString::fromLatin1("RecentDocuments/")))) { + KDesktopFile df(uri,true); + uri=df.readURL(); + } + + TQStringList::Iterator it; + for (it = favs.begin(); it != favs.end(); ++it) + { + if ((*it)[0]=='/') + { + KDesktopFile df((*it),true); + if (df.readURL().replace("file://",TQString::null)==uri) + break; + } + } + if (it==favs.end()) + { + TQString file = KickerLib::newDesktopFile(uri); + KDesktopFile df(file); + df.writeEntry("Encoding", "UTF-8"); + df.writeEntry("Type","Link"); + df.writeEntry("Name", item.m_title); + df.writeEntry("GenericName", item.m_description); + df.writeEntry("Icon", item.m_icon); + df.writeEntry("URL", uri); + + newItem = m_favoriteView->insertItem(item.m_icon, item.m_title, item.m_description, + uri, serviceMenuEndId()+favs.count()+1, -1); + favs += file; + } + } + } + else if (TQTextDrag::canDecode(ev)) + { + TQString text; + TQTextDrag::decode(ev,text); + + if (text.endsWith(".desktop")) + { + KService::Ptr p = KService::serviceByDesktopPath(text.replace("file://",TQString::null)); + if (p && favs.find(p->storageId())==favs.end()) { + newItem = m_favoriteView->insertMenuItem(p, serviceMenuEndId()+favs.count()+1); + favs+=p->storageId(); + } + } + else + { + TQStringList::Iterator it; + for (it = favs.begin(); it != favs.end(); ++it) + { + if ((*it)[0]=='/') + { + KDesktopFile df((*it),true); + if (df.readURL().replace("file://",TQString::null)==text) + break; + } + } + if (it==favs.end()) + { + KFileItem* item = new KFileItem(text, TQString::null, KFileItem::Unknown); + KURL kurl(text); + + TQString file = KickerLib::newDesktopFile(text); + KDesktopFile df(file); + df.writeEntry("Encoding", "UTF-8"); + df.writeEntry("Type","Link"); + df.writeEntry("Name", item->name()); + df.writeEntry("GenericName", i18n("Directory: %1").arg(kurl.upURL().path())); + df.writeEntry("Icon", item->iconName()); + df.writeEntry("URL", text); + + newItem = m_favoriteView->insertItem(item->iconName(), item->name(), i18n("Directory: %1").arg(kurl.upURL().path()), text, serviceMenuEndId()+favs.count()+1, -1); + favs += file; + } + } + } + + if ( newItem ) { + if (!after && m_favoriteView->childCount()>0) { + newItem->moveItem( m_favoriteView->firstChild() ); + m_favoriteView->firstChild()->moveItem( newItem ); + } + else + newItem->moveItem( after ); + KickerSettings::setFavorites(favs); + slotFavoritesMoved( newItem, 0, after ); + } + m_stacker->raiseWidget(m_favoriteView); +} + +void KMenu::resetOverflowCategory() +{ + if (m_overflowCategoryState==NotNeeded) + m_overflowList.setAutoDelete( true ); + + m_overflowList.clear(); + m_overflowList.setAutoDelete( false ); + m_overflowCategoryState = None; + m_overflowCategory = num_categories; +} + +void KMenu::fillOverflowCategory() +{ + if (m_overflowCategoryState==Filling) { + initCategoryTitlesUpdate(); + for (HitMenuItem * item = m_overflowList.first(); item; item = m_overflowList.next() ) { + max_category_id [item->category]++; + item->id=max_category_id [item->category]; + + KMenuItem *hit_item = m_searchResultsWidget->insertItem(iconForHitMenuItem(item), item->display_name, item->display_info, item->uri.url(), max_category_id [item->category], getHitMenuItemPosition (item)); + hit_item->setService(item->service); + } + updateCategoryTitles(); + } +} + +int KMenu::max_items(int category) const +{ + if (category==ACTIONS) + return 10; + + return 5; +} + +#define DBUS_HAL_INTERFACE "org.freedesktop.Hal" +#define DBUS_HAL_SYSTEM_POWER_INTERFACE "org.freedesktop.Hal.Device.SystemPowerManagement" +#define HAL_UDI_COMPUTER "/org/freedesktop/Hal/devices/computer" + +#ifdef KDELIBS_SUSE +#include +#endif + +void KMenu::insertSuspendOption( int &nId, int &index ) +{ +#ifdef KDELIBS_SUSE + int supported = -1; + bool suspend_ram, suspend_disk, standby; + + liblazy_hal_get_property_bool(HAL_UDI_COMPUTER, "power_management.can_suspend", &supported); + if (supported == 1) + suspend_ram = true; + else + suspend_ram = false; + liblazy_hal_get_property_bool(HAL_UDI_COMPUTER, "power_management.can_standby", &supported); + if (supported == 1) + standby = true; + else + standby = false; + liblazy_hal_get_property_bool(HAL_UDI_COMPUTER, "power_management.can_hibernate", &supported); + if (supported == 1) + suspend_disk = true; + else + suspend_disk = false; + + if (liblazy_hal_is_caller_privileged("org.freedesktop.hal.power-management.hibernate") != 1) + suspend_disk = false; + if (liblazy_hal_is_caller_privileged("org.freedesktop.hal.power-management.suspend") != 1) + suspend_ram = false; + if (liblazy_hal_is_caller_privileged("org.freedesktop.hal.power-management.standby") != 1) + standby = false; + + if ( ! ( standby + suspend_ram + suspend_disk ) ) + return; + + i18n("Suspend Computer"); + + if ( suspend_disk ) + m_exitView->leftView()->insertItem( "suspend2disk", i18n( "Suspend to Disk" ), + i18n( "Pause without logging out" ), "kicker:/suspend_disk", nId++, index++ ); + + if ( suspend_ram ) + m_exitView->leftView()->insertItem( "suspend2ram", i18n( "Suspend to RAM" ), + i18n( "Pause without logging out" ), "kicker:/suspend_ram", nId++, index++ ); + + if ( standby ) + m_exitView->leftView()->insertItem( "player_pause", i18n( "Standby" ), + i18n( "Pause without logging out" ), "kicker:/standby", nId++, index++ ); +#endif +} + +void KMenu::slotSuspend(int id) +{ +#ifdef KDELIBS_SUSE + int error = 0; + int wake = 0; + DBusMessage *reply = 0; + + if (id == 1) { + error = liblazy_dbus_system_send_method_call(DBUS_HAL_INTERFACE, + HAL_UDI_COMPUTER, + DBUS_HAL_SYSTEM_POWER_INTERFACE, + "Hibernate", + &reply, + DBUS_TYPE_INVALID); + } else if (id == 2) + error = liblazy_dbus_system_send_method_call(DBUS_HAL_INTERFACE, + HAL_UDI_COMPUTER, + DBUS_HAL_SYSTEM_POWER_INTERFACE, + "Suspend", + &reply, + DBUS_TYPE_INT32, + &wake, + DBUS_TYPE_INVALID); + else if (id == 3) + error = liblazy_dbus_system_send_method_call(DBUS_HAL_INTERFACE, + HAL_UDI_COMPUTER, + DBUS_HAL_SYSTEM_POWER_INTERFACE, + "Standby", + &reply, + DBUS_TYPE_INVALID); + else + return; + if (error) +#endif + KMessageBox::error(this, i18n("Suspend failed")); + +} + +// vim:cindent:sw=4: diff --git a/kicker/kicker/ui/k_new_mnu.h b/kicker/kicker/ui/k_new_mnu.h new file mode 100644 index 000000000..ef45ef815 --- /dev/null +++ b/kicker/kicker/ui/k_new_mnu.h @@ -0,0 +1,343 @@ +/***************************************************************** + + Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + Copyright (c) 2006 Debajyoti Bera + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +******************************************************************/ + +#ifndef __k_new_mnu_h__ +#define __k_new_mnu_h__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../interfaces/kickoff-search-plugin.h" + +// #include "kmenubase.h" +#include "../core/kmenubase.h" +#include "service_mnu.h" +#include "query.h" + +class KickerClientMenu; +class KickoffTabBar; +class KBookmarkMenu; +class KActionCollection; +class KBookmarkOwner; +class Panel; +class TQWidgetStack; +class KHistoryCombo; +class TQScrollView; +class PopupMenuTitle; +class MediaWatcher; +class KURIFilterData; +class KBookmarkGroup; +class KBookmarkManager; +class ItemView; +class FlipScrollView; +class TQListViewItem; +class KMenuItem; +class TQListView; +class TQTabBar; +class TQTab; + +static TQString categories[14] = {I18N_NOOP("Actions"), I18N_NOOP("Applications"), I18N_NOOP("Bookmarks"), + I18N_NOOP("Notes"), I18N_NOOP("Emails"), I18N_NOOP("Files"), I18N_NOOP("Music"), + I18N_NOOP("Browsing History"), I18N_NOOP("Chat Logs"), I18N_NOOP("Feeds"), + I18N_NOOP("Pictures"), I18N_NOOP("Videos"), I18N_NOOP("Documentation"), + I18N_NOOP("Others")}; + +static TQString kerry_categories[14] = {"contacts", "applications", "webpages", "everything", "conversations", + "everything", "media", "webpages", "conversations", "webpages", "images", + "media", "everything", "everything"}; + +enum MenuOrientation { BottomUp, TopDown, UnDetermined }; +enum OverflowCategoryState { None, Filling, NotNeeded }; + +class KMenu : public KMenuBase +{ + Q_OBJECT + Q_PROPERTY (bool KStyleMenuDropShadow READ useKStyleMenuDropShadow ) + +public: + KMenu(); + ~KMenu(); + + int insertClientMenu(KickerClientMenu *p); + void removeClientMenu(int id); + + bool useKStyleMenuDropShadow() const { return true; } + + virtual void showMenu(); + virtual bool eventFilter(TQObject*, TQEvent*); + + void clearRecentAppsItems(); + void clearRecentDocsItems(); + bool highlightMenuItem(const TQString& /*id*/) { return false;} + + void selectFirstItem() {} + void popup(const TQPoint&, int indexAtPoint); + + enum MaskEffect { Plain, Dissolve }; + + virtual TQSize sizeHint() const; + virtual TQSize minimumSizeHint() const; + + void searchOver(); + void initCategoryTitlesUpdate(); + bool anotherHitMenuItemAllowed(int cat, bool count=true); + void addHitMenuItem(HitMenuItem*); + void insertSearchResult(HitMenuItem* item); + + void updateCategoryTitles(); + +signals: + void aboutToHide(); + void aboutToShow(); + +public slots: + virtual void initialize(); + + virtual void hide(); + virtual void show(); + + void stackWidgetRaised(TQWidget*); + +protected slots: + void slotLock(); + void slotOpenHomepage(); + void slotLogout(); + void slotPopulateSessions(); + void slotSessionActivated( int ); + void slotGoSubMenu(const TQString& relPath); + void slotGoBack(); + void slotGoExitMainMenu(); + void slotGoExitSubMenu(const TQString& url); + void tabClicked(TQTab*); + + void paletteChanged(); + virtual void configChanged(); + void updateRecent(); + + void initSearch(); + void searchAccept(); + void searchChanged(const TQString &); + // when timeout happens or doQueryNow calls + void doQuery (bool return_pressed = false); + void searchActionClicked(TQListViewItem*); + + void slotStartService(KService::Ptr); + void slotStartURL(const TQString&); + void slotContextMenuRequested( TQListViewItem * item, const TQPoint & pos, int col ); + + void clearedHistory(); + + void slotSloppyTimeout(); + + void slotContextMenu(int); + void slotFavoritesMoved( TQListViewItem*, TQListViewItem*, TQListViewItem* ); + + void updateMedia(); + void slotFavDropped(TQDropEvent * e, TQListViewItem *after ); + void slotSuspend(int id); + +protected: + virtual void paintEvent(TQPaintEvent *); + virtual void resizeEvent ( TQResizeEvent * ); + virtual void mousePressEvent ( TQMouseEvent * e ); + virtual void mouseReleaseEvent ( TQMouseEvent * e ); + virtual void mouseMoveEvent ( TQMouseEvent * e ); + + void doNewSession(bool lock); + void createRecentMenuItems(); + void insertStaticItems(); + void insertStaticExitItems(); + void insertSuspendOption( int &id, int &index ); + virtual void clearSubmenus(); +// void raiseStackWidget(TQWidget *view); + + bool runCommand(); + + void setupUi(); + + void saveConfig(); + void searchProgramList(TQString relPath); + void searchBookmarks(KBookmarkGroup); + void searchAddressbook(); + + void createNewProgramList(); + void createNewProgramList(TQString relPath); + + void paintSearchTab( bool active ); + + void goSubMenu(const TQString& relPath, bool keyboard = false); + void setOrientation(MenuOrientation orientation); + +private: + int serviceMenuStartId() { return 5242; } + int serviceMenuEndId() { return 5242; } + + void fillMenu( KServiceGroup::Ptr &_root, KServiceGroup::List &_list, + const TQString &_relPath, ItemView* view, int & id ); + + void fillSubMenu(const TQString& relPath, ItemView *view); + + TQPopupMenu *sessionsMenu; + int client_id; + bool delay_init; + TQIntDict clients; + KActionCollection *actionCollection; + PopupMenuList dynamicSubMenus; + + TQTimer m_sloppyTimer; + TQTimer m_mediaFreeTimer; + MediaWatcher * m_mediaWatcher; + TQRegion m_sloppyRegion; + TQRect m_sloppySource; + bool m_sloppySourceClicked; + TQWidget * m_sloppyWidget; + ItemView * m_recentlyView; + ItemView * m_favoriteView; + ItemView * m_searchResultsWidget; + TQListView * m_searchActions; + FlipScrollView * m_browserView; + ItemView * m_systemView; + FlipScrollView * m_exitView; + TQVBox * m_searchWidget; + TQLabel * m_resizeHandle; + + bool m_isresizing; + // timer for search without pressing enter feature + TQTimer *input_timer, *init_search_timer; + + Query current_query; + + bool dontQueryNow(const TQString &); + + // start timeout timer to call doQuery is enough time has passed since last keypress + void checkToDoQuery (const TQString &); + // when return is pressed + void doQueryNow (); + void clearSearchResults(bool showHelp = true); + + int *max_category_id; // maximum id in this category: max_category_id - base_category_id gives the current number of hits displayed in this category + int *categorised_hit_total; // current number of hits returned in each category + + bool ensureServiceRunning(const TQString & service); + + TQString iconForHitMenuItem(HitMenuItem *hit_item); + + int getHitMenuItemPosition (HitMenuItem *hit_item); + TQMap mimetype_iconstore; + TQMap media_mimetypes; + // report error as a menu item + void reportError (TQString err); + void addToHistory(); + + int max_items(int category) const; + TQString TOP_CATEGORY_STRING; + bool *already_added; + + void notifyServiceStarted(KService::Ptr service); + void parseLine( bool final ); + TQString m_iconName; + TQStringList m_middleFilters; + TQStringList m_finalFilters; + KURIFilterData* m_filterData; + TQPtrList m_current_menu_items; + TQListViewItem *m_searchIndex, *m_searchInternet; + + bool checkUriInMenu(const KURL &uri); + + TQRegExp emailRegExp,uriRegExp,uri2RegExp,authRegExp; + + KBookmarkManager *bookmarkManager; + KABC::AddressBook* m_addressBook; + + enum ContextMenuEntry { AddItemToPanel, EditItem, AddMenuToPanel, EditMenu, + AddItemToDesktop, AddMenuToDesktop, PutIntoRunDialog, + AddToFavorites, RemoveFromFavorites, ClearRecentlyUsedApps, + ClearRecentlyUsedDocs }; + struct PopupPath + { + TQString title, description, icon, path, menuPath; + }; + + enum KickoffTabEntry { FavoriteTab, ApplicationsTab, ComputerTab, + HistoryTab, LeaveTab, SearchTab, NumTabs }; + + KPopupMenu* m_popupMenu; + KService* m_popupService; + PopupPath m_popupPath; + + KickoffTabBar* m_tabBar; + TQTab* m_tabs[NumTabs]; + + TQString newDesktopFile(const KURL& url, const TQString &directory); + void updateRecentlyUsedApps(KService::Ptr &service); + + TQPixmap main_border_lc; + TQPixmap main_border_rc; + TQPixmap main_border_tl; + TQPixmap main_border_tr; + TQPixmap button_box_left; + + TQPixmap search_tab_left; + TQPixmap search_tab_right; + TQPixmap search_tab_center; + + TQPixmap search_tab_top_left; + TQPixmap search_tab_top_right; + TQPixmap search_tab_top_center; + + TQWidgetStack *m_stacker; + + TQStringList m_programsInMenu; + TQStringList m_newInstalledPrograms, m_seenPrograms; + bool m_seenProgramsChanged; + TQString m_currentDate; + + MenuOrientation m_orientation; + bool m_toolTipsEnabled; + int m_media_id; + + bool m_recentDirty, m_browserDirty, m_kerryInstalled, m_isShowing; + + KickoffSearch::Plugin* m_search_plugin; + TQObject* m_search_plugin_interface; + + OverflowCategoryState m_overflowCategoryState; + TQPtrList m_overflowList; + int m_overflowCategory; + + void resetOverflowCategory(); + void fillOverflowCategory(); + + TQString insertBreaks(const TQString& text, TQFontMetrics fm, int width, TQString leadInsert = TQString::null); +}; + +#endif diff --git a/kicker/kicker/ui/kickoff_bar.cpp b/kicker/kicker/ui/kickoff_bar.cpp new file mode 100644 index 000000000..2f0de9aea --- /dev/null +++ b/kicker/kicker/ui/kickoff_bar.cpp @@ -0,0 +1,200 @@ +/***************************************************************** + + Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + Copyright (c) 2006 Dirk Mueller + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +******************************************************************/ + +#include "kickoff_bar.h" +#include "itemview.h" + +#include +#include +#include +#include +#include + +#include +#include "kickerSettings.h" + +KickoffTabBar::KickoffTabBar(TQWidget* parent, const char* name) + : TQTabBar(parent, name), m_tabsActivated(true) +{ + setAcceptDrops(true); +} + +void KickoffTabBar::deactivateTabs(bool b) +{ + m_tabsActivated = !b; + + update(); +} + +void KickoffTabBar::paint(TQPainter* p, TQTab* t, bool selected) const +{ + TQStyle::SFlags flags = TQStyle::Style_Default; + + if (isEnabled() && t->isEnabled()) + flags |= TQStyle::Style_Enabled; + if ( m_tabsActivated && selected ) + flags |= TQStyle::Style_Selected; +// else if(t == d->pressed) +// flags |= TQStyle::Style_Sunken; + //selection flags + if(t->rect().contains(mapFromGlobal(TQCursor::pos()))) + flags |= TQStyle::Style_MouseOver; + style().drawControl( TQStyle::CE_TabBarTab, p, this, t->rect(), + colorGroup(), flags, TQStyleOption(t) ); + + paintLabel( p, t->rect(), t, t->identifier() == keyboardFocusTab() ); +} + + +void KickoffTabBar::paintLabel(TQPainter* p, const TQRect& br, TQTab* t, bool has_focus) const +{ + TQRect r = br; + + bool selected = m_tabsActivated && (currentTab() == t->identifier()); + int vframe = style().pixelMetric( TQStyle::PM_TabBarTabVSpace, this ); + + p->setFont( font() ); + TQFontMetrics fm = p->fontMetrics(); + int fw = fm.size( Qt::SingleLine|Qt::ShowPrefix, t->text() ).width(); + + TQRect rt(r); + rt.setWidth(fw); + + if ( t->iconSet()) + { + // the tab has an iconset, draw it in the right mode + TQIconSet::Mode mode = (t->isEnabled() && isEnabled()) + ? TQIconSet::Normal : TQIconSet::Disabled; + if ( mode == TQIconSet::Normal && has_focus ) + mode = TQIconSet::Active; + TQPixmap pixmap = t->iconSet()->pixmap( TQIconSet::Large, mode ); + int pixw = pixmap.width(); + int pixh = pixmap.height(); + int xoff = br.x() + (br.width() - pixw)/2; + int yoff = br.y() + (br.height() - 4 - pixh - ((KickerSettings::kickoffTabBarFormat() != KickerSettings::IconOnly) ? fm.height() : 0) - vframe)/2; + + p->drawPixmap( xoff, 4 + yoff, pixmap ); + + r.setTop(vframe/2 + yoff + pixh - 8); + rt.setTop(vframe/2 + yoff + pixh - 8); + rt.setHeight(((KickerSettings::kickoffTabBarFormat() != KickerSettings::IconOnly) ? fm.height() : 0) + vframe/2); + } + else + rt.setHeight(vframe/2 + fm.height()); + + rt.setWidth(fw+8); + rt.moveCenter(r.center()); + + TQStyle::SFlags flags = TQStyle::Style_Default; + + if (isEnabled() && t->isEnabled()) + flags |= TQStyle::Style_Enabled; + if (has_focus) + flags |= TQStyle::Style_HasFocus; + if ( selected ) + flags |= TQStyle::Style_Selected; + // else if(t == d->pressed) + // flags |= TQStyle::Style_Sunken; + if(t->rect().contains(mapFromGlobal(TQCursor::pos()))) + flags |= TQStyle::Style_MouseOver; + style().drawControl( TQStyle::CE_TabBarLabel, p, this, rt, + t->isEnabled() ? colorGroup(): palette().disabled(), + flags, TQStyleOption(t) ); +} + +TQSize KickoffTabBar::sizeHint() const +{ + TQSize s = TQTabBar::sizeHint(); + + return s; +} + +void KickoffTabBar::layoutTabs() +{ + TQTabBar::layoutTabs(); + + TQFontMetrics fm = fontMetrics(); + int fh = ((KickerSettings::kickoffTabBarFormat() != KickerSettings::IconOnly) ? fm.height() : 0) + 4; + + int hframe = style().pixelMetric( TQStyle::PM_TabBarTabHSpace, this ); + int vframe = style().pixelMetric( TQStyle::PM_TabBarTabVSpace, this ); + int overlap = style().pixelMetric( TQStyle::PM_TabBarTabOverlap, this ); + + TQSize s; + for (int t = 0; t < count(); ++t) + { + TQTab* tab = tabAt(t); + if (tab->iconSet()) + s = s.expandedTo(tab->iconSet()->pixmap(TQIconSet::Large, TQIconSet::Normal).size()); + } + + int x = 0; + for (int t = 0; t < count(); ++t) { + TQTab* tab = tabAt(TQApplication::reverseLayout() ? count() - t - 1 : t); + int h = fh; + if (tab->iconSet()) + h += 4 + s.height() + 4; + TQRect r = tab->rect(); + + int fw = fm.size( Qt::SingleLine|Qt::ShowPrefix, tab->text() ).width(); + int iw = 0; + if ( tab->iconSet() != 0 ) + iw = tab->iconSet()->pixmap( TQIconSet::Large, TQIconSet::Normal ).width(); + int w = QMAX(iw, fw + 6 + 6 ) + hframe; + h += ((KickerSettings::kickoffTabBarFormat() != KickerSettings::IconOnly) ? fm.height() : 0) + vframe; + tab->setRect(TQRect(TQPoint(x, 0), style().sizeFromContents(TQStyle::CT_TabBarTab, this, + TQSize(w, h), TQStyleOption(tab)))); + x += tab->rect().width() - overlap; + } +} + +void KickoffTabBar::dragEnterEvent(TQDragEnterEvent* event) +{ + event->accept(KMenuItemDrag::canDecode(event)); +} + +void KickoffTabBar::dragMoveEvent(TQDragMoveEvent* event) +{ + TQTab* t = selectTab(event->pos()); + + // ### uhhh, look away + if (t && t->identifier() == 0) + { + setCurrentTab(t); + } +} + +void KickoffTabBar::mousePressEvent( TQMouseEvent * e ) +{ + if ( e->button() != LeftButton ) { + e->ignore(); + return; + } + TQTab *t = selectTab( e->pos() ); + if ( t && t->isEnabled() ) { + emit tabClicked(t); + } + TQTabBar::mousePressEvent(e); +} + +#include "kickoff_bar.moc" +// vim:cindent:sw=4: diff --git a/kicker/kicker/ui/kickoff_bar.h b/kicker/kicker/ui/kickoff_bar.h new file mode 100644 index 000000000..2330ffe39 --- /dev/null +++ b/kicker/kicker/ui/kickoff_bar.h @@ -0,0 +1,53 @@ +/***************************************************************** + + Copyright (c) 1996-2000 the kicker authors. See file AUTHORS. + Copyright (c) 2006 Dirk Mueller + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +******************************************************************/ + +#ifndef __kickoff_bar_h__ +#define __kickoff_bar_h__ + +#include + +class KickoffTabBar : public TQTabBar +{ + Q_OBJECT +public: + KickoffTabBar(TQWidget* parent, const char* name); + + void deactivateTabs(bool b); + virtual TQSize sizeHint() const; + +protected: + virtual void paint(TQPainter*, TQTab*, bool) const; + virtual void paintLabel(TQPainter* p, const TQRect& br, TQTab* t, bool has_focus) const; + virtual void layoutTabs(); + virtual void dragEnterEvent(TQDragEnterEvent*); + virtual void dragMoveEvent(TQDragMoveEvent*); + virtual void mousePressEvent ( TQMouseEvent * ); + +signals: + void tabClicked(TQTab*); + +private: + bool m_tabsActivated; +}; + + +#endif diff --git a/kicker/kicker/ui/kmenuitembase.ui b/kicker/kicker/ui/kmenuitembase.ui new file mode 100644 index 000000000..0cbea1155 --- /dev/null +++ b/kicker/kicker/ui/kmenuitembase.ui @@ -0,0 +1,141 @@ + +KMenuItemBase + + + KMenuItemBase + + + + 0 + 0 + 514 + 80 + + + + + 7 + 5 + 0 + 0 + + + + + 0 + 0 + + + + + 32767 + 80 + + + + KMenuItemBase + + + + unnamed + + + 2 + + + + layout11 + + + + unnamed + + + + itemTitle + + + + 7 + 1 + 1 + 0 + + + + + 14 + + + + + + + RichText + + + WordBreak|AlignTop + + + + + itemDescription + + + + 7 + 7 + 0 + 1 + + + + + 188 + 188 + 188 + + + + + + + RichText + + + WordBreak|AlignTop + + + + + + + layout4 + + + + unnamed + + + + itemPixmap + + + + 64 + 64 + + + + + + + AlignTop|AlignHCenter + + + + + + + + diff --git a/kicker/kicker/ui/media_watcher.cpp b/kicker/kicker/ui/media_watcher.cpp new file mode 100644 index 000000000..72efc897f --- /dev/null +++ b/kicker/kicker/ui/media_watcher.cpp @@ -0,0 +1,57 @@ +/***************************************************************** + +Copyright (c) 2006 Stephan Kulow + +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 "media_watcher.h" +#include +#include +#include +#include + +MediaWatcher::MediaWatcher( TQObject *parent ) : + TQObject( parent ), DCOPObject("mediawatcher") +{ + connectDCOPSignal( "kded", "mediamanager", "mediumAdded(TQString,bool)", + "slotMediumAdded(TQString,bool)", true ); + connectDCOPSignal( "kded", "mediamanager", "mediumRemoved(TQString,bool)", + "slotMediumAdded(TQString,bool)", true ); + connectDCOPSignal( "kded", "mediamanager", "mediumChanged(TQString,bool)", + "slotMediumAdded(TQString,bool)", true ); + + updateDevices(); +} + +void MediaWatcher::updateDevices() +{ + DCOPRef nsd( "kded", "mediamanager" ); + nsd.setDCOPClient( kapp->dcopClient() ); + m_devices = nsd.call( "fullList" ); +} + +void MediaWatcher::slotMediumAdded( TQString item, bool a ) +{ + updateDevices(); + + emit mediumChanged(); +} + +#include "media_watcher.moc" diff --git a/kicker/kicker/ui/media_watcher.h b/kicker/kicker/ui/media_watcher.h new file mode 100644 index 000000000..604fcaabe --- /dev/null +++ b/kicker/kicker/ui/media_watcher.h @@ -0,0 +1,51 @@ +/***************************************************************** + +Copyright (c) 2006 Stephan Kulow + +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 _media_watcher_ +#define _media_watcher_ + +#include +#include +#include + +class MediaWatcher : public TQObject, public DCOPObject +{ + Q_OBJECT + K_DCOP + + TQStringList m_devices; + void updateDevices(); + +k_dcop: + void slotMediumAdded(TQString medium, bool a); + +signals: + void mediumChanged(); + +public: + MediaWatcher(TQObject *parent); + + TQStringList devices() const { return m_devices; } +}; + +#endif diff --git a/kicker/kicker/ui/mykickoffsearchinterface.cpp b/kicker/kicker/ui/mykickoffsearchinterface.cpp new file mode 100644 index 000000000..e66c0ca85 --- /dev/null +++ b/kicker/kicker/ui/mykickoffsearchinterface.cpp @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2006 by Stephan Binner * + * * + * 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 "mykickoffsearchinterface.h" +#include "../ui/k_new_mnu.h" + +MyKickoffSearchInterface::MyKickoffSearchInterface( KMenu* menu, TQObject* parent, const char* name ) + : KickoffSearchInterface( parent, name ), _menu( menu ) +{ +} + +bool MyKickoffSearchInterface::anotherHitMenuItemAllowed(int cat) +{ + return _menu->anotherHitMenuItemAllowed(cat); +} + +void MyKickoffSearchInterface::addHitMenuItem(HitMenuItem* item) +{ + _menu->addHitMenuItem(item); +} + + +void MyKickoffSearchInterface::searchOver() +{ + _menu->searchOver(); +} + +void MyKickoffSearchInterface::initCategoryTitlesUpdate() +{ + _menu->initCategoryTitlesUpdate(); +} + +void MyKickoffSearchInterface::updateCategoryTitles() +{ + _menu->updateCategoryTitles(); +} + +#include "mykickoffsearchinterface.moc" diff --git a/kicker/kicker/ui/mykickoffsearchinterface.h b/kicker/kicker/ui/mykickoffsearchinterface.h new file mode 100644 index 000000000..b728718c4 --- /dev/null +++ b/kicker/kicker/ui/mykickoffsearchinterface.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2006 by Stephan Binner * + * * + * 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 MYKICKOFFSEARCHINTERFACE_H +#define MYKICKOFFSEARCHINTERFACE_H + +#include "../interfaces/kickoffsearchinterface.h" + +class KMenu; + +using namespace KickoffSearch; + +class MyKickoffSearchInterface :public KickoffSearchInterface +{ + Q_OBJECT + +public: + MyKickoffSearchInterface( KMenu*, TQObject* parent, const char* name = 0 ); + + bool anotherHitMenuItemAllowed(int cat); + void addHitMenuItem(HitMenuItem* item); + void searchOver(); + void initCategoryTitlesUpdate(); + void updateCategoryTitles(); + +private: + KMenu* _menu; + +}; + +#endif /* MYKICKOFFSEARCHINTERFACE_H */ diff --git a/kicker/kicker/ui/query.cpp b/kicker/kicker/ui/query.cpp new file mode 100644 index 000000000..12b9b6910 --- /dev/null +++ b/kicker/kicker/ui/query.cpp @@ -0,0 +1,136 @@ +/***************************************************************** + + Copyright (c) 2006 Stephan Binner + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +******************************************************************/ + +#include "query.h" +#include + +Query::Query() +{ + alternatives.setAutoDelete(true); +} + +void Query::clear() +{ + query_term = TQString::null; + alternatives.clear(); +} + +void Query::set(const TQString &term) +{ + query_term = term; + alternatives.clear(); + + current_alternative = new Alternative; + current_part = TQString::null; + within_quotes = false; + exclude_part = false; + + for (uint index=0;indexexcludes+=current_part.lower(); + else + current_alternative->includes+=current_part.lower(); + } + within_quotes = false; + exclude_part = false; + current_part = TQString::null; +} + +TQString Query::get() const +{ + return query_term; +} + +bool Query::matches(const TQString &term) +{ + TQString lower_term = term.lower(); + + for (Alternative* alt=alternatives.first(); alt; alt=alternatives.next()) { + if (!alt->includes.count()) + continue; + + bool next_alternative = false; + + for ( TQStringList::ConstIterator it = alt->excludes.begin(); it != alt->excludes.end(); ++it ) { + if ( lower_term.find(*it)!=-1 ) { + next_alternative = true; + continue; + } + } + if (next_alternative) + continue; + + for ( TQStringList::ConstIterator it = alt->includes.begin(); it != alt->includes.end(); ++it ) { + if ( lower_term.find(*it)==-1 ) { + next_alternative = true; + continue; + } + } + if (next_alternative) + continue; + +//kdDebug() << "Found hit in '" << term << "'" << endl; + return true; + } + + return false; +} diff --git a/kicker/kicker/ui/query.h b/kicker/kicker/ui/query.h new file mode 100644 index 000000000..ea3eab43e --- /dev/null +++ b/kicker/kicker/ui/query.h @@ -0,0 +1,55 @@ +/***************************************************************** + + Copyright (c) 2006 Stephan Binner + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +******************************************************************/ + +#ifndef QUERY_H +#define QUERY_H + +#include +#include + +class Alternative +{ +public: + TQStringList includes; + TQStringList excludes; +}; + +class Query +{ + public: + Query(); + void clear(); + void set(const TQString &); + TQString get() const; + bool matches(const TQString &); + + private: + TQString query_term; + TQPtrList alternatives; + + void add_term(); + TQString current_part; + Alternative *current_alternative; + bool within_quotes; + bool exclude_part; +}; + +#endif diff --git a/kicker/libkicker/kickerSettings.kcfg b/kicker/libkicker/kickerSettings.kcfg index 6f7f2427e..99625809d 100644 --- a/kicker/libkicker/kickerSettings.kcfg +++ b/kicker/libkicker/kickerSettings.kcfg @@ -98,6 +98,70 @@ + + + true + + + + + true + + + + + true + + + + + 0 + + + + + 0 + + + + + 0 + -100 + 100 + + + + + false + + + + + false + + + + + + + + + + + + + + + LabelAndIcon + + + + + + true + + + @@ -177,6 +241,19 @@ false + + + + + + + false + + + + + + @@ -352,6 +429,29 @@ + + + + + + + + 50 + + + + + + + + + + 2 + + + + + diff --git a/kicker/libkicker/kickertip.cpp b/kicker/libkicker/kickertip.cpp index 34cf65c56..215320c84 100644 --- a/kicker/libkicker/kickertip.cpp +++ b/kicker/libkicker/kickertip.cpp @@ -38,6 +38,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // putting this #include higher results in compile errors #include +#include static const int DEFAULT_FRAMES_PER_SECOND = 30; @@ -71,14 +72,16 @@ KickerTip::KickerTip(TQWidget * parent) m_dissolveDelta(-1), m_direction(KPanelApplet::Up), m_dirty(false), - m_toolTipsEnabled(KickerSettings::showToolTips()), - m_tippingFor(0) + m_tippingFor(0), + m_timer(0, "KickerTip::m_timer"), + m_frameTimer(0, "KickerTip::m_frameTimer") { setFocusPolicy(NoFocus); setBackgroundMode(NoBackground); resize(0, 0); hide(); connect(&m_frameTimer, TQT_SIGNAL(timeout()), TQT_SLOT(internalUpdate())); + connect(kapp, TQT_SIGNAL(settingsChanged(SettingsCategory)), TQT_SLOT(slotSettingsChanged())); } KickerTip::~KickerTip() @@ -87,6 +90,11 @@ KickerTip::~KickerTip() delete m_mimeFactory; } +void KickerTip::slotSettingsChanged() +{ + TQToolTip::setGloballyEnabled(KickerSettings::showToolTips()); +} + void KickerTip::display() { if (!tippingEnabled()) @@ -194,7 +202,7 @@ void KickerTip::paintEvent(TQPaintEvent * e) void KickerTip::mousePressEvent(TQMouseEvent * /*e*/) { - TQToolTip::setGloballyEnabled(m_toolTipsEnabled); + m_timer.stop(); hide(); } @@ -463,8 +471,11 @@ void KickerTip::enableTipping(bool tip) m_tippingEnabled--; } + assert(m_tippingEnabled >= -1); + if (m_tippingEnabled < 1 && m_self) { + m_self->m_timer.stop(); m_self->hide(); } } @@ -480,6 +491,8 @@ void KickerTip::hide() m_timer.stop(); m_frameTimer.stop(); TQWidget::hide(); + + TQToolTip::setGloballyEnabled(KickerSettings::showToolTips()); } bool KickerTip::eventFilter(TQObject *object, TQEvent *event) @@ -508,7 +521,6 @@ bool KickerTip::eventFilter(TQObject *object, TQEvent *event) !qApp->activePopupWidget() && !isTippingFor(widget)) { - m_toolTipsEnabled = TQToolTip::isGloballyEnabled(); TQToolTip::setGloballyEnabled(false); tipFor(widget); @@ -530,8 +542,6 @@ bool KickerTip::eventFilter(TQObject *object, TQEvent *event) } break; case TQEvent::Leave: - TQToolTip::setGloballyEnabled(m_toolTipsEnabled); - m_timer.stop(); if (isTippingFor(widget) && isVisible()) @@ -544,7 +554,7 @@ bool KickerTip::eventFilter(TQObject *object, TQEvent *event) tipFor(0); break; case TQEvent::MouseButtonPress: - TQToolTip::setGloballyEnabled(m_toolTipsEnabled); + m_timer.stop(); hide(); default: break; diff --git a/kicker/libkicker/kickertip.h b/kicker/libkicker/kickertip.h index 6bcc863f8..14118029b 100644 --- a/kicker/libkicker/kickertip.h +++ b/kicker/libkicker/kickertip.h @@ -92,6 +92,7 @@ protected slots: void tipperDestroyed(TQObject* o); void internalUpdate(); void display(); + void slotSettingsChanged(); private: TQBitmap m_mask; @@ -108,7 +109,6 @@ private: TQTimer m_timer; TQTimer m_frameTimer; bool m_dirty; - bool m_toolTipsEnabled; const TQWidget* m_tippingFor; diff --git a/kicker/libkicker/panelbutton.cpp b/kicker/libkicker/panelbutton.cpp index 972e5195e..f1ab69fb8 100644 --- a/kicker/libkicker/panelbutton.cpp +++ b/kicker/libkicker/panelbutton.cpp @@ -44,6 +44,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #include "global.h" @@ -67,6 +68,7 @@ PanelButton::PanelButton( TQWidget* parent, const char* name ) m_hasAcceptedDrag(false), m_arrowDirection(KPanelExtension::Bottom), m_popupDirection(KPanelApplet::Up), + m_iconAlignment(AlignCenter), m_orientation(Horizontal), m_size((KIcon::StdSizes)-1), m_fontPercent(0.40) @@ -188,6 +190,12 @@ void PanelButton::setPopupDirection(KPanelApplet::Direction d) setArrowDirection(KickerLib::directionToPopupPosition(d)); } +void PanelButton::setIconAlignment(AlignmentFlags align) +{ + m_iconAlignment = align; + update(); +} + void PanelButton::setOrientation(Orientation o) { m_orientation = o; @@ -303,7 +311,9 @@ int PanelButton::widthForHeight(int height) const int PanelButton::heightForWidth(int width) const { - return preferredDimension(width); + int rc=preferredDimension(width); + + return rc; } const TQPixmap& PanelButton::labelIcon() const @@ -562,11 +572,16 @@ void PanelButton::drawButtonLabel(TQPainter *p) icon.height() - 2); } + int y = 0; + if (m_iconAlignment & AlignVCenter) + y = (height() - icon.height()) / 2; + else if (m_iconAlignment & AlignBottom) + y = (height() - icon.height()); + if (!m_buttonText.isEmpty() && orientation() == Horizontal) { int h = height(); int w = width(); - int y = (h - icon.height())/2; p->save(); TQFont f = font(); @@ -635,8 +650,11 @@ void PanelButton::drawButtonLabel(TQPainter *p) } else if (!icon.isNull()) { - int y = (height() - icon.height()) / 2; - int x = (width() - icon.width()) / 2; + int x = 0; + if (m_iconAlignment & AlignHCenter) + x = (width() - icon.width()) / 2; + else if (m_iconAlignment & AlignRight) + x = (width() - icon.width()); p->drawPixmap(x, y, icon); } @@ -798,7 +816,19 @@ void PanelButton::loadIcons() TQString nm = m_iconName; KIcon::States defaultState = isEnabled() ? KIcon::DefaultState : KIcon::DisabledState; - m_icon = ldr->loadIcon(nm, KIcon::Panel, m_size, defaultState, 0L, true); + if (nm=="kmenu-suse") + { + TQString pth = locate( "data", "kicker/pics/kmenu_basic.mng" ); + if (!pth.isEmpty()) + { + m_icon = TQImage(pth); + m_iconh = TQPixmap(m_icon); + m_iconz = TQPixmap(m_icon); + return; + } + } + else + m_icon = ldr->loadIcon(nm, KIcon::Panel, m_size, defaultState, 0L, true); if (m_icon.isNull()) { @@ -863,7 +893,7 @@ PanelPopupButton::PanelPopupButton(TQWidget *parent, const char *name) connect(this, TQT_SIGNAL(pressed()), TQT_SLOT(slotExecMenu())); } -void PanelPopupButton::setPopup(TQPopupMenu *popup) +void PanelPopupButton::setPopup(TQWidget *popup) { if (m_popup) { @@ -881,7 +911,7 @@ void PanelPopupButton::setPopup(TQPopupMenu *popup) } } -TQPopupMenu *PanelPopupButton::popup() const +TQWidget *PanelPopupButton::popup() const { return m_popup; } @@ -960,7 +990,9 @@ void PanelPopupButton::slotExecMenu() } m_popup->adjustSize(); - m_popup->exec(KickerLib::popupPosition(popupDirection(), m_popup, this)); + if(dynamic_cast(m_popup)) + static_cast(m_popup)->exec(KickerLib::popupPosition(popupDirection(), m_popup, this)); + // else.. hmm. some derived class has to fix it. } void PanelPopupButton::menuAboutToHide() @@ -970,8 +1002,10 @@ void PanelPopupButton::menuAboutToHide() return; } - setDown(false); - KickerTip::enableTipping(true); + if (isDown()) { + setDown(false); + KickerTip::enableTipping(true); + } } void PanelPopupButton::triggerDrag() @@ -989,3 +1023,5 @@ void PanelPopupButton::setInitialized(bool initialized) m_initialized = initialized; } + + diff --git a/kicker/libkicker/panelbutton.h b/kicker/libkicker/panelbutton.h index 78f36ed5c..770d0e1be 100644 --- a/kicker/libkicker/panelbutton.h +++ b/kicker/libkicker/panelbutton.h @@ -254,9 +254,11 @@ public slots: /** * Sets the direction to pop up the contents of the button. */ - void setPopupDirection(KPanelApplet::Direction d); + virtual void setPopupDirection(KPanelApplet::Direction d); protected: + + void setIconAlignment(AlignmentFlags align); /** * Subclasses must implement this to define the name of the button which is * used to identify this button for saving and loading. It must be unique @@ -391,6 +393,7 @@ private: TQPixmap m_iconz; // mouse over KPanelExtension::Position m_arrowDirection; KPanelApplet::Direction m_popupDirection; + AlignmentFlags m_iconAlignment; Orientation m_orientation; int m_size; double m_fontPercent; @@ -419,12 +422,12 @@ public: * Sets the button's popup menu. * @param popup the menu to pop up */ - void setPopup(TQPopupMenu *popup); + void setPopup(TQWidget *popup); /** * @return the button's popup menu */ - TQPopupMenu *popup() const; + TQWidget *popup() const; bool eventFilter(TQObject *, TQEvent *); virtual void showMenu(); @@ -459,8 +462,8 @@ protected slots: private slots: void menuAboutToHide(); -private: - TQPopupMenu *m_popup; +protected: + TQWidget *m_popup; bool m_pressedDuringPopup; bool m_initialized; diff --git a/kicker/taskbar/taskbar.cpp b/kicker/taskbar/taskbar.cpp index 88768e811..808408fc9 100644 --- a/kicker/taskbar/taskbar.cpp +++ b/kicker/taskbar/taskbar.cpp @@ -59,7 +59,8 @@ TaskBar::TaskBar( TQWidget *parent, const char *name ) m_showIcon(false), m_showOnlyIconified(false), m_textShadowEngine(0), - m_ignoreUpdates(false) + m_ignoreUpdates(false), + m_relayoutTimer(0, "TaskBar::m_relayoutTimer") { arrowType = LeftArrow; blocklayout = true; diff --git a/kicker/taskbar/taskcontainer.cpp b/kicker/taskbar/taskcontainer.cpp index 6bc74044e..1e4829a38 100644 --- a/kicker/taskbar/taskcontainer.cpp +++ b/kicker/taskbar/taskcontainer.cpp @@ -67,7 +67,11 @@ TaskContainer::TaskContainer(Task::Ptr task, TaskBar* bar, discardNextMouseEvent(false), aboutToActivate(false), m_mouseOver(false), - m_paintEventCompression(false) + animationTimer(0, "TaskContainer::animationTimer"), + dragSwitchTimer(0, "TaskContainer::dragSwitchTimer"), + attentionTimer(0, "TaskContainer::attentionTimer"), + m_paintEventCompression(false), + m_paintEventCompressionTimer(0, "TaskContainer::paintEventCompressionTimer") { init(); setAcceptDrops(true); // Always enabled to activate task during drag&drop. @@ -95,7 +99,11 @@ TaskContainer::TaskContainer(Startup::Ptr startup, PixmapList& startupFrames, discardNextMouseEvent(false), aboutToActivate(false), m_mouseOver(false), - m_paintEventCompression(false) + animationTimer(0, "TaskContainer::animationTimer"), + dragSwitchTimer(0, "TaskContainer::dragSwitchTimer"), + attentionTimer(0, "TaskContainer::attentionTimer"), + m_paintEventCompression(false), + m_paintEventCompressionTimer(0, "TaskContainer::paintEventCompressionTimer") { init(); setEnabled(false);