/* OLPC Floating Toolbar * Copyright (C) 2006 Marc Maurer * * 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., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifdef ABI_PLUGIN_BUILTIN #define abi_plugin_register abipgn_olpctoolbar_register #define abi_plugin_unregister abipgn_olpctoolbar_unregister #define abi_plugin_supports_version abipgn_olpctoolbar_supports_version #endif #include "xap_Module.h" #include "OlpcToolbar.h" #include "fv_View.h" #include "fp_Run.h" #include "fp_Line.h" #define TOOLBAR_DELAY 1000 /* in milliseconds */ // ----------------------------------------------------------------------- // // Abiword Plugin Interface // // ----------------------------------------------------------------------- ABI_PLUGIN_DECLARE ("AbiOlpcToolbar") // We, the mighty SessionManager, manage it all! OlpcToolbarManager s_OlpcToolbarManager; OlpcToolbarManager* OlpcToolbarManager::m_pManager = NULL; OlpcToolbarManager* OlpcToolbarManager::getManager() { return m_pManager; } ABI_FAR_CALL int abi_plugin_register (XAP_ModuleInfo * mi) { mi->name = "Floating Toolbar"; mi->desc = "Floating context sensitive toolbar designed for the One Laptop Per Child system"; mi->version = ABI_VERSION_STRING; mi->author = "Marc Maurer"; mi->usage = "No Usage"; return 1; } ABI_FAR_CALL int abi_plugin_unregister (XAP_ModuleInfo * mi) { mi->name = 0; mi->desc = 0; mi->version = 0; mi->author = 0; mi->usage = 0; return 1; } ABI_FAR_CALL int abi_plugin_supports_version (UT_uint32 major, UT_uint32 minor, UT_uint32 release) { return 1; } static void s_toolbar_timer_callback(UT_Worker *pWorker) { UT_DEBUGMSG(("s_toolbar_timer_callback()\n")); OlpcToolbarManager* pManager = OlpcToolbarManager::getManager(); if (pManager) { pManager->eventTimer( reinterpret_cast(pWorker->getInstanceData()), reinterpret_cast(pWorker) ); } else UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } OlpcToolbarManager::OlpcToolbarManager() : m_avListenerId(0) { XAP_App* pApp = XAP_App::getApp(); if (pApp) { // add a view listener pApp->addListener(this, &m_avListenerId); } m_pManager = this; } OlpcToolbarManager::~OlpcToolbarManager() { // TODO: remove the view listener } bool OlpcToolbarManager::notify(AV_View *pView, const AV_ChangeMask mask) { UT_return_val_if_fail(pView, false); bool bBetween = false; bool bDoIt = false; if (mask & AV_CHG_MOUSEPOS) { // The selection changed; now figure out if we do have a selection or not xxx_UT_DEBUGMSG(("Looking in OLPC toolbar \n")); FV_View *pV = dynamic_cast(pView); if (pV) { if (pV->isSelectionEmpty()) { xxx_UT_DEBUGMSG(("OlpcToolbarManager::notify - selection is empty!\n")); // kill the timer belonging to this view (if any) ToolbarTimerMap::iterator timerPos = m_toolbarTimers.find(pView); if (timerPos != m_toolbarTimers.end()) { UT_Timer* pTimer = timerPos->second; if (pTimer) { pTimer->stop(); DELETEP(pTimer); } m_toolbarTimers.erase(timerPos); UT_DEBUGMSG(("OlpcToolbarManager::notify - timer killed for this view\n")); } // delete the OLPC toolbar for this view (if we already have one) ToolbarMap::iterator toolbarPos = m_toolbars.find(pView); if (toolbarPos != m_toolbars.end()) { EV_UnixOlpcToolbar* pToolbar = toolbarPos->second; if (pToolbar) { DELETEP(pToolbar); } m_toolbars.erase(toolbarPos); xxx_UT_DEBUGMSG(("OlpcToolbarManager::notify - toolbar deleted for this view\n")); } } else { PT_DocPosition pos = pV->getDocPositionFromLastXY(); PT_DocPosition left = pV->getSelectionLeftAnchor(); PT_DocPosition right = pV->getSelectionRightAnchor(); bBetween = ((pos >= left) && (pos < right)); xxx_UT_DEBUGMSG((" left %d pos %d right %d bBetween %d \n",left,pos,right,bBetween)); bDoIt = bBetween; if(bDoIt) { ToolbarMap::iterator toolbarPos = m_toolbars.find(pView); if (toolbarPos != m_toolbars.end()) { // we have toolbar on this view already bDoIt = false; } } if(bDoIt) { xxx_UT_DEBUGMSG(("OlpcToolbarManager::notify - hovering on selection!\n")); // start a toolbar timer for this view // TODO: check if we don't already have one ToolbarTimerMap::iterator timerPos = m_toolbarTimers.find(pView); ToolbarTimerMap::iterator pos = m_toolbarTimers.find(pView); if (pos != m_toolbarTimers.end()) { // already have a timer running bDoIt = false; } } if(bDoIt) { UT_Timer* pTimer = UT_Timer::static_constructor(s_toolbar_timer_callback, pView); pTimer->set(TOOLBAR_DELAY); m_toolbarTimers.insert(ToolbarTimerMap::value_type(pView, pTimer)); pTimer->start(); } else if(!bBetween) { ToolbarTimerMap::iterator timerPos = m_toolbarTimers.find(pView); if (timerPos != m_toolbarTimers.end()) { UT_Timer* pTimer = timerPos->second; if (pTimer) { pTimer->stop(); DELETEP(pTimer); } m_toolbarTimers.erase(timerPos); xxx_UT_DEBUGMSG(("OlpcToolbarManager::notify - timer killed for this view\n")); } xxx_UT_DEBUGMSG(("OlpcToolbarManager::notify - timer killed for this view\n")); } } } else UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } return true; } AV_ListenerType OlpcToolbarManager::getType(void) { return AV_LISTENER_TOOLBAR; // I think ;) } void OlpcToolbarManager::eventTimer(AV_View* pView, UT_Timer* pTimer) { UT_DEBUGMSG(("OlpcToolbarManager::eventTimer()\n")); UT_return_if_fail(pView); UT_return_if_fail(pTimer); // thanks timer, you're not useful anymore pTimer->stop(); ToolbarTimerMap::iterator pos = m_toolbarTimers.find(pView); if (pos != m_toolbarTimers.end()) { m_toolbarTimers.erase(pos); } DELETEP(pTimer); // now for the fun part: show the toolbar! FV_View *pV = dynamic_cast(pView); if (pV) { if (!pV->isSelectionEmpty()) { PT_DocPosition curPos = pV->getInsPoint(); fl_BlockLayout* pLayout = pV->getBlockAtPosition(curPos); if (pLayout) { UT_sint32 x; UT_sint32 y; UT_sint32 x2; UT_sint32 y2; UT_sint32 height; bool bDirection; // TODO: use the proper direction fp_Run* pRun = pLayout->findPointCoords(curPos, false, x, y, x2, y2, height, bDirection); if (pRun) { fp_Page* pPage = pRun->getLine()->getPage(); if (pPage) { UT_sint32 pageXOff; UT_sint32 pageYOff; pV->getPageScreenOffsets(pPage, pageXOff, pageYOff); UT_DEBUGMSG(("Page Screen Offsets: x:%d, y:%d, ys:%d\n", pageXOff, pageYOff, pV->getYScrollOffset())); y += pageYOff; // // Subtract the height of run to make the text visible. y -= pRun->getHeight(); XAP_Frame* pFrame = XAP_App::getApp()->getLastFocussedFrame(); if (pFrame) { // TODO: fix bad cast non-XP cast AP_UnixFrameImpl* pFrameImpl = reinterpret_cast(pFrame->getFrameImpl()); GtkWidget* pDA = pFrameImpl->getDrawingArea(); if (pDA) { GdkWindow* window = pDA->window; gint wx, wy; gdk_window_get_origin(window, &wx, &wy); GR_Graphics* pGr = pV->getGraphics(); if (pGr) { UT_sint32 totalXOffset = wx + pGr->tdu(pV->getPageViewLeftMargin()); UT_sint32 totalYOffset = wy + pGr->tdu(pV->getPageViewTopMargin()); if((totalYOffset + pGr->tdu(y)) < 160) { totalYOffset = 160 + pGr->tdu(pRun->getHeight())*3; } UT_DEBUGMSG(("YOffset is %d \n",totalYOffset)); EV_UnixOlpcToolbar* pOlpcToolbar = new EV_UnixOlpcToolbar( static_cast(XAP_App::getApp()), pFrame, "FormatOps", "en_US"); pOlpcToolbar->setReferenceCoords( totalXOffset + pGr->tdu(x), totalYOffset + pGr->tdu(y), pGr->tdu(height)); pOlpcToolbar->synthesize(); // bind view listener // pOlpcToolbar->bindListenerToView(static_cast(pV)); pOlpcToolbar->show(); xxx_UT_DEBUGMSG(("OLPC Toolbar created now refresh it \n")); m_toolbars.insert(ToolbarMap::value_type(pView, pOlpcToolbar)); static_cast(pV)->notifyListeners(AV_CHG_FMTCHAR | AV_CHG_FMTBLOCK); } else UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } } else UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } else UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } else UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } else UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } else UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); // hmmm, the selection is gone already, weird } }