/* ** 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Author : Alexandre Parenteau --- May 1999 */ #include "stdafx.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #include "umain.h" #include "uconsole.h" #include "uwidget.h" #include #if defined(_DEBUG) && defined(WIN32) # define new DEBUG_NEW # undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #if qMacAPP # include "UMenuMgr.h" # include "UCheckBox.h" # include "URadio.h" #endif #ifdef qMacPP # include "UPseudoSupport.h" # include # include # include # include # include #endif #ifdef WIN32 # include "PseudoButton.h" #endif /*! \def MIN_IDLE minimum idle used by the framework to handle the timers */ #define MIN_IDLE 100 static long sLastIdle = 0; // handler to forward the tooltip (usually the status bar) extern "C" { static void (*sTooltipHandler) (const char *) = 0L; } // all the framework widgets are located there class CAllTargets { public: CAllTargets() {} ~CAllTargets() { // delete the remaining widgets at exit. // we don't make a loop because the widget gets // removed from the map when calling the destructor. STD map::iterator i; while(targets.size() != 0) { i = targets.begin(); delete (*i).second; } } inline STD map & Get(void) { return targets; } inline const STD map & Get(void) const { return targets; } protected: STD map targets; } sAllTargets; const UMSGMAP* UCmdTarget::GetBaseMessageMap() { return 0L; } const UMSGMAP* UCmdTarget::GetThisMessageMap() { return &UCmdTarget::messageMap; } const UMSGMAP* UCmdTarget::GetMessageMap() const { return &UCmdTarget::messageMap; } const UMSGMAP UCmdTarget::messageMap = { 0L, &UCmdTarget::_messageEntries[0] }; const UMSGMAP_ENTRY UCmdTarget::_messageEntries[] = { {EV_NONE, epNone, UMSG_NOEXT, 0, 0, (UPMSG)0 } }; static bool sTurnOffDispatching = false; class CTurnOffDispatching { public: CTurnOffDispatching() : m_didSetIt(false) { if(!sTurnOffDispatching) { m_didSetIt = true; sTurnOffDispatching = true; } } ~CTurnOffDispatching() { if(m_didSetIt) sTurnOffDispatching = false; } protected: bool m_didSetIt; }; UIMPLEMENT_DYNAMIC(UCmdTarget, UObject) // the message class used to post retained messages class CMsg { public: CMsg(int wid, int ev, int arg1, void *arg2) : m_wid(wid), m_ev(ev), m_arg1(arg1), m_arg2(arg2) { } CMsg() : m_wid(0), m_ev(0), m_arg1(0), m_arg2(0L) { } CMsg(const CMsg & msg) : m_wid(0), m_ev(0), m_arg1(0), m_arg2(0L) { *this = msg; } CMsg & operator=(const CMsg & msg) { m_wid = msg.m_wid; m_ev = msg.m_ev; m_arg1 = msg.m_arg1; m_arg2 = msg.m_arg2; return *this; } int m_wid, m_ev, m_arg1; void *m_arg2; }; static std::vector sRetainedMsgs; union UMessageMapFunctions { UPMSG pfn; // generic member function pointer // specific type safe variants void(UCmdTarget::*pfnV_V)(void); void(UCmdTarget::*pfnV_VP)(void *); void(UCmdTarget::*pfnV_iVP)(int,void *); void(UCmdTarget::*pfnV_i)(int); void(UCmdTarget::*pfnV_ii)(int, int); void(UCmdTarget::*pfnV_iii)(int, int, int); void(UCmdTarget::*pfnV_iiVP)(int, int, void *); int(UCmdTarget::*pfni_ii)(int, int); int(UCmdTarget::*pfni_i)(int); int(UCmdTarget::*pfni_V)(); int(UCmdTarget::*pfni_iVP)(int,void *); int(UCmdTarget::*pfni_VP)(void *); }; int UEventSendMessage(int wid, int/*UEventType*/ evt, int arg1, const void *arg2) { if(sTurnOffDispatching) return 0; const STD map & targets = sAllTargets.Get(); STD map< int, UCmdTarget * >::const_iterator iter = targets.find(wid); int res = 0; if(iter != targets.end()) { UCmdTarget *target = (*iter).second; UCmdTarget::UDispatchInfo dispatchInfo = UCmdTarget::kUNoDispatchInfo; #if qDebug try { #endif res = target->UDispatchMessage(evt, arg1, arg2, dispatchInfo); #if qDebug } catch(...) { UASSERT(0); throw; } #endif } else { UAppConsole("Unknown widget id : %d\n", wid); } return res; } int UEventSendMessageExt(int wid, int/*UEventType*/ evt, int arg1, void *arg2, int * handledFlag) { const STD map & targets = sAllTargets.Get(); STD map< int, UCmdTarget * >::const_iterator iter = targets.find(wid); int res = 0; UCmdTarget::UDispatchInfo dispatchInfo = UCmdTarget::kUNoDispatchInfo; if(iter != targets.end()) { UCmdTarget *target = (*iter).second; #if qDebug try { #endif res = target->UDispatchMessage(evt, arg1, arg2, dispatchInfo); #if qDebug } catch(...) { UASSERT(0); throw; } #endif } else { UAppConsole("Unknown widget id : %d\n", wid); } *handledFlag = (dispatchInfo & UCmdTarget::kUDispatchHandled) != 0; return res; } // for the retained messages int UEventPostMessage(int wid, int/*UEventType*/ evt, int arg1, void *arg2) { sRetainedMsgs.push_back(CMsg(wid, evt, arg1, arg2)); return 0; } // a notify message get broadcasted to every in the map. int UEventNotifyMessage(int/*UEventType*/ evt, int arg1, void *arg2) { STD map< int, UCmdTarget * >::const_iterator iter; const STD map & targets = sAllTargets.Get(); int res = 0; for(iter = targets.begin(); iter != targets.end(); ++iter) { UCmdTarget *target = (*iter).second; UCmdTarget::UDispatchInfo dispatchInfo = UCmdTarget::kUNoDispatchInfo; #if qDebug try { #endif res = target->UDispatchMessage(evt, arg1, arg2, dispatchInfo); #if qDebug } catch(...) { UASSERT(0); throw; } #endif if(res) break; } return res; } int UEventGetWidID(void) { static int newid = 100; return newid++; } UCmdTarget *UEventQueryWidget(int widid) { const STD map & targets = sAllTargets.Get(); STD map< int, UCmdTarget * >::const_iterator iter = targets.find(widid); if(iter != targets.end()) { UCmdTarget *target = (*iter).second; return target; } return 0L; } void UEventRemoveWidID(int widid) { STD map & targets = sAllTargets.Get(); STD map::iterator i = targets.find(widid); if(i == targets.end()) return; targets.erase(i); } int UCmdTarget::UDispatchMessage(int/*UEventType*/ evt, int arg1, const void *arg2, UDispatchInfo & dispatchInfo) { const UMSGMAP* themap; const UMSGMAP_ENTRY *entries; for(themap = GetMessageMap(); themap != 0L; themap = themap->pBaseMap) { entries = themap->lpEntries; while(entries->nMessage != EV_NONE) { if(entries->ext == UMSG_RANGE) { int cmd = (unsigned short)ULOWORD(arg1); if(evt == entries->nMessage && cmd >= entries->ext1 && cmd <= entries->ext2) goto dispatch; } else if(entries->nMessage == evt) goto dispatch; entries++; } } return 0; dispatch: union UMessageMapFunctions mmf; mmf.pfn = entries->pfn; int res = 0; dispatchInfo = UDispatchInfo((int)dispatchInfo | UCmdTarget::kUDispatchHandled); switch(entries->nSig) { case epV_V: (this->*mmf.pfnV_V)(); break; case epV_VP: (this->*mmf.pfnV_VP)((void *)arg2); break; case epV_iVP: (this->*mmf.pfnV_iVP)(arg1, (void *)arg2); break; case epV_IVP: (this->*mmf.pfnV_iVP)(UHIWORD(arg1), (void *)arg2); break; case epV_i: (this->*mmf.pfnV_i)(arg1); break; case epV_I: (this->*mmf.pfnV_i)(UHIWORD(arg1)); break; case epV_ii: (this->*mmf.pfnV_ii)(ULOWORD(arg1), UHIWORD(arg1)); break; case epV_iii: (this->*mmf.pfnV_iii)(ULOWORD(arg1), UHIWORD(arg1), (size_t)arg2); break; case epV_iiVP: (this->*mmf.pfnV_iiVP)(ULOWORD(arg1), UHIWORD(arg1), (void *)arg2); break; case epi_ii: res = (this->*mmf.pfni_ii)(ULOWORD(arg1), UHIWORD(arg1)); break; case epi_i: res = (this->*mmf.pfni_i)(arg1); break; case epi_V: res = (this->*mmf.pfni_V)(); break; case epi_iVP: res = (this->*mmf.pfni_iVP)(arg1, (void *)arg2); break; case epi_VP: res = (this->*mmf.pfni_VP)((void *)arg2); break; default: UAppConsole("Unknown event cast : %d\n", entries->nSig); break; } return res; } UCmdTarget::UCmdTarget(int widid) : m_widid(widid) { std::map::value_type val(widid, this); sAllTargets.Get().insert(val); } UCmdTarget::~UCmdTarget() { UEventRemoveWidID(m_widid); } void UCmdUI::Enable(bool bOn) { m_continue = false; #ifdef qMacAPP if((long)m_widget <= 2L) { // this is a menu ::Enable(m_nID, bOn); m_widget = (void *)(bOn ? 1L : 2L); // see UCmdUI::Check } else { TView *view = (TView *)m_widget; if(MA_MEMBER(view, TView)) { view->SetActiveState(bOn, kRedraw); } } #endif #ifdef qMacPP if(m_wrapper != 0L) { // this is a menu *m_wrapper->outEnabled = bOn; } else { LPane *view = (LPane *)m_widget; if(view != 0L) { if(bOn) view->Enable(); else view->Disable(); } } #endif #ifdef WIN32 if(pCmdUI != 0L) { // this is a menu pCmdUI->Enable(bOn); } else if(m_widget) { // this something else CWnd *wid = (CWnd *)m_widget; BOOL state = bOn; if(state != wid->IsWindowEnabled()) wid->EnableWindow(state); } #endif #ifdef qGTK if(m_widget != 0L) { if(GTK_IS_WIDGET(m_widget)) { gtk_widget_set_sensitive(GTK_WIDGET(m_widget), bOn); } } #endif } void UCmdUI::Check(bool bOn) { m_continue = false; #ifdef qMacAPP if((long)m_widget <= 2L) { // this is a menu if(m_widget == 0L) { UASSERT("You need to call UCmdUI::Enable before UCmdUI::Check !\n"); } else { ::EnableCheck(m_nID, m_widget == (void *)1L, bOn); } } else { TView *view = (TView *)m_widget; if(MA_MEMBER(view, TCheckBox)) { ((TCheckBox *)view)->SetState(bOn, kRedraw); } else if(MA_MEMBER(view, TRadio)) { ((TRadio *)view)->SetState(bOn, kRedraw); } } #endif #ifdef qMacPP if(m_wrapper != 0L) { // this is a menu *m_wrapper->outUsesMark = true; *m_wrapper->outMark = bOn ? checkMark : noMark; } else { LPane *view = (LPane *)m_widget; if(dynamic_cast(view) != 0L || dynamic_cast(view) != 0L || dynamic_cast(view) != 0L || dynamic_cast(view) != 0L) { USemaphore policeman(gTurnOffBroadcast); view->SetValue(bOn ? Button_On : Button_Off); } } #endif #ifdef WIN32 if(pCmdUI != 0L) { // this is a menu pCmdUI->SetCheck(bOn ? 1 : 0); } else if(m_widget) { CWnd *wnd = (CWnd *)m_widget; if(wnd->IsKindOf(RUNTIME_CLASS(CPseudoToggle))) { CPseudoToggle *btn = (CPseudoToggle *)wnd; btn->SetState(bOn); } else if(wnd->IsKindOf(RUNTIME_CLASS(CButton))) { CButton *btn = (CButton *)wnd; btn->SetCheck(bOn ? BST_CHECKED : BST_UNCHECKED); } } #endif #ifdef qGTK if(m_widget != 0L) { if(GTK_IS_TOGGLE_BUTTON(m_widget)) { CTurnOffDispatching turnOff; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_widget), bOn); } } #endif } void UCmdUI::SetText(const char *title) { m_continue = false; #ifdef qMacAPP if((long)m_widget <= 2L) { // this is a menu CStr255_AC pstr(title); SetCommandName(m_nID, pstr); } else { TCtlMgr *view = (TCtlMgr *)m_widget; if(MA_MEMBER(view, TCtlMgr)) { view->SetText(CStr255_AC(title), kRedraw); } } #endif #ifdef qGTK if(m_widget != 0L) { if(GTK_IS_TOGGLE_BUTTON(m_widget)) { GtkLabel *label = getLabelFromContainer(GTK_CONTAINER(m_widget)); if(label != 0L) gtk_label_set_text(label, title); } } #endif #ifdef qMacPP if(m_wrapper != 0L) { // this is a menu LStr255 pstr(title); LString::CopyPStr(pstr, m_wrapper->outName); } else { LPane *view = (LPane *)m_widget; LEditText *edit; LStaticText *stat; if((edit = dynamic_cast(view)) != 0L) { edit->SetText((Ptr)title, strlen(title)); } else if((stat = dynamic_cast(view)) != 0L) { stat->SetText((Ptr)title, strlen(title)); } } #endif #ifdef WIN32 if(pCmdUI != 0L) { // this is a menu if(title != 0L && *title != '\0') pCmdUI->SetText(title); } #endif } void UCmdUI::SetTooltip(const char *tooltip) { if(tooltip == 0L) return; char status[256] = {'\0'}; char prompt[256] = {'\0'}; strcpy(status, tooltip); char *cr = strchr(status, '\n'); if(cr != 0L) { strcpy(prompt, cr + 1); *cr = '\0'; } #ifdef WIN32 # define _countof(array) (sizeof(array)/sizeof(array[0])) NMHDR *hdr = (NMHDR *)pNMHDR; ASSERT(hdr->code == TTN_NEEDTEXTA || hdr->code == TTN_NEEDTEXTW); TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)hdr; TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)hdr; # ifndef _UNICODE if (hdr->code == TTN_NEEDTEXTA) lstrcpyn(pTTTA->szText, prompt, _countof(pTTTA->szText)); else _mbstowcsz(pTTTW->szText, prompt, _countof(pTTTW->szText)); # else if (hdr->code == TTN_NEEDTEXTA) _wcstombsz(pTTTA->szText, prompt, _countof(pTTTA->szText)); else lstrcpyn(pTTTW->szText, prompt, _countof(pTTTW->szText)); # endif #endif if(sTooltipHandler != 0L) sTooltipHandler(status); } void UEventSetTooltipHandler(void (*handler) (const char *)) { sTooltipHandler = handler; } void UEventGiveTime(void) { STD map< int, UCmdTarget * >::const_iterator iter; const STD map & targets = sAllTargets.Get(); // post the retained events while(!sRetainedMsgs.empty()) { CMsg msg = *sRetainedMsgs.begin(); sRetainedMsgs.erase(sRetainedMsgs.begin()); UEventSendMessage(msg.m_wid, msg.m_ev, msg.m_arg1, msg.m_arg2); } // take care about the EV_UPDATEUI notification if(!UWidget::IsUIValid()) { UWidget::DoValidUI(true); // warn the widget it's going to happen so they can cache // some informations. for(iter = targets.begin(); iter != targets.end(); ++iter) { UCmdTarget *target = (*iter).second; if(!target->IsKindOf(URUNTIME_CLASS(UWidget))) continue; UWidget *wid = (UWidget *)target; wid->BeforeUpdateUI(); } for(iter = targets.begin(); iter != targets.end(); ++iter) { UCmdTarget *target = (*iter).second; if(!target->IsKindOf(URUNTIME_CLASS(UWidget))) continue; UWidget *wid = (UWidget *)target; UEventSendMessage(wid->GetWidID(), EV_UPDATEUI, 0, 0L); } } long thisIdle = UTickCount(); if((thisIdle - sLastIdle) < MIN_IDLE) return; // search the timers for(iter = targets.begin(); iter != targets.end(); ++iter) { UCmdTarget *target = (*iter).second; if(!target->IsKindOf(URUNTIME_CLASS(UWidget))) continue; UWidget *wid = (UWidget *)target; std::vector & timers = wid->GetTimers(); std::vector::iterator t; for(t = timers.begin(); t != timers.end(); ++t) { UTimer & timer = *t; if((thisIdle - timer.m_lastlap) >= timer.m_lap) { // trigger the timer timer.m_lastlap = thisIdle; UEventSendMessage(wid->GetWidID(), EV_TIMER, timer.m_cmd, (void *)thisIdle); } } } sLastIdle = thisIdle; } long UTickCount(void) { #ifdef WIN32 return ::GetTickCount(); #elif defined(macintosh) long res = ::TickCount(); return res = (float)res * (1000.0 / 60.0); #else struct timeval tval; static long refcount; gettimeofday(&tval, 0L); if(refcount == 0) refcount = tval.tv_sec; return (tval.tv_sec - refcount) * 1000 + tval.tv_usec / 1000; #endif } const STD map & UEventGetTargets(void) { return sAllTargets.Get(); }