diff options
Diffstat (limited to 'Src/Wasabi/api/wnd/wndclass')
76 files changed, 21173 insertions, 0 deletions
diff --git a/Src/Wasabi/api/wnd/wndclass/SelItemList.cpp b/Src/Wasabi/api/wnd/wndclass/SelItemList.cpp new file mode 100644 index 00000000..07f5edd9 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/SelItemList.cpp @@ -0,0 +1,72 @@ +#include <precomp.h> +#include "SelItemList.h" +#include "listwnd.h" + +#define SELITEMEXPAND 2040 + +SelItemList::SelItemList(ListWnd *parent) : +listwnd(parent), list(SELITEMEXPAND), num_selected(0) { } + +void SelItemList::setSelected(int pos, int selected, int cb) +{ + int s = list.getSize(); + if (pos >= s) + { + if (!selected) + { + // turning off something that's out of range is a no-brainer + return ; + } + s = ((s / SELITEMEXPAND) + 1) * SELITEMEXPAND; + list.setSize(s); + } + + char *l = list.getMemory(); + if (selected) + { + // quick and dirty way to prevent more than one item from + // ever being selected? + if (listwnd->getPreventMultipleSelection()) + { + listwnd->deselectAll(cb); + } + if (!isSelected(pos)) + { + l[pos] = 1; + num_selected++; + if (cb) listwnd->itemSelection(pos, TRUE); + listwnd->invalidateItem(pos); + } + } + else + { + if (isSelected(pos)) + { + l[pos] = 0; + num_selected--; + if (cb) listwnd->itemSelection(pos, FALSE); + listwnd->invalidateItem(pos); + } + } +} +int SelItemList::isSelected(int pos) +{ + if (pos >= list.getSize()) return 0; + return list.getMemory()[pos]; +} +int SelItemList::getNumSelected() { return num_selected; } + +void SelItemList::deleteByPos(int pos) +{ + ASSERT(pos >= 0); + int s = list.getSize(); + if (pos >= s) return ; + num_selected -= isSelected(pos); + char *m = list.getMemory() + pos; + MEMCPY(m, m + 1, s - (pos + 1)); +} +void SelItemList::deselectAll() +{ // for internal use, doesn't send callbacks + list.zeroMemory(); + num_selected = 0; +} diff --git a/Src/Wasabi/api/wnd/wndclass/SelItemList.h b/Src/Wasabi/api/wnd/wndclass/SelItemList.h new file mode 100644 index 00000000..f17a1511 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/SelItemList.h @@ -0,0 +1,31 @@ +#ifndef NULLSOFT_SELITEMLISTH +#define NULLSOFT_SELITEMLISTH + +class ListWnd; + +#define SELITEMEXPAND 2040 + +//note to whoever redid this as a bitlist +// a) this is pointlessly slow as a bitlist given the memory used +// b) perhaps you should investigate bitlist.h +class SelItemList +{ +public: + SelItemList(ListWnd *parent); + + void setSelected(int pos, int selected, int cb=1); + int isSelected(int pos); + int getNumSelected(); + + void deleteByPos(int pos); +protected: +friend ListWnd; + void deselectAll(); + +private: + ListWnd *listwnd; + MemBlock<char> list; + int num_selected; +}; + +#endif
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/abstractwndhold.cpp b/Src/Wasabi/api/wnd/wndclass/abstractwndhold.cpp new file mode 100644 index 00000000..95af35a8 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/abstractwndhold.cpp @@ -0,0 +1,287 @@ +#include "precomp.h" +#include <api/syscb/api_syscb.h> + +#include "abstractwndhold.h" +#include <api/service/svc_enum.h> +#include <api/wnd/wndclass/guiobjwnd.h> +#include <api/service/svcs/svc_wndcreate.h> +#include <api/script/scriptguid.h> +#include <api/script/objects/c_script/c_guiobject.h> +#include <api/script/objects/c_script/c_group.h> +#include <api/wnd/notifmsg.h> + + +#define CB_ABSTRACTLOAD 0x1125 + +AbstractWndHolder::AbstractWndHolder(const wchar_t *grpid, int autoresize) : ServiceWndHolder(NULL, NULL) +{ + scripts_enabled = 1; + groupid = grpid; + group_item = NULL; + guid = INVALID_GUID; + group = NULL; + cbreg = 0; + inselfresize = 0; + autoresizefromcontent = autoresize; + allow_deferred_content = 0; + need_deferred_load = 0; +} + +AbstractWndHolder::AbstractWndHolder(SkinItem *groupitem, int autoresize) : ServiceWndHolder(NULL, NULL) { + group_item = groupitem; + guid = INVALID_GUID; + group = NULL; + cbreg = 0; + inselfresize = 0; + autoresizefromcontent = autoresize; + allow_deferred_content = 0; + need_deferred_load = 0; +} + +AbstractWndHolder::AbstractWndHolder(GUID g, int autoresize) : ServiceWndHolder(NULL, NULL) { + guid = g; + group = NULL; + cbreg = 0; + group_item = NULL; + inselfresize = 0; + autoresizefromcontent = autoresize; + allow_deferred_content = 0; + need_deferred_load = 0; +} + +AbstractWndHolder::~AbstractWndHolder() { + if (group) { +#ifdef WASABI_COMPILE_SKIN + WASABI_API_SKIN->group_destroy(group); +#endif + group = NULL; + } + if (cbreg) WASABI_API_SYSCB->syscb_deregisterCallback(this); +} + +int AbstractWndHolder::onInit() { + ABSTRACTWNDHOLDER_PARENT::onInit(); + createChild(); + return 1; +} + +ifc_window *AbstractWndHolder::rootwndholder_getRootWnd() { + if (group != NULL) return group; + return ABSTRACTWNDHOLDER_PARENT::rootwndholder_getRootWnd(); +} + +void AbstractWndHolder::abstract_setContent(const wchar_t *group_id, int autoresize) +{ + setBothContent(group_id, INVALID_GUID, autoresize); +} + +void AbstractWndHolder::abstract_setContentBySkinItem(SkinItem *item, int autoresize) { + setContentSkinItem(item, autoresize); +} + +void AbstractWndHolder::abstract_setContent(GUID g, int autoresize) { + setBothContent(NULL, g, autoresize); +} + +void AbstractWndHolder::setBothContent(const wchar_t *group_id, GUID g, int autoresize) +{ + group_item = NULL; + if (autoresize != -1) + autoresizefromcontent = autoresize; + if (WCSCASEEQLSAFE(groupid, group_id) || (guid != INVALID_GUID && guid == g)) return; + GUID _g = nsGUID::fromCharW(group_id); + if (g == INVALID_GUID && _g != INVALID_GUID) { + groupid = NULL; + guid = _g; + } else { + groupid = group_id; + guid = g; + } + createChild(); +} + +void AbstractWndHolder::setContentSkinItem(SkinItem *item, int autoresize) +{ + if (autoresize != -1) + autoresizefromcontent = autoresize; + groupid.trunc(0); + guid = INVALID_GUID; + group_item = item; + createChild(); +} + +int AbstractWndHolder::onGroupChange(const wchar_t *grpid) +{ + if (WCSCASEEQLSAFE(groupid, grpid)) + { + createChild(); + notifyParent(ChildNotify::GROUPRELOAD); + notifyParent(ChildNotify::AUTOWHCHANGED); + } + return 1; +} + +void AbstractWndHolder::createChild() { + if (!isInited()) return; + destroyContent(); + if (allow_deferred_content && !isVisible()) + need_deferred_load = 1; + else + doLoadContent(); +} + +void AbstractWndHolder::destroyContent() +{ + if (group != NULL) { +#ifdef WASABI_COMPILE_SKIN + WASABI_API_SKIN->group_destroy(group); +#endif + if (cbreg) { + WASABI_API_SYSCB->syscb_deregisterCallback(this); + cbreg=0; + } + ABSTRACTWNDHOLDER_PARENT::setChild(NULL, NULL); + } + group = NULL; +} + +int AbstractWndHolder::onDeferredCallback(intptr_t p1, intptr_t p2) +{ + if (p1 == CB_ABSTRACTLOAD) { + doLoadContent(); + return 1; + } + return ABSTRACTWNDHOLDER_PARENT::onDeferredCallback(p1, p2); +} + +#include <bfc/util/profiler.h> + +void AbstractWndHolder::doLoadContent() { + int didsomething = 0; + need_deferred_load = 0; + if (group_item != NULL) { +#ifdef WASABI_COMPILE_SKIN + group = WASABI_API_SKIN->group_createBySkinItem(group_item, scripts_enabled); +#endif + if (group) { + if (!cbreg) { + WASABI_API_SYSCB->syscb_registerCallback(this); + cbreg=1; + } + group->setParent(this); + group->init(this); + abstract_onNewContent(); + didsomething = 1; + } + } else if (!groupid.isempty()) { +#ifdef WASABI_COMPILE_SKIN + group = WASABI_API_SKIN->group_create(groupid, scripts_enabled); +#endif + if (group) { + if (!cbreg) { + WASABI_API_SYSCB->syscb_registerCallback(this); + cbreg=1; + } + group->setParent(this); + group->init(this); + abstract_onNewContent(); + didsomething = 1; + } + } else if (guid != INVALID_GUID) { + svc_windowCreate *svc = WindowCreateByGuidEnum(guid).getNext(); + ABSTRACTWNDHOLDER_PARENT::setChild(svc->createWindowByGuid(guid, this), svc); + abstract_onNewContent(); + didsomething = 1; + } + if (didsomething && isPostOnInit()) + { + onResize(); + } +} + +void AbstractWndHolder::abstract_onNewContent() +{ + if (isPostOnInit()) { + ifc_window *w = getDesktopParent(); + if (w != NULL) { + if (w->enumMinMaxEnforcer(0)) { + w->signalMinMaxEnforcerChanged(); + } + } + } +} + +GuiObject *AbstractWndHolder::abstract_findObject(const wchar_t *object_id) +{ + ifc_window *w = rootwndholder_getRootWnd(); + if (w == NULL) return NULL; + GuiObject *o = static_cast<GuiObject *>(w->getInterface(guiObjectGuid)); + return o->guiobject_findObject(object_id); +} + +ScriptObject *AbstractWndHolder::abstract_findScriptObject(const wchar_t *object_id) +{ + ifc_window *w = rootwndholder_getRootWnd(); + if (w == NULL) return NULL; + GuiObject *o = static_cast<GuiObject *>(w->getInterface(guiObjectGuid)); + GuiObject *fo = o->guiobject_findObject(object_id); + if (fo != NULL) return fo->guiobject_getScriptObject(); + return NULL; +} + + +GuiObject *AbstractWndHolder::abstract_getContent() { + ifc_window *w = rootwndholder_getRootWnd(); + if (w == NULL) return NULL; + return static_cast<GuiObject *>(w->getInterface(guiObjectGuid)); +} + +ScriptObject *AbstractWndHolder::abstract_getContentScriptObject() { + ifc_window *w = rootwndholder_getRootWnd(); + if (w == NULL) return NULL; + return static_cast<ScriptObject *>(w->getInterface(scriptObjectGuid)); +} + +int AbstractWndHolder::onResize() { + int rt = ABSTRACTWNDHOLDER_PARENT::onResize(); + if (group != NULL && abstract_wantAutoResizeFromContent()) { + if (inselfresize) return rt; + inselfresize=1; + int w = group->getPreferences(SUGGESTED_W); + int h = group->getPreferences(SUGGESTED_H); + resize(NOCHANGE, NOCHANGE, w == AUTOWH ? NOCHANGE : w, h == AUTOWH ? NOCHANGE : h); + inselfresize = 0; + } + return rt; +} + +void AbstractWndHolder::onSetVisible( int show ) +{ + ABSTRACTWNDHOLDER_PARENT::onSetVisible( show ); + if ( allow_deferred_content ) + { + if ( show & need_deferred_load ) + { + doLoadContent(); + } + if ( !show && !need_deferred_load ) + { + destroyContent(); + need_deferred_load = 1; + } + } +} + +void AbstractWndHolder::abstract_setScriptsEnabled(int en) +{ + if (scripts_enabled == en) return; + scripts_enabled = en; + if (group != NULL) { + destroyContent(); + doLoadContent(); + } +} + +int AbstractWndHolder::abstact_getScriptsEnabled() { + return scripts_enabled; +} diff --git a/Src/Wasabi/api/wnd/wndclass/abstractwndhold.h b/Src/Wasabi/api/wnd/wndclass/abstractwndhold.h new file mode 100644 index 00000000..c81ad683 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/abstractwndhold.h @@ -0,0 +1,267 @@ +#ifndef __ABSTRACTWNDHOLD_H +#define __ABSTRACTWNDHOLD_H + +#include <api/wnd/wndclass/svcwndhold.h> +#include <api/syscb/callbacks/wndcb.h> + +class GuiObject; +class ScriptObject; +class SkinItem; + +#define ABSTRACTWNDHOLDER_PARENT ServiceWndHolder + + +/** + The AbstractWndHolder enables you to display deferred content. This means + the content does not actually need to exist at creation. It will be automatically + loaded when it is found. + + This class is meant to be derived from. You shouldn't create an AbstractWndHolder + directly to implement this kind of functionality. Please use GuiObjectWnd along + with the setContent() method. + + @short An abstracted window holder with deferred content loading. + @author Nullsoft + @ver 1.0 + @see GuiObjectWnd +*/ +class AbstractWndHolder : public ABSTRACTWNDHOLDER_PARENT, public WndCallbackI { + + protected: + + // don't create me directly, create a GuiObjectWnd and setContent("group.id") on it + // these are for inheriting and have a deferred content (so if you _were_ using that, + // your findObjects would fail right after init and before abstract_onNewContent) + + /** + Sets the group id of the content and the auto resize flag. + + Setting the auto resize flag to 1 will enable the automatic resize of + the window according to the size of the content attempting to be + displayed. + + @param groupid The content group. + @param _autoresizefromcontent 0, disable auto resize; 1, enable auto resize; + */ + AbstractWndHolder(const wchar_t *groupid=NULL, int _autoresizefromcontent=0); + AbstractWndHolder(SkinItem *groupitem, int _autoresizefromcontent=0); + + /** + Finds the group with the matching GUID and uses that group as the content. + This insures you get the exact group and not a replacement. + + Setting the auto resize flag to 1 will enable the automatic resize of + the window according to the size of the content attempting to be + displayed. + + @param _guid The GUID the content group. + @param _autoresizefromcontent 0, disable auto resize; 1, enable auto resize; + */ + AbstractWndHolder(GUID _guid, int _autoresizefromcontent=0); + + public: + + + /** + Destroys the instance of the group used for content automatically. + */ + virtual ~AbstractWndHolder(); + + + /** + Sets the content group using the group id. + + @param groupid The content group id. + @param _autoresizefromcontent -1, no change; 0, disable auto resize; 1, enable auto resize; + */ + virtual void abstract_setContent(const wchar_t *groupid, int _autoresizefromcontent=-1); // -1 = no change, 0 = false, 1 = true + + /** + Sets the content group using the group's GUID. + This insures you get the exact group and not a replacement. + + @param g The content group's GUID. + @param _autoresizefromcontent -1, no change; 0, disable auto resize; 1, enable auto resize; + */ + virtual void abstract_setContent(GUID g, int _autoresizefromcontent=-1); // -1 = no change, 0 = false, 1 = true + + virtual void abstract_setContentBySkinItem(SkinItem *groupitem, int _autoresizefromcontent=-1); // -1 = no change, 0 = false, 1 = true + + /** + This event is triggered when the content is loaded or the content changes. + Override it to implement your own handling of this event. + */ + virtual void abstract_onNewContent(); + + /** + This event is triggered when the content group is changed. + Override it to implement your own handling of this event. + + @see abstract_setContant() + @ret 1, if you handle the event; + @param grpid The new group that was set. + */ + virtual int onGroupChange(const wchar_t *grpid); + + /** + This event is triggered when the visibility of the window changes. + Override it to implement your own handling of this event. + + @see onGroupChange() + @param show 0, hide window; 1, show window; + */ + virtual void onSetVisible(int show); + + + /** + This event is triggered when the window is being initialized. + Override it to implement your own handling of this event. + + @ret 1, if you handle the event; + */ + virtual int onInit(); + + /** + This event is triggered when the window is resized. + Override it to implement your own handling of this event. + + @ret 1, if you handle the event; + */ + virtual int onResize(); + + + /** + Get the api_window. + + @ret + */ + ifc_window *rootwndholder_getRootWnd(); + + /** + Find an object in the content group. This is done via + the object's text id. + + @see abstract_findScriptObject() + @ret !NULL, The requested object pointer; NULL, Failed to find object. + @param The id of the object to find. + */ + virtual GuiObject *abstract_findObject(const wchar_t *object_id); + + /** + Find a script object in the content group. This is done via the + script object's text id. + + @see abstract_findObject() + @ret !NULL, The requested script object pointer; NULL, Failed to find the object. + @param The id of the script object to find. + // TODO: benski> currently unused. cut? + */ + virtual ScriptObject *abstract_findScriptObject(const wchar_t *object_id); + + /** + Get the content group. + + @see abstract_getContentScriptObject() + @ret A pointer to the content group GuiObject. + */ + virtual GuiObject *abstract_getContent(); + + /** + Get the content script object. + + @see abstract_getContent() + @ret A pointer to the content ScriptObject. + */ + virtual ScriptObject *abstract_getContentScriptObject(); + + /** + Get the ifc_window that is holding the content group. + + @see rootwndholder_getRootWnd() + @ret A pointer to the ifc_window holding the content group. + */ + virtual ifc_window *abstract_getContentRootWnd() { return group; } + + /** + Read the auto-resize from content flag. + + @see abstract_setAutoResizeFromContent() + @ret 0, disable auto resize; 1, enable auto resize; + @param + */ + virtual int abstract_wantAutoResizeFromContent() { return autoresizefromcontent; } + + /** + Set the auto-resize from content flag. + + @see abstract_wantAutoResizeFromContent() + @param i 0, disable auto resize; 1, enable auto resize; + */ + virtual void abstract_setAutoResizeFromContent(int i) { autoresizefromcontent = i; } + + /** + Set the allow deferred content flag. Allowing deferred content enables content to be + loaded after the window is shown. + + @param allow 0, Do not allow; 1, Allow + */ + virtual void abstract_setAllowDeferredContent(int allow) { allow_deferred_content = allow; } + + + /** + The deferred callback. + + @ret 1, If you handle the event. + @param p1 + @param p2 + */ + virtual int onDeferredCallback(intptr_t p1, intptr_t p2); + + virtual void setContentSkinItem(SkinItem *groupitem, int autoresize=-1); + + virtual void abstract_setScriptsEnabled(int en); + virtual int abstact_getScriptsEnabled(); + + private: + + + /** + Creates the child content window when required. + */ + void createChild(); + + /** + Set Both Content. + + @param guid + @param g + @param _autoresizefromcontent + */ + void setBothContent(const wchar_t *guid, GUID g, int _autoresizefromcontent); + + /** + Loads the content from the previously specified group. + + @see abstract_setContent() + */ + void doLoadContent(); + + /** + Destroys the instantiated copy of the content group. + */ + void destroyContent(); + + StringW groupid; + GUID guid; + + ifc_window *group; + int cbreg; + int inselfresize; + int autoresizefromcontent; + int allow_deferred_content; + int need_deferred_load; + SkinItem *group_item; + int scripts_enabled = 0; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/appbarwnd.cpp b/Src/Wasabi/api/wnd/wndclass/appbarwnd.cpp new file mode 100644 index 00000000..11629fc1 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/appbarwnd.cpp @@ -0,0 +1,1113 @@ +#include "precomp.h" +#include "appbarwnd.h" +#include <tataki/region/region.h> +#include <api/wnd/resizable.h> +#include <api/wndmgr/layout.h> +#include <api/config/items/cfgitem.h> +#include <api/config/items/attrint.h> +#include "../../../../Plugins/General/gen_ff/wa2cfgitems.h" + +#define CB_CHECK 0x101 +#define DOCK_DISTANCE_X 5 +#define DOCK_DISTANCE_Y 5 + +#ifndef WIN32 +#error port me or remove me from the inheritance on this platform ! +#endif + +#include <windows.h> +#include <windowsx.h> +#include <shlobj.h> +#include "../../../../Plugins/General/gen_ff/main.h" +#include "appbarwnd.h" + +extern _int cfg_options_appbardockingdistance; + +// ----------------------------------------------------------------------- +AppBarWnd::AppBarWnd() { + m_registered = 0; + m_side = APPBAR_NOTDOCKED; + m_enabled = 0; + m_cur_side = APPBAR_NOTDOCKED; + m_cur_autohide = 0; + m_cur_hiding = 0; + m_oldZOrder = NULL; + m_destroying = FALSE; + m_norestore = 0; + m_sliding = 0; + m_autounhide_timer_set = 0; + m_autohide_timer_set = 0; + m_suspended = 0; + m_fs = 0; + m_wahidden = 0; +} + +// ----------------------------------------------------------------------- +AppBarWnd::~AppBarWnd() { + m_destroying = TRUE; + if (m_cur_side != APPBAR_NOTDOCKED) unDock(); + unregisterWinAppBar(); +} + +// ----------------------------------------------------------------------- +int AppBarWnd::registerWinAppBar() +{ + if (m_registered) + unregisterWinAppBar(); + + APPBARDATA abd; + + abd.cbSize = sizeof(APPBARDATA); + abd.hWnd = getOsWindowHandle(); + abd.uCallbackMessage = APPBAR_CALLBACK; + + m_registered = (int)SHAppBarMessage(ABM_NEW, &abd); + return m_registered; +} + +// ----------------------------------------------------------------------- +void AppBarWnd::unregisterWinAppBar() { + if (m_registered) { + APPBARDATA abd; + + abd.cbSize = sizeof(APPBARDATA); + abd.hWnd = getOsWindowHandle(); + + SHAppBarMessage(ABM_REMOVE, &abd); + m_registered = 0; + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::appbar_dock(int side) { + m_side = side; + updateDocking(); +} + +// ----------------------------------------------------------------------- +int AppBarWnd::appbar_isDocked() { + return m_side != APPBAR_NOTDOCKED; +} + +// ----------------------------------------------------------------------- +int AppBarWnd::appbar_getSide() { + return m_side; +} + +// ----------------------------------------------------------------------- +void AppBarWnd::appbar_setEnabledSides(int mask) { + m_enabled = mask; +} + +// ----------------------------------------------------------------------- +int AppBarWnd::appbar_getEnabledSides() { + return m_enabled; +} + +// ----------------------------------------------------------------------- +int AppBarWnd::appbar_isSideEnabled(int side) { + if (side == APPBAR_LEFT && !(m_enabled & APPBAR_LEFT_ENABLED)) return 0; + if (side == APPBAR_TOP && !(m_enabled & APPBAR_TOP_ENABLED)) return 0; + if (side == APPBAR_RIGHT && !(m_enabled & APPBAR_RIGHT_ENABLED)) return 0; + if (side == APPBAR_BOTTOM && !(m_enabled & APPBAR_BOTTOM_ENABLED)) return 0; + return 1; +} + +// ----------------------------------------------------------------------- +int AppBarWnd::appbar_isSideAutoHideSafe(int side) { + OSWINDOWHANDLE cur = getCurAutoHide(side); + + if (cur == NULL || cur == getOsWindowHandle()) { + RECT primary = {0}; + Wasabi::Std::getViewport(&primary, hwnd, 1); + + DebugStringW( L"primary screen coords = %d,%d -> %d,%d (%dx%d)\n", primary.left, primary.top, primary.right, primary.bottom, primary.right - primary.left, primary.bottom - primary.top ); + int monitor = 0; + //int g = 0; + while (1) { + RECT r; + int ret = Wasabi::Std::enumViewports(monitor++, &r, 1); + if (ret == 0) break; + + if (Wasabi::Std::rectEqual(&primary, &r)) continue; + + DebugStringW(L"secondary screen = %d,%d -> %d,%d (%dx%d)\n", r.left, r.top, r.right, r.bottom, r.right-r.left, r.bottom-r.top); + if (r.right <= primary.left && side == APPBAR_LEFT) return 0; + if (r.bottom <= primary.top && side == APPBAR_TOP) return 0; + if (r.left >= primary.right && side == APPBAR_RIGHT) return 0; + if (r.top >= primary.bottom && side == APPBAR_BOTTOM) return 0; + } + } + else + return 0; + + return 1; +} + +// ----------------------------------------------------------------------- +OSWINDOWHANDLE AppBarWnd::getCurAutoHide(int side) { + APPBARDATA abd; + abd.cbSize = sizeof(APPBARDATA); + abd.hWnd = getOsWindowHandle(); + abd.uEdge = side; + return (OSWINDOWHANDLE)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd); +} + +// ----------------------------------------------------------------------- +int AppBarWnd::appbar_testDock(int x, int y, RECT *dockrect) { + POINT ptCursor = {x, y}; + LONG cxScreen, cyScreen; + int dx=999999, dy=999999; + int horiz=-1, vert=-1; + RECT viewRect = {0}; + + Wasabi::Std::getViewport(&viewRect, hwnd, 1); + + // Find out which edge of the screen we're closest to + cxScreen = viewRect.right; + cyScreen = viewRect.bottom; + + if (x < viewRect.left || x > cxScreen || y < viewRect.top || y > cyScreen) return APPBAR_NOTDOCKED; + + if (ptCursor.x < (cxScreen / 2)) { + if (m_enabled & APPBAR_LEFT_ENABLED) { + dx = ptCursor.x; + horiz = APPBAR_LEFT; + } + } + else { + if (m_enabled & APPBAR_RIGHT_ENABLED) { + dx = cxScreen - ptCursor.x; + horiz = APPBAR_RIGHT; + } + } + + if (ptCursor.y < (cyScreen / 2)) { + if (m_enabled & APPBAR_TOP_ENABLED) { + dy = ptCursor.y; + vert = APPBAR_TOP; + } + } + else { + if (m_enabled & APPBAR_BOTTOM_ENABLED) { + dy = cyScreen - ptCursor.y; + vert = APPBAR_BOTTOM; + } + } + + int ret = -1; + #ifdef GEN_FF + int dockdist = cfg_options_appbardockingdistance; + #else + // TODO: do a config lookup, but make it not so slow + /* + const GUID options_guid = + { 0x280876cf, 0x48c0, 0x40bc, { 0x8e, 0x86, 0x73, 0xce, 0x6b, 0xb4, 0x62, 0xe5 } }; + int dockdist = _intVal(WASABI_API_CONFIG->config_getCfgItemByGuid(options_guid), L"Appbars Docking Distance", 5);*/ + int dockdist = 5; + #endif + if ((cxScreen * dy) > (cyScreen * dx)) + if (dx <= dockdist) + ret = horiz; + if (dy <= dockdist) + ret = vert; + + if (dockrect && ret != -1) { + getDockRect(ret, dockrect); + } + + return ret; +} + +// ----------------------------------------------------------------------- +void AppBarWnd::getDockRect(int side, RECT *rect) { + LONG cxScreen, cyScreen; + RECT viewRect = {0}; + Wasabi::Std::getViewport(&viewRect, hwnd, 1); + + cxScreen = viewRect.right; + cyScreen = viewRect.bottom; + + if (isMaximized()) { + getRestoredRect(rect); + if (renderRatioActive()) multRatio(rect); + } + else getWindowRect(rect); + + Layout *l = (Layout *)getInterface(layoutGuid); + if (l) { + RECT adj; + l->getSnapAdjust(&adj); + if (renderRatioActive()) { + multRatio((int *)&adj.left, (int *)&adj.top); + multRatio((int *)&adj.right, (int *)&adj.bottom); + } + int h = rect->bottom - rect->top; + int w = rect->right - rect->left; + h -= adj.top + adj.bottom; + w -= adj.left + adj.right; + rect->left += adj.left; + rect->top += adj.top; + rect->bottom = rect->top + h; + rect->right = rect->left + w; + } + + switch (side) { + case APPBAR_TOP: + case APPBAR_LEFT: + OffsetRect(rect, -rect->left, -rect->top); + break; + case APPBAR_BOTTOM: + case APPBAR_RIGHT: + OffsetRect(rect, cxScreen-rect->right, cyScreen-rect->bottom); + break; + } + + switch (side) { + case APPBAR_TOP: + case APPBAR_BOTTOM: + rect->left = viewRect.left; + rect->right = cxScreen; + break; + case APPBAR_LEFT: + case APPBAR_RIGHT: + rect->top = viewRect.top; + rect->bottom = cyScreen; + break; + } + + OSWINDOWHANDLE cur = getCurAutoHide(side); + int safeah = appbar_isSideAutoHideSafe(side); + + if (!safeah || !(appbar_wantAutoHide() && (!cur || cur == getOsWindowHandle()))) { + straightenRect(side, rect); + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::updateDocking() { + if (!isVisible()) { + m_suspended = 1; + return; + } + updateSide(); + appbar_updateAutoHide(); + appbar_updateAlwaysOnTop(); + updateTimers(); +} + +// ----------------------------------------------------------------------- +void AppBarWnd::updateTimers() { + if (m_cur_autohide) { + if (m_cur_hiding) { + resetAutoHideTimer(); + setAutoUnHideTimer(); + } + else { + resetAutoUnHideTimer(); + setAutoHideTimer(); + } + } +} + +// ----------------------------------------------------------------------- +int AppBarWnd::appbar_updateAlwaysOnTop() { + if (m_side == APPBAR_NOTDOCKED) return 0; + SetWindowPos(getOsWindowHandle(), appbar_wantAlwaysOnTop() ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + return 1; +} + +// ----------------------------------------------------------------------- +int AppBarWnd::appbar_updateAutoHide() { + int autohide = appbar_wantAutoHide(); + if (m_cur_autohide == autohide) return 0; + + if (autohide && !appbar_isSideAutoHideSafe(m_cur_side)) autohide = 0; + + if (m_cur_autohide == autohide) return 0; + + if (autohide) { + // cur_autohide is off, turn it on + m_cur_hiding = 0; + setAutoHideTimer(); + } + else { + // cur_autohide is on, turn it off + if (m_cur_hiding) resetAutoUnHideTimer(); + else resetAutoHideTimer(); + } + + m_cur_autohide = autohide; + dock(m_cur_side); + + return 1; +} + +// ----------------------------------------------------------------------- +void AppBarWnd::onAfterReinit() { + APPBARWND_PARENT::onAfterReinit(); + m_autohide_timer_set = 0; + m_autounhide_timer_set = 0; + updateTimers(); +} + +// ----------------------------------------------------------------------- +void AppBarWnd::setAutoHideTimer(){ + if (!m_autohide_timer_set) { + SetTimer(getOsWindowHandle(), IDT_AUTOHIDE, cfg_uioptions_appbarshidetime, NULL); + m_autohide_timer_set = 1; + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::setAutoUnHideTimer(){ + if (!m_autounhide_timer_set) { + SetTimer(getOsWindowHandle(), IDT_AUTOUNHIDE, cfg_uioptions_appbarsshowtime, NULL); + m_autounhide_timer_set = 1; + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::resetAutoHideTimer(){ + if (m_autohide_timer_set) { + KillTimer(getOsWindowHandle(), IDT_AUTOHIDE); + m_autohide_timer_set = 0; + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::resetAutoUnHideTimer() { + if (m_autounhide_timer_set) { + KillTimer(getOsWindowHandle(), IDT_AUTOUNHIDE); + m_autounhide_timer_set = 0; + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::updateSide() { + if (m_cur_side == m_side) return; + if (m_side != m_cur_side && m_cur_side != APPBAR_NOTDOCKED && m_side != APPBAR_NOTDOCKED && m_cur_autohide) { + resetAutoHideSide(m_cur_side); + } + if (m_side == APPBAR_NOTDOCKED) unDock(); + else dock(m_side); +} + +// ----------------------------------------------------------------------- +void AppBarWnd::resetAutoHideSide(int side) { + HWND cur = getCurAutoHide(side); + if (cur == getOsWindowHandle()) { + APPBARDATA abd; + abd.cbSize = sizeof(APPBARDATA); + abd.hWnd = cur; + abd.uEdge = side; + abd.lParam = FALSE; + SHAppBarMessage(ABM_SETAUTOHIDEBAR, &abd); + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::setAutoHideSide(int side) { + APPBARDATA abd; + abd.cbSize = sizeof(APPBARDATA); + abd.hWnd = getOsWindowHandle(); + abd.uEdge = side; + abd.lParam = TRUE; + SHAppBarMessage(ABM_SETAUTOHIDEBAR, &abd); +} + +// ----------------------------------------------------------------------- +void AppBarWnd::dock(int side) { + unOwn(); + + if (!registerWinAppBar()) { + reOwn(); + m_side = APPBAR_NOTDOCKED; + m_cur_side = APPBAR_NOTDOCKED; + m_cur_autohide = 0; + } + + maximize(0); + + RECT rect; + getDockRect(side, &rect); + + { + RECT adj = rect; + if (ABS(getRenderRatio() - 1.0) > 0.01f) { + int _w = adj.right-adj.left; + int _h = adj.bottom-adj.top; + double rr = getRenderRatio(); + _w = (int)((double)(_w) / rr + 0.5); + _h = (int)((double)(_h) / rr + 0.5); + adj.right = adj.left + _w; + adj.bottom = adj.top + _h; + } + snapAdjust(&adj, 1); + resizeToRect(&adj); + } + + if (!appbar_wantAutoHide() || !appbar_isSideAutoHideSafe(side)) { + notifyWinAppBarPosition(side, rect); + } + else { + getEdge(side, &rect); + notifyWinAppBarPosition(side, rect); + setAutoHideSide(side); + m_cur_hiding = 0; + } + + if (!m_suspended) appbar_onDock(side); + + #ifdef WASABI_APPBAR_ONDOCKCHANGED + WASABI_APPBAR_ONDOCKCHANGED(this) + #endif +} + +// ----------------------------------------------------------------------- +void AppBarWnd::unDock() { + if (m_cur_side != APPBAR_NOTDOCKED) { + + resetAutoHideSide(m_cur_side); + unregisterWinAppBar(); + + if (!m_destroying) { + reOwn(); + if (!m_norestore) restore(); + #ifdef WASABI_APPBAR_ONDOCKCHANGED + WASABI_APPBAR_ONDOCKCHANGED(this) + #endif + } + + m_cur_side = APPBAR_NOTDOCKED; + + if (!m_suspended) appbar_onUnDock(); + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::notifyWinAppBarPosition(int side, RECT rect) { + APPBARDATA abd; + + abd.cbSize = sizeof(APPBARDATA); + abd.hWnd = getOsWindowHandle(); + abd.rc = rect; + abd.uEdge = side; + + SHAppBarMessage(ABM_SETPOS, &abd); + m_cur_side = side; +} + +// ----------------------------------------------------------------------- +int AppBarWnd::appbar_isHiding() { + return m_cur_hiding; +} + +// ----------------------------------------------------------------------- +int AppBarWnd::appbar_isAutoHiding() { + return m_cur_autohide; +} + +// ----------------------------------------------------------------------- +void AppBarWnd::appbar_posChanged() { + if (m_side == APPBAR_NOTDOCKED) return; + RECT wr; + getWindowRect(&wr); + int w = wr.right-wr.left; + int h = wr.bottom-wr.top; + + if (m_cur_autohide && m_cur_side != APPBAR_NOTDOCKED && !appbar_isSideAutoHideSafe(m_cur_side)) + m_cur_autohide = 0; + + RECT rc; + getDockRect(m_cur_side, &rc); + + if (!m_cur_autohide) { + { + RECT adj = rc; + if (ABS(getRenderRatio() - 1.0) > 0.01f) { + int _w = adj.right-adj.left; + int _h = adj.bottom-adj.top; + double rr = getRenderRatio(); + _w = (int)((double)(_w) / rr + 0.5); + _h = (int)((double)(_h) / rr + 0.5); + adj.right = adj.left + _w; + adj.bottom = adj.top + _h; + } + snapAdjust(&adj, 1); + resizeToRect(&adj); + } + notifyWinAppBarPosition(m_cur_side, rc); + } + else { + int aaw = appbar_getAutoHideWidthHeight(); + RECT er; + getEdge(m_cur_side, &er); + notifyWinAppBarPosition(m_cur_side, er); + RECT adj = {0,0,0,0}; + Layout *l = (Layout *)getInterface(layoutGuid); + if (l) l->getSnapAdjust(&adj); + if (renderRatioActive()) multRatio(&adj); + if (m_cur_hiding) { + switch (m_cur_side) { + case APPBAR_TOP: + rc.bottom = er.top + aaw + adj.bottom; + rc.top = rc.bottom - h; + break; + case APPBAR_BOTTOM: + rc.top = er.bottom - aaw - adj.top; + rc.bottom = rc.top + h; + break; + case APPBAR_LEFT: + rc.right = er.left + aaw + adj.right; + rc.left = rc.right - w; + break; + case APPBAR_RIGHT: + rc.left = er.right - aaw - adj.left; + rc.right = rc.left + w; + break; + } + } + if (ABS(getRenderRatio() - 1.0) > 0.01f) { + int _w = rc.right-rc.left; + int _h = rc.bottom-rc.top; + double rr = getRenderRatio(); + _w = (int)((double)(_w) / rr + 0.5); + _h = (int)((double)(_h) / rr + 0.5); + rc.right = rc.left + _w; + rc.bottom = rc.top + _h; + } + resizeToRect(&rc); + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::getEdge(int side, RECT *rc) { + ASSERT(rc != NULL); + Wasabi::Std::getViewport(rc, hwnd, 1); + switch (side) { + case APPBAR_TOP: + rc->bottom = rc->top; break; + case APPBAR_BOTTOM: + rc->top = rc->bottom; break; + case APPBAR_LEFT: + rc->right = rc->left; break; + case APPBAR_RIGHT: + rc->left = rc->right; break; + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::appBarCallback(UINT uMsg, WPARAM wParam, LPARAM lParam) { + APPBARDATA abd = {0}; + + if (m_registered) { + abd.cbSize = sizeof(abd); + abd.hWnd = getOsWindowHandle(); + + switch (wParam) + { + // the taskbar's autohide or always-on-top state has changed. + case ABN_STATECHANGE: + DebugString("AppBarCallback: ABN_STATECHANGE\n"); + break; + + // a full screen application is opening or closing. we must drop + // to the bottom of the Z-Order and restore it later. + case ABN_FULLSCREENAPP: + DebugString("AppBarCallback: ABN_FULLSCREENAPP\n"); + if (lParam && !m_fs) { + m_fs=1; + m_oldZOrder = GetWindow(getOsWindowHandle(), GW_HWNDPREV); + SetWindowPos(getOsWindowHandle(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + } + else if (!lParam && m_fs) { + m_fs = 0; + SetWindowPos(getOsWindowHandle(), appbar_wantAlwaysOnTop() ? HWND_TOPMOST : m_oldZOrder, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + m_oldZOrder = NULL; + } + break; + + // something changed that may have modified the possible appbar's positions + case ABN_POSCHANGED: + DebugString("AppBarCallback: ABN_POSCHANGED\n"); + appbar_posChanged(); + break; + + case ABN_WINDOWARRANGE: + if (lParam && !m_wahidden) { + m_wahidden = 1; + ShowWindow(getOsWindowHandle(), SW_HIDE); + } + else if (!lParam && m_wahidden) { + m_wahidden = 0; + ShowWindow(getOsWindowHandle(), SW_NORMAL); + } + break; + } + } +} + +// ----------------------------------------------------------------------- +LRESULT AppBarWnd::wndProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + if ( m_registered ) + { + switch ( msg ) + { + case WM_MOVE: + { + //DebugString("WM_MOVE\n"); + return 0; + } + case WM_WINDOWPOSCHANGED: + { + //DebugString("WM_WINDOWPOSCHANGED\n"); + //LPWINDOWPOS lpwpos = (LPWINDOWPOS)lparam; + APPBARDATA abd = { 0 }; + abd.cbSize = sizeof( APPBARDATA ); + abd.hWnd = getOsWindowHandle(); + + SHAppBarMessage( ABM_WINDOWPOSCHANGED, &abd ); + } + case APPBAR_CALLBACK: + { + if ( !m_destroying ) appBarCallback( msg, wparam, lparam ); + return 0; + } + case WM_DISPLAYCHANGE: + { + DebugString( "WM_DISPLAYCHANGE\n" ); + appbar_posChanged(); + } + case WM_TIMER: + { // // not using multiplexed timer for independent speed + switch ( wparam ) + { + case IDT_AUTOHIDE: + onAutoHideTimer(); + break; + case IDT_AUTOUNHIDE: + onAutoUnHideTimer(); + break; + } + } + case WM_COMMAND: + { + // forward onto the main Winamp window and let it do it + if ( HIWORD( wparam ) == THBN_CLICKED ) + { + SendMessageW( plugin.hwndParent, msg, wparam, lparam ); + } + } + } + } + + return APPBARWND_PARENT::wndProc( hwnd, msg, wparam, lparam ); +} + +// ----------------------------------------------------------------------- +void AppBarWnd::onAutoHideTimer() { + HWND me = getOsWindowHandle(); + POINT pt; + RECT rc; + HWND hact; + if (m_cur_autohide) { + if (!m_cur_hiding) { + GetCursorPos(&pt); + GetWindowRect(hwnd, &rc); + snapAdjust(&rc, -1); + hact = GetForegroundWindow(); + + if ((!PtInRect(&rc, pt) || screenCorner(&pt)) && (hact != me) && /*(hact!= NULL) && */(GetWindowOwner(hact) != me)) { + resetAutoHideTimer(); + autoHide(); + setAutoUnHideTimer(); + } + } + else { + resetAutoHideTimer(); + setAutoUnHideTimer(); + } + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::onAutoUnHideTimer() { + RECT rc; + POINT pt; + HWND me = getOsWindowHandle(); + + GetWindowRect(me, &rc); + snapAdjust(&rc, -1); + if (m_cur_autohide) { + if (m_cur_hiding) { + GetCursorPos(&pt); + if (PtInRect(&rc, pt) && !screenCorner(&pt)) { + resetAutoUnHideTimer(); + autoUnHide(); + setAutoHideTimer(); + } + } + else { + resetAutoUnHideTimer(); + setAutoHideTimer(); + } + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::autoHide() { + if (m_cur_autohide) { + if (!m_cur_hiding) { + RECT rc; + + getWindowRect(&rc); + + int h = rc.bottom-rc.top; + int w = rc.right-rc.left; + + int aaw = appbar_getAutoHideWidthHeight(); + + RECT adj={0,0,0,0}; + Layout *l = static_cast<Layout*>(getInterface(layoutGuid)); + l->getSnapAdjust(&adj); + if (renderRatioActive()) multRatio(&adj); + + RECT viewRect = {0}; + Wasabi::Std::getViewport(&viewRect, hwnd, 1); + + switch (m_side) { + case APPBAR_TOP: + rc.top = -(h - aaw + adj.top - adj.bottom); + break; + case APPBAR_BOTTOM: + rc.top = (viewRect.bottom - viewRect.top) - aaw - adj.top; + break; + case APPBAR_LEFT: + rc.left = -(w - aaw + adj.left - adj.right); + break; + case APPBAR_RIGHT: + rc.left = viewRect.right - aaw - adj.left; + break; + } + + switch (m_side) { + case APPBAR_TOP: + case APPBAR_BOTTOM: + rc.bottom = rc.top + h; + break; + case APPBAR_LEFT: + case APPBAR_RIGHT: + rc.right = rc.left + w; + break; + } + + slideWindow(&rc); + m_cur_hiding = 1; + } + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::autoUnHide() { + if (m_cur_autohide) { + if (m_cur_hiding) { + m_cur_hiding = 0; + + RECT rc; + getWindowRect(&rc); + + int h = rc.bottom-rc.top; + int w = rc.right-rc.left; + + int aaw = appbar_getAutoHideWidthHeight(); + + RECT adj={0,0,0,0}; + Layout *l = static_cast<Layout*>(getInterface(layoutGuid)); + l->getSnapAdjust(&adj); + if (renderRatioActive()) multRatio(&adj); + + switch (m_side) { + case APPBAR_TOP: + rc.top += (h - aaw) - (adj.top + adj.bottom); + rc.bottom += (h - aaw) - (adj.top + adj.bottom); + break; + case APPBAR_BOTTOM: + rc.top -= (h - aaw) - (adj.top + adj.bottom); + rc.bottom -= (h - aaw) - (adj.top + adj.bottom); + break; + case APPBAR_LEFT: + rc.right += (w - aaw) - (adj.left + adj.right); + rc.left += (w - aaw) - (adj.left + adj.right); + break; + case APPBAR_RIGHT: + rc.left -= (w - aaw) - (adj.left + adj.right); + rc.right -= (w - aaw) - (adj.left + adj.right); + break; + } + + slideWindow(&rc); + } + } +} + +// ----------------------------------------------------------------------- + +const int g_dtSlideHide = 400; +const int g_dtSlideShow = 200; + +// ----------------------------------------------------------------------- +void AppBarWnd::slideWindow(RECT *prc) { + if (m_cur_autohide) { + m_sliding = 1; + RECT rcOld; + RECT rcNew; + int x, y, dx, dy, dt, t, t0; + BOOL fShow; + HANDLE hThreadMe; + int priority; + + HWND hwnd = getOsWindowHandle(); + + rcNew = *prc; + + /*DebugString("rcNew : left=%d, top=%d, " + "right=%d, bottom=%d\n", rcNew.left, + rcNew.top, rcNew.right, rcNew.bottom);*/ + + if ((g_dtSlideShow > 0) && (g_dtSlideHide > 0)) { + GetWindowRect(hwnd, &rcOld); + + fShow = TRUE;/*(rcNew.bottom - rcNew.top) > (rcOld.bottom - rcOld.top) || + (rcNew.right - rcNew.left) > (rcOld.right - rcOld.left);*/ + + dx = (rcNew.left - rcOld.left); + dy = (rcNew.top - rcOld.top); + + if (fShow) { + rcOld = rcNew; + OffsetRect(&rcOld, -dx, -dy); + //DebugString("appbar_slideWindow %d %d\n", rcOld.left, rcOld.top); + move(rcOld.left, rcOld.top); + dt = g_dtSlideShow; + } + else { + dt = g_dtSlideHide; + } + + hThreadMe = GetCurrentThread(); + priority = GetThreadPriority(hThreadMe); + SetThreadPriority(hThreadMe, THREAD_PRIORITY_HIGHEST); + + t0 = GetTickCount(); + while ((t = GetTickCount()) < t0 + dt) { + x = rcOld.left + dx * (t - t0) / dt; + y = rcOld.top + dy * (t - t0) / dt; + + //DebugString("appbar_slideWindow(2) %d %d\n", x, y); + move(x, y); + //SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + if (fShow) { + UpdateWindow(hwnd); + //invalidateWindowRegion(); + //updateWindowRegion(); + } + else UpdateWindow(GetDesktopWindow()); + } + + SetThreadPriority(hThreadMe, priority); + } + + //DebugString("appbar_slideWindow(3) %d %d\n", rcNew.left, rcNew.top); + move(rcNew.left, rcNew.top); + appbar_onSlide(); + } + WASABI_API_MAKI->vcpu_setComplete(); + m_sliding = 0; +} + +// ----------------------------------------------------------------------- +void AppBarWnd::unOwn() +{ + // registration was successful, we should reparent the window to NULL so that minimizing the app or changing the main AOT flag does + // nothing to this window + Layout *l = static_cast<Layout*>(getInterface(layoutGuid)); + if (l) { + if (!l->getNoParent()) { + l->setNoParent(2); + // whoaaah! + reinit(); + } + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::reOwn() { + OSWINDOWHANDLE mw = WASABI_API_WND->main_getRootWnd()->getOsWindowHandle(); + if (IsIconic(mw)) ShowWindow(mw, SW_RESTORE); + // undock was successful, we should re-own the window to what it was previously. if the old owner is minimized, we should restore it first + OSWINDOWHANDLE oldparent = WASABI_API_WND->main_getRootWnd()->getOsWindowHandle(); + if (IsIconic(oldparent)) ShowWindow(oldparent, SW_RESTORE); + Layout *l = static_cast<Layout *>(getInterface(layoutGuid)); + if (l) { + int oldnp = l->getNoParent(); + const wchar_t *np = l->getGuiObject()->guiobject_getXmlParam(L"noparent"); + int newnp = WTOI(np); + if (oldnp != newnp) + { + l->setNoParent(newnp); + // whoaaah! + reinit(); + } + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::straightenRect(int side, RECT *r) { + int w=0, h=0; + + int wasregistered = m_registered; + if (!m_registered) registerWinAppBar(); + + APPBARDATA abd; + abd.hWnd = hwnd; + abd.cbSize = sizeof(APPBARDATA); + abd.rc = *r; + abd.uEdge = side; + + RECT viewRect = {0}; + Wasabi::Std::getViewport(&viewRect, hwnd, 1); + + switch (side) { + case APPBAR_LEFT: + case APPBAR_RIGHT: + w = abd.rc.right - abd.rc.left; + abd.rc.top = viewRect.top; + abd.rc.bottom = viewRect.bottom; + break; + case APPBAR_TOP: + case APPBAR_BOTTOM: + h = abd.rc.bottom - abd.rc.top; + abd.rc.left = viewRect.left; + abd.rc.right = viewRect.right; + break; + } + + SHAppBarMessage(ABM_QUERYPOS, &abd); + + switch (abd.uEdge) { + case APPBAR_LEFT: + abd.rc.right = abd.rc.left + w; + break; + case APPBAR_RIGHT: + abd.rc.left = abd.rc.right - w; + break; + case APPBAR_TOP: + abd.rc.bottom = abd.rc.top + h; + break; + case APPBAR_BOTTOM: + abd.rc.top = abd.rc.bottom - h; + break; + } + + if (!wasregistered) unregisterWinAppBar(); + + *r = abd.rc; +} + +// ----------------------------------------------------------------------- +void AppBarWnd::appbar_setNoRestore(int no) { + m_norestore = no; +} + +// ----------------------------------------------------------------------- +void AppBarWnd::onSetVisible( int show ) +{ + if ( !show && m_side != APPBAR_NOTDOCKED && !m_suspended ) + { + if ( m_cur_autohide ) + { + resetAutoHideSide( m_cur_side ); + if ( m_cur_hiding ) + resetAutoUnHideTimer(); + else + resetAutoHideTimer(); + } + + m_suspended = 1; + unDock(); + APPBARWND_PARENT::onSetVisible( show ); + return; + } + else if ( show && m_suspended ) + { + APPBARWND_PARENT::onSetVisible( show ); + m_suspended = 0; + updateDocking(); + return; + } + + APPBARWND_PARENT::onSetVisible( show ); +} + +// ----------------------------------------------------------------------- +int AppBarWnd::screenCorner(POINT *pt) { + RECT primary = {0}; + Wasabi::Std::getViewport(&primary, hwnd, 1); + if (pt->x > primary.right-2 && pt->x <= primary.right) { + if (pt->y > primary.bottom-2 && pt->y <= primary.bottom) { + // bottom right corner + return 1; + } + else if (pt->y < primary.top+2 && pt->y >= primary.top) { + // top right corner + return 1; + } + } + else if (pt->x < primary.left+2 && pt->x >= primary.left) { + if (pt->y > primary.bottom-2 && pt->y <= primary.bottom) { + // bottom left corner + return 1; + } + else if (pt->y < primary.top+2 && pt->y >= primary.top) { + // top left corner + return 1; + } + } + return 0; +} + +// ----------------------------------------------------------------------- +void AppBarWnd::snapAdjust(RECT *r, int way) +{ + RECT s; + Layout *l = static_cast<Layout*>(getInterface(layoutGuid)); + if (!l) return; + l->getSnapAdjust(&s); + int h = r->bottom - r->top; + int w = r->right - r->left; + if (way == 1) { + h += s.top + s.bottom; + w += s.left + s.right; + r->left -= s.left; + r->top -= s.top; + r->bottom = r->top + h; + r->right = r->left + w; + } + else if (way == -1) { + h -= s.top + s.bottom; + w -= s.left + s.right; + r->left += s.left; + r->top += s.top; + r->bottom = r->top + h; + r->right = r->left + w; + } +} + +// ----------------------------------------------------------------------- +void AppBarWnd::onRatioChanged() +{ + APPBARWND_PARENT::onRatioChanged(); + if (m_side != APPBAR_NOTDOCKED) appbar_posChanged(); +}
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/appbarwnd.h b/Src/Wasabi/api/wnd/wndclass/appbarwnd.h new file mode 100644 index 00000000..7b8fba35 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/appbarwnd.h @@ -0,0 +1,155 @@ +#ifndef _APPBARWND_H +#define _APPBARWND_H + +#include <bfc/common.h> +#include <shellapi.h> +#include <api/wnd/wndclass/clickwnd.h> + +#define APPBARWND_PARENT ClickWnd + +#define APPBAR_TOP_ENABLED 1 +#define APPBAR_LEFT_ENABLED 2 +#define APPBAR_BOTTOM_ENABLED 4 +#define APPBAR_RIGHT_ENABLED 8 + +#define APPABR_ALL_ENABLED (APPBAR_TOP_ENABLED|APPBAR_LEFT_ENABLED|APPBAR_BOTTOM_ENABLED|APPBAR_RIGHT_ENABLED) + +#define APPBAR_CALLBACK WM_USER + 1010 + +#define IDT_AUTOHIDE 0x10000 +#define IDT_AUTOUNHIDE 0x10001 + +#ifdef WIN32 +#define APPBAR_NOTDOCKED -1 +#define APPBAR_LEFT ABE_LEFT +#define APPBAR_TOP ABE_TOP +#define APPBAR_RIGHT ABE_RIGHT +#define APPBAR_BOTTOM ABE_BOTTOM +#else +#error port me +#endif + +// todo : dispatch +class AppBar { +public: + virtual void appbar_dock(int side)=0; + virtual int appbar_isDocked()=0; + virtual int appbar_getSide()=0; + virtual void appbar_setEnabledSides(int mask)=0; + virtual int appbar_getEnabledSides()=0; + virtual int appbar_isSideEnabled(int side)=0; + virtual int appbar_testDock(int x, int y, RECT *dockrect=NULL)=0; + virtual int appbar_updateAutoHide()=0; + virtual int appbar_updateAlwaysOnTop()=0; + virtual int appbar_isHiding()=0; + virtual int appbar_wantAutoHide()=0; + virtual int appbar_wantAlwaysOnTop()=0; + virtual int appbar_isAutoHiding()=0; + virtual void appbar_onDock(int side) {} + virtual void appbar_onUnDock() {} + virtual void appbar_onSlide() {} + virtual void appbar_posChanged()=0; + virtual int appbar_isSideAutoHideSafe(int side)=0; + virtual int appbar_getAutoHideWidthHeight()=0; + virtual void appbar_setNoRestore(int no)=0; +}; + +// {242CFAA4-31B3-4b01-97C8-2F0A9FFDEF79} +static const GUID appBarGuid = +{ 0x242cfaa4, 0x31b3, 0x4b01, { 0x97, 0xc8, 0x2f, 0xa, 0x9f, 0xfd, 0xef, 0x79 } }; + +class api_region; + +// TODO: benski> only class making active use of being derived from this seems to be Layout and GuiObjectWnd +// maybe just layout ... + +class AppBarWnd : public APPBARWND_PARENT, public AppBar { + public: + AppBarWnd(); + virtual ~AppBarWnd(); + + void appbar_dock(int side); + int appbar_isDocked(); + int appbar_getSide(); + + void appbar_setEnabledSides(int mask); + int appbar_getEnabledSides(); + int appbar_isSideEnabled(int side); + + int appbar_testDock(int x, int y, RECT *dockrect=NULL); + + int appbar_updateAutoHide(); + int appbar_updateAlwaysOnTop(); + + int appbar_isSideAutoHideSafe(int side); + + virtual int appbar_wantAutoHide() { return 1; } + virtual int appbar_wantAlwaysOnTop() { return 1; } + + int appbar_isHiding(); + int appbar_isAutoHiding(); + + void appbar_posChanged(); + void appbar_setNoRestore(int no); + virtual int appbar_getAutoHideWidthHeight() { return 2; } + + virtual LRESULT wndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + virtual void onAfterReinit(); + virtual void onSetVisible(int show); + + virtual void onRatioChanged(); + + private: + + void appBarCallback(UINT uMsg, WPARAM wParam, LPARAM lParam); + + int registerWinAppBar(); + void unregisterWinAppBar(); + void notifyWinAppBarPosition(int side, RECT rect); + + OSWINDOWHANDLE getCurAutoHide(int side); + + void getDockRect(int side, RECT *rc); + void getEdge(int side, RECT *rc); + void straightenRect(int side, RECT *r); + void updateDocking(); + void updateSide(); + void updateTimers(); + void resetAutoHideSide(int side); + void setAutoHideSide(int side); + void setAutoHideTimer(); + void setAutoUnHideTimer(); + void resetAutoHideTimer(); + void resetAutoUnHideTimer(); + void onAutoHideTimer(); + void onAutoUnHideTimer(); + void autoHide(); + void autoUnHide(); + void slideWindow(RECT *prc); + int screenCorner(POINT *pt); + void snapAdjust(RECT *r, int way); + + void dock(int side); + void unDock(); + + void unOwn(); + void reOwn(); + + int m_registered; + int m_side; + int m_enabled; + int m_cur_autohide; + int m_cur_side; + int m_cur_hiding; + OSWINDOWHANDLE m_oldZOrder; + int m_destroying; + int m_norestore; + int m_autohide_timer_set; + int m_autounhide_timer_set; + int m_sliding; + int m_suspended; + int m_fs; + int m_wahidden; +}; + +#endif //_APPBARWND_H
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/backbufferwnd.cpp b/Src/Wasabi/api/wnd/wndclass/backbufferwnd.cpp new file mode 100644 index 00000000..10e0a04a --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/backbufferwnd.cpp @@ -0,0 +1,86 @@ +#include <precomp.h> +#include "backbufferwnd.h" +#include <tataki/canvas/bltcanvas.h> +#include <api/api.h> +#include <tataki/region/region.h> + +// ----------------------------------------------------------------------- +BackBufferWnd::BackBufferWnd() { + backbuffer = 0; + canvas_w = -1; + canvas_h = -1; + back_buffer = NULL; +} + +// ----------------------------------------------------------------------- +BackBufferWnd::~BackBufferWnd() { + delete back_buffer; +} + +//------------------------------------------------------------------------ +BltCanvas *BackBufferWnd::getBackBuffer() { + return back_buffer; +} + +// ----------------------------------------------------------------------- +int BackBufferWnd::onPaint(Canvas *canvas) { + + BBWND_PARENT::onPaint(canvas); + + if (!canvas) return 1; + + RECT r; + getClientRect(&r); + + if (back_buffer && r.right-r.left > 0 && r.bottom -r.top > 0) { + + int w = r.right-r.left; + int h = r.bottom-r.top; + + if (canvas_w != w || canvas_h != h) { + delete back_buffer; + back_buffer = new BltCanvas(w, h, getOsWindowHandle()); + canvas_w = w; + canvas_h = h; + } + +#ifdef _WIN32 + RegionI reg; + canvas->getClipRgn(®); + back_buffer->selectClipRgn(®); +#else +#warning port me +#endif + canvas->blit(r.left, r.top, back_buffer, 0, 0, w, h); + back_buffer->selectClipRgn(NULL); + } + + return 1; +} + +int BackBufferWnd::onSiblingInvalidateRgn(api_region *r, ifc_window *who, int who_idx, int my_idx) { + if (who_idx >= my_idx || !wantBackBuffer()) return 0; + + RECT rr; + getClientRect(&rr); + + api_region *_r = getRegion(); + RegionI *__r=NULL; + + if (!_r) { + __r = new RegionI(); + _r = __r; + _r->setRect(&rr); + } else { + _r->offset(rr.left, rr.top); + } + + int intersect = _r->doesIntersectRgn(r); + if (intersect) + r->addRegion(_r); + + delete __r; + + return intersect; +} + diff --git a/Src/Wasabi/api/wnd/wndclass/backbufferwnd.h b/Src/Wasabi/api/wnd/wndclass/backbufferwnd.h new file mode 100644 index 00000000..1f536ba1 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/backbufferwnd.h @@ -0,0 +1,53 @@ +#ifndef __BBWND_H +#define __BBWND_H + +#include <api/wnd/wndclass/abstractwndhold.h> + +#ifdef WASABI_COMPILE_SKIN +#define BBWND_PARENT AbstractWndHolder +#else +#define BBWND_PARENT ServiceWndHolder +#endif + +/** + class BackBufferWnd + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class BackBufferWnd : public BBWND_PARENT { + + public: + + BackBufferWnd(); + virtual ~BackBufferWnd(); + + virtual int onPaint(Canvas *c); + + /** + BackBufferWnd method wantBackBuffer . + + @ret 0 + @param None + */ + virtual int wantBackBuffer() { return 0; } + virtual BltCanvas *getBackBuffer(); + virtual int onSiblingInvalidateRgn(api_region *r, ifc_window *who, int who_idx, int my_idx); + + /** + BackBufferWnd method wantSiblingInvalidations . + + @ret 0 + @param None + */ + virtual int wantSiblingInvalidations() { return wantBackBuffer(); } + + private: + + int backbuffer; + BltCanvas *back_buffer; + int canvas_w, canvas_h; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/blankwnd.cpp b/Src/Wasabi/api/wnd/wndclass/blankwnd.cpp new file mode 100644 index 00000000..beeddfd5 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/blankwnd.cpp @@ -0,0 +1,24 @@ +#include "precomp.h" + +#include "blankwnd.h" +#include <tataki/canvas/canvas.h> +#include <api/wnd/PaintCanvas.h> + +BlankWnd::BlankWnd(RGB32 _color) : color(_color) +{ +} + +int BlankWnd::onPaint(Canvas *canvas) +{ + PaintCanvas pc; + if (canvas == NULL) + { + if (!pc.beginPaint(this)) return 0; + canvas = &pc; + } + + canvas->fillRect(&clientRect(), color); + + return 1; +} + diff --git a/Src/Wasabi/api/wnd/wndclass/blankwnd.h b/Src/Wasabi/api/wnd/wndclass/blankwnd.h new file mode 100644 index 00000000..56f44139 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/blankwnd.h @@ -0,0 +1,41 @@ +#ifndef _BLANKWND_H +#define _BLANKWND_H + +#include <bfc/common.h> +#include <api/wnd/virtualwnd.h> + +#define BLANKWND_PARENT VirtualWnd + +/** + Class BlankWnd provides blank windows. The initial color can be set in the + constructor, with a default of black. There is a method for painting the window from a Canvas. + + @short Blank Window with background color. + @author Nullsoft + @ver 1.0 + @see VirtualWnd +*/ +class BlankWnd : public BLANKWND_PARENT { +public: + /** + You can set the background color for the window via an RGB value. + The RGB value is contructed using RGB(), like so RGB(Red, Green, Blue); + + @param color The RGB value of the background color to use. + */ + BlankWnd(RGB32 color=RGB(0,0,0)); + + /** + This event is triggered when the window needs to be repainted. + Override it to implement your own handling of this event. + + @ret 1, If you handle the event; + @param canvas A pointer to the canvas on which will we paint. + */ + virtual int onPaint(Canvas *canvas); + +private: + RGB32 color; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/bufferpaintwnd.cpp b/Src/Wasabi/api/wnd/wndclass/bufferpaintwnd.cpp new file mode 100644 index 00000000..a32d29c5 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/bufferpaintwnd.cpp @@ -0,0 +1,113 @@ +#include <precomp.h> +#include "bufferpaintwnd.h" +#include <tataki/canvas/bltcanvas.h> + +// ----------------------------------------------------------------------- +BufferPaintWnd::BufferPaintWnd() { + canvas_w = -1; + canvas_h = -1; + render_canvas = NULL; + invalidated = 1; +} + +// ----------------------------------------------------------------------- +BufferPaintWnd::~BufferPaintWnd() { + delete render_canvas; +} + +// ----------------------------------------------------------------------- +int BufferPaintWnd::onInit() { + BUFFERPAINTWND_PARENT::onInit(); + return 1; +} + +// ----------------------------------------------------------------------- +void BufferPaintWnd::bufferPaint() { + updateCanvas(); + if (render_canvas != NULL) + onBufferPaint(render_canvas, canvas_w, canvas_h); +} + +void BufferPaintWnd::invalidateBuffer() { + invalidated = 1; + invalidate(); +} + +// ----------------------------------------------------------------------- +void BufferPaintWnd::getBufferPaintSize(int *w, int *h) { + RECT r; + getClientRect(&r); + if (w) *w = r.right-r.left; + if (h) *h = r.bottom-r.top; +} + +// ----------------------------------------------------------------------- +void BufferPaintWnd::getBufferPaintSource(RECT *r) { + ASSERT(r != NULL); + r->left = 0; + r->right = canvas_w; + r->top = 0; + r->bottom = canvas_h; +} + +// ----------------------------------------------------------------------- +void BufferPaintWnd::getBufferPaintDest(RECT *r) { + ASSERT(r != NULL); + getClientRect(r); +} + +// ----------------------------------------------------------------------- +int BufferPaintWnd::onPaint(Canvas *canvas) { + + BUFFERPAINTWND_PARENT::onPaint(canvas); + + if (invalidated) bufferPaint(); + invalidated = 0; + + RECT r; + getBufferPaintDest(&r); + RECT sr; + getBufferPaintSource(&sr); + + render_canvas->/*getSkinBitmap()->*/stretchToRectAlpha(canvas, &sr, &r, getPaintingAlpha()); + + return 1; +} + +// ----------------------------------------------------------------------- +int BufferPaintWnd::onResize() { + if (!BUFFERPAINTWND_PARENT::onResize()) return 0; + if (updateCanvas()) { + invalidated = 1; + invalidate(); + } + return 1; +} + +// ----------------------------------------------------------------------- +int BufferPaintWnd::updateCanvas() { + int w, h; + getBufferPaintSize(&w, &h); + + if (wantEvenAlignment()) { + if (w & 1) w++; + if (h & 1) h++; + } + + if (w == 0 || h == 0) return 0; + + int newone = 0; + + if (canvas_w != w || canvas_h != h) { + if (render_canvas) + render_canvas->DestructiveResize(w, wantNegativeHeight() ? -h : h); + else + render_canvas = new BltCanvas(w, wantNegativeHeight() ? -h : h, getOsWindowHandle()); + canvas_w = w; + canvas_h = h; + newone = 1; + onNewBuffer(canvas_w, canvas_h); + } + + return newone; +} diff --git a/Src/Wasabi/api/wnd/wndclass/bufferpaintwnd.h b/Src/Wasabi/api/wnd/wndclass/bufferpaintwnd.h new file mode 100644 index 00000000..778fb751 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/bufferpaintwnd.h @@ -0,0 +1,40 @@ +#ifndef __BPAINTWND_H +#define __BPAINTWND_H + +#include <api/wnd/wndclass/guiobjwnd.h> + +#define BUFFERPAINTWND_PARENT GuiObjectWnd + +class BufferPaintWnd : public BUFFERPAINTWND_PARENT { + + public: + BufferPaintWnd(); + virtual ~BufferPaintWnd(); + + virtual int onInit(); + virtual int onPaint(Canvas *c); + + virtual int onBufferPaint(BltCanvas *c, int w, int h) { return 1; } + virtual int wantEvenAlignment() { return 0; } // if you need even coordinates for your framebuffer, return 1 here + virtual void getBufferPaintSize(int *w, int *h); // by default returns client width/height + virtual void getBufferPaintSource(RECT *r); // by default returns the size of the quickpaint canvas + virtual void getBufferPaintDest(RECT *r); // by default returns the size of client area + virtual int wantNegativeHeight() { return 0; } + virtual void invalidateBuffer(); + virtual int onResize(); + virtual void onNewBuffer(int w, int h) {} + + protected: + BltCanvas *render_canvas; + + private: + void bufferPaint(); + int updateCanvas(); + + int canvas_w, canvas_h; + + int invalidated; +}; + + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/buttbar.cpp b/Src/Wasabi/api/wnd/wndclass/buttbar.cpp new file mode 100644 index 00000000..ffb491cf --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/buttbar.cpp @@ -0,0 +1,208 @@ +#include "precomp.h" +//PORTABLE +#include <bfc/wasabi_std.h> +#include <api/wnd/wndclass/buttbar.h> +#include <api/wnd/notifmsg.h> +#include <api/wnd/wndclass/buttwnd.h> +#include <api/wnd/popup.h> +#include <api/script/objects/c_script/c_text.h> +#include <api/script/objects/c_script/h_button.h> + + +class ButtHooker : public H_Button { +public: + ButtHooker(ButtBar *hangout, ScriptObject *butt) : bb(hangout), H_Button(butt) { } + + void hook_onLeftClick() { + bb->onLeftPush(0, 0); + } + +private: + ButtBar *bb; +}; + +ButtBar::ButtBar(int resizemode) { + spacer = 0; + resize_mode = resizemode; + hooker = NULL; + if (resize_mode == STACK) { + setContent(L"wasabi.buttonbar.stack"); + } +} + +ButtBar::~ButtBar() { + buttons.deleteAll(); + delete hooker; +} + +void ButtBar::setResizeMode(int resizemode) { + if (resize_mode == resizemode) return; + resize_mode = resizemode; + if (isPostOnInit()) onResize(); +} + +int ButtBar::onInit() { + int i; + + BUTTBAR_PARENT::onInit(); + + // create the buttons + for (i = 0; i < buttons.getNumItems(); i++) { + buttons[i]->init(this); + if (resize_mode == STACK) { + if (i != 0) buttons[i]->setVisible(FALSE); + if (i == 0) setGroupLabel(buttons[i]->getButtonText()); + } + } + + return 1; +} + +int ButtBar::addChild(ButtonWnd *child) { + buttons.addItem(child); + if (isInited()) { + child->init(this); + child->setParent(this); + onResize(); + if (buttons.getNumItems() == 1) + setGroupLabel(child->getButtonText()); + } + return 1; +} + +int ButtBar::removeChild(ButtonWnd *child) { + if (!buttons.haveItem(child)) return 0; + if (isInited()) onResize(); + return 1; +} + +int ButtBar::getNumChildren() { + return buttons.getNumItems(); +} + +ButtonWnd *ButtBar::enumChild(int n) { + return buttons[n]; +} + +int ButtBar::getWidth() { + int w = 0; + for (int i = 0; i < buttons.getNumItems(); i++) { + w += buttons[i]->getWidth()+spacer; + } + return w; +} + +int ButtBar::getHeight() { + if (resize_mode == STACK) { + ifc_window *rw = getContentRootWnd(); + return rw->getPreferences(SUGGESTED_H); + } else { + int h = 0; + for (int i = 0; i < buttons.getNumItems(); i++) { + h = MAX(h, buttons[i]->getHeight()+1); + } + return h; + } +} + +void ButtBar::onLeftPush(int x, int y) +{ + if (resize_mode == STACK) + { + PopupMenu pop(this); + foreach(buttons) + pop.addCommand(buttons.getfor()->getButtonText(), foreach_index); + endfor + int r = pop.popAnchored(); + if (r >= 0) { + buttons[r]->onLeftPush(0, 0); + setGroupLabel(buttons[r]->getButtonText()); + } + } +} + +int ButtBar::childNotify(ifc_window *child, int msg, intptr_t p1, intptr_t p2) { + switch (msg) { + case ChildNotify::BUTTON_LEFTPUSH: { + int ret; + if (ret = onLeftPush(child->getNotifyId())) { + return ret; + } else { +// This won't fit the current notification schema. +// We _must_ change it -- too many interfaces assume that the +// button notification is called back through the parent. +// return notifyParent(msg, p1, p2); + +// So, I made a new basewnd method passNotifyUp() to defer a notification +// to the current object's notification target. + return passNotifyUp(child, msg, p1, p2); + } + } + break; + } + return BUTTBAR_PARENT::childNotify(child, msg, p1, p2); +} + +int ButtBar::onResize() { + BUTTBAR_PARENT::onResize(); // calling your parent is good(tm) =) + if (!isPostOnInit()) return 0; // that's just an optim, in case someone's dumb and calling us directly when it shouldnt + switch (resize_mode) { + case NORMAL: { + RECT r = clientRect(); + int height = r.bottom - r.top; + int x = r.left; + for (int i = 0; i < buttons.getNumItems(); i++) { + int w = buttons[i]->getWidth()+spacer; + buttons[i]->resize(x, r.top, w, height); + x += w; + if (x > r.right) break; + } + } + break; + case STRETCH: { + if (buttons.getNumItems() > 0) { + RECT r = clientRect(); + int height = r.bottom - r.top; + int w = (r.right - r.left) / buttons.getNumItems(); + int x = r.left; + for (int i = 0; i < buttons.getNumItems(); i++) { + if (i == buttons.getNumItems()-1) w = (r.right - r.left) - x; + buttons[i]->resize(x, r.top, w, height); + x += w; + } + } + } + break; + case STACK: // no point + break; + } + + return 1; +} + +int ButtBar::onPaint(Canvas *canvas) { + ASSERT(canvas != NULL); + if (resize_mode != STACK) { + BUTTBAR_PARENT::onPaint(canvas); + renderBaseTexture(canvas, clientRect()); + } + return 1; +} + +void ButtBar::setGroupLabel(const wchar_t *l) { + setName(l); + onNewContent(); +} + +void ButtBar::onNewContent() +{ + if (resize_mode != STACK) return; + ScriptObject *text = findScriptObject(L"buttonbar.text"); + if (text == NULL) return; + C_Text(text).setText(getNameSafe(L"no tabs")); + + // hook the clicks + delete hooker; + ScriptObject *mousetrap = findScriptObject(L"mousetrap"); + hooker = new ButtHooker(this, mousetrap); +} diff --git a/Src/Wasabi/api/wnd/wndclass/buttbar.h b/Src/Wasabi/api/wnd/wndclass/buttbar.h new file mode 100644 index 00000000..473ddd58 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/buttbar.h @@ -0,0 +1,168 @@ +#ifndef _BUTTBAR_H +#define _BUTTBAR_H + +#include <bfc/common.h> +#include <api/wnd/wndclass/guiobjwnd.h> +#include <bfc/ptrlist.h> + +class ButtonWnd; +class ButtHooker; + +#define BUTTBAR_PARENT GuiObjectWnd +/** + A resizable button bar control. + + @short A resizable button bar + @author Nullsoft + @ver 1.0 + @see ButtonWnd +*/ +class ButtBar : public BUTTBAR_PARENT { +public: + // resize modes + /** + Resize modes for the button bar. + */ + enum { NORMAL, STRETCH, STACK }; + + /** + You can set the resize mode for the button bar by specifying it + via the contructor. + + @see ~ButtBar() setResizeMode() + @param resizemode The default resize mode. + */ + ButtBar(int resizemode=NORMAL); + + /** + Deletes all the buttons present in the button bar. + + @see ButtBar() + */ + virtual ~ButtBar(); + + /** + This event is triggered when the button bar is being initialized. + If you override this, please call up the parent chain first, then + do your initialization. + + @ret 1 + */ + virtual int onInit(); + + /** + This event is triggered when the button bar is being resized. + If you override this, please call up the parent chain first, then + do your own resize handling. + + @ret 1 + */ + virtual int onResize(); + + /** + This event is triggered when the button bar is being painted. + If you override this, please call up the parent chain first, then + do your painting. + + @ret 1 + @param canvas The canvas upon which we will paint ourself. + */ + virtual int onPaint(Canvas *canvas); + + /** + Sets the resize mode for the button bar. + + @param resizemode NORMAL, Normal Resize; STRETCH, Stretch the button bar to window width; STACK, ?; + */ + virtual void setResizeMode(int resizemode); + + /** + Enables you to add a child window to your button bar. + Since this is a button bar, the windows you can add must be + derived or be ButtonWnd's. + + @see removeChild() + @see getNumChildren() + @see enumChild() + @see ButtonWnd + @ret 1 + @param child A pointer to the child window to add. + */ + int addChild(ButtonWnd *child); + + /** + */ + int removeChild(ButtonWnd *child); // does not delete, just removes + + /** + Get the number of children (buttons) that the button bar has. + + @ret The number of children (buttons). + */ + int getNumChildren(); + + /** + Get a pointer to a child (button) in the button bar, by button index number. + The first button added is at index 0. + + @ret !NULL, a pointer the requested button; NULL, The button does not exist; + */ + ButtonWnd *enumChild(int n); + + /** + Get the width of the button bar (in pixels). + + @see getHeight() + @ret Width of the button bar (in pixels). + */ + int getWidth(); + + /** + Get the height of the button bar (in pixels). + + @see getWidth() + @ret Height of the button bar (in pixels). + */ + int getHeight(); + + /** + Event is triggered when the left mouse button is used to click on the + button bar. Override this to implement your own handling of the event. + If you override this method, call up the parent chain. + + @param x The x coordinate of the mouse cursor in the button bar. + @param y The y coordinate of the mouse cursor in the button bar. + */ + virtual void onLeftPush(int x, int y); + + /** + Notify a child window via a generic message system. + + @see addChild() + @ret + @param child A pointer to the child window which will receive the notify. + @param msg The message you want to send to the child. + @param p1 A user parameter. + @param p2 A user parameter. + */ + virtual int childNotify(ifc_window *child, int msg, + intptr_t param1=0, intptr_t param2=0); + + // GuiObjectWnd + virtual void onNewContent(); + + void setSpacer(int sp) { spacer = sp; } + + void setGroupLabel(const wchar_t *l); + +protected: + virtual int onLeftPush(int id) { return 0; } + + PtrList<ButtonWnd> buttons; +private: + int resize_mode; + int spacer; + ButtHooker *hooker; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/buttwnd.cpp b/Src/Wasabi/api/wnd/wndclass/buttwnd.cpp new file mode 100644 index 00000000..ddd500e5 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/buttwnd.cpp @@ -0,0 +1,761 @@ +#include <precomp.h> +// bitmap-style buttons + +#include "buttwnd.h" +#include <bfc/wasabi_std.h> +#include <tataki/canvas/bltcanvas.h> +#include <tataki/region/region.h> +#include <api/wnd/notifmsg.h> + + +#include <api/wndmgr/msgbox.h> +#include <api/wnd/PaintCanvas.h> +#define DEFAULT_BUTTON_HEIGHT 20 + +ButtonWnd::ButtonWnd(const wchar_t *button_text) +{ + if (button_text != NULL) + setName(button_text); + currgn = NULL; + hirgn = NULL; + normalrgn = NULL; + pushedrgn = NULL; + activatedrgn = NULL; + base_texture = NULL; + xShift=0; + yShift=0; + textsize = DEFAULT_BUTTON_TEXT_SIZE; + alignment = TEXTALIGN_CENTER; + activated = 0; + userhilite = 0; + userdown = 0; + use_base_texture = 0; + center_bitmap = 0; + enabled = 1; + checked=0; + autodim=0; + borders = 1; + borderstyle = 0; + setBorderStyle(L"button_normal"); + iwantfocus = 1; + color_text = L"wasabi.button.text"; + color_hilite = L"wasabi.button.hiliteText"; + color_dimmed = L"wasabi.button.dimmedText"; + checkbmp = L"wasabi.popup.menu.check"; + inactivealpha = 255; + activealpha = 255; + setRectRgn(1); + retcode = MSGBOX_ABORTED; + forcedown=0; +} + +ButtonWnd::~ButtonWnd() { + delete normalrgn; + delete pushedrgn; + delete hirgn; + delete activatedrgn; +} + +void ButtonWnd::checkState(POINT *pt) { + POINT pt2; + if (pt == NULL) { + pt = &pt2; + Wasabi::Std::getMousePos(pt); + } + + api_region *old = currgn; + + if (getDown()) { // button is down + if (pushedrgn) + currgn = pushedrgn; + else + currgn = normalrgn; + } else { // button is not down + if (hirgn && getHilite()) + currgn = hirgn; + else + currgn = normalrgn; + } + + if (old != currgn) invalidateWindowRegion(); +} + +void ButtonWnd::onCancelCapture() { + BUTTONWND_PARENT::onCancelCapture(); + checkState(); +} + +int ButtonWnd::onMouseMove(int x, int y) { + POINT pt; + checkState(&pt); + return BUTTONWND_PARENT::onMouseMove(x, y); +} + +api_region *ButtonWnd::getRegion() { + if (borders) return NULL; + return currgn; +} + +void ButtonWnd::setModalRetCode(int r) { retcode = r; } +int ButtonWnd::getModalRetCode() const { return retcode; } + +void ButtonWnd::onLeaveArea() { + BUTTONWND_PARENT::onLeaveArea(); + if (hirgn || getDown()) invalidate(); +} + +void ButtonWnd::onEnterArea() { + BUTTONWND_PARENT::onEnterArea(); + if (hirgn) invalidate(); +} + +/*BOOL ButtonWnd::mouseInRegion(int x, int y) { + POINT pos={x,y}; + POINT p2=pos; + + RECT r; + getClientRect(&r); + pos.x-=r.left; + pos.y-=r.top; + + return (((!currgn || rectrgn) && PtInRect(&r, p2)) || (currgn && currgn->ptInRegion(&pos))); +}*/ + +int ButtonWnd::setBitmaps(const wchar_t *_normal, const wchar_t *_pushed, const wchar_t *_hilited, const wchar_t *_activated) { + + if (_normal) { delete normalrgn; normalrgn = NULL; } + if (_pushed) { delete pushedrgn; pushedrgn = NULL; } + if (_hilited) { delete hirgn; hirgn = NULL; } + if (_activated) { delete activatedrgn; activatedrgn = NULL; } + + if (_normal) { + normalbmp = _normal; + normalrgn = new RegionI(normalbmp.getBitmap()); + currgn = normalrgn; + } + + if (_pushed) { + pushedbmp = _pushed; + pushedrgn = new RegionI(pushedbmp.getBitmap()); + } + + if (_hilited) { + hilitebmp = _hilited; + hirgn = new RegionI(hilitebmp.getBitmap()); + } + + if (_activated) { + activatedbmp = _activated; + activatedrgn = new RegionI(activatedbmp.getBitmap()); + } + + if (isPostOnInit()) + invalidate(); + + return 1; +} + +SkinBitmap *ButtonWnd::getNormalBitmap() { + return normalbmp.getBitmap(); +} + +void ButtonWnd::freeResources() { + BUTTONWND_PARENT::freeResources(); + delete normalrgn; + delete pushedrgn; + delete hirgn; + delete activatedrgn; + pushedrgn=NULL; + normalrgn=NULL; + hirgn=NULL; + activatedrgn=NULL; + currgn=NULL; +} + +void ButtonWnd::reloadResources() { + BUTTONWND_PARENT::reloadResources(); + if (normalbmp.getBitmap()) + normalrgn = new RegionI(normalbmp.getBitmap()); + if (pushedbmp.getBitmap()) + pushedrgn = new RegionI(pushedbmp.getBitmap()); + if (hilitebmp.getBitmap()) + hirgn = new RegionI(hilitebmp.getBitmap()); + if (activatedbmp.getBitmap()) + activatedrgn = new RegionI(activatedbmp.getBitmap()); + currgn = normalrgn; +} + +int ButtonWnd::setBitmapCenter(int centerit) { + return center_bitmap = !!centerit; +} + +int ButtonWnd::setBitmaps(OSMODULEHANDLE hInst, int _normal, int _pushed, int _hilited, int _activated, const wchar_t *_colorgroup) +{ + if (_normal) { delete normalrgn; normalrgn = NULL; } + if (_pushed) { delete pushedrgn; pushedrgn = NULL; } + if (_hilited) { delete hirgn; hirgn = NULL; } + if (_activated) { delete activatedrgn; activatedrgn = NULL; } + + if (_colorgroup == NULL) + _colorgroup = colorgroup; + + if (_normal) + { + normalbmp.setHInstanceBitmapColorGroup(_colorgroup); +#ifdef _WIN32 + normalbmp.setHInstance(hInst); +#else +#warning port me? +#endif + normalbmp = _normal; + normalrgn = new RegionI(normalbmp.getBitmap()); + } + + if (_pushed) { + pushedbmp.setHInstanceBitmapColorGroup(_colorgroup); +#ifdef _WIN32 + pushedbmp.setHInstance(hInst); +#else +#warning port me? +#endif + pushedbmp = _pushed; + pushedrgn = new RegionI(pushedbmp.getBitmap()); + } + + if (_hilited) { + hilitebmp.setHInstanceBitmapColorGroup(_colorgroup); +#ifdef _WIN32 + hilitebmp.setHInstance(hInst); +#else +#warning port me? +#endif + hilitebmp = _hilited; + hirgn = new RegionI(hilitebmp.getBitmap()); + } + + if (_activated) { + activatedbmp.setHInstanceBitmapColorGroup(_colorgroup); + #ifdef _WIN32 + activatedbmp.setHInstance(hInst); +#else +#warning port me? +#endif + activatedbmp = _activated; + activatedrgn = new RegionI(activatedbmp.getBitmap()); + } + + return 1; +} + +void ButtonWnd::setUseBaseTexture(int useit) +{ + if (use_base_texture == useit) return; + use_base_texture = useit; + invalidate(); +} + +void ButtonWnd::setBaseTexture(SkinBitmap *bmp, int x, int y, int tile) +{ + base_texture = bmp; + use_base_texture = TRUE; + tile_base_texture=tile; + xShift=x; + yShift=y; + invalidate(); +} + +int ButtonWnd::setButtonText(const wchar_t *text, int size) +{ + textsize = size; + ASSERT(textsize > 0); + setName(text); + invalidate(); + return 1; +} + +const wchar_t * ButtonWnd::getButtonText() +{ + return getName(); +} + +void ButtonWnd::setTextAlign(TextAlign align) +{ + alignment = align; + invalidate(); +} + +int ButtonWnd::getWidth() +{ + int addl=0; + if (checked) { + addl=checkbmp.getWidth()+3; + } + if (rightbmp.getBitmap()) + addl+=rightbmp.getWidth()+3; + if (normalbmp == NULL) + { + TextInfoCanvas blt(this); + Wasabi::FontInfo fontInfo; + fontInfo.pointSize = textsize; + StringPrintfW lstr(L"j%sj", getNameSafe(L"")); + if (wcschr(lstr, '\t')) lstr.cat(L" "); + int a=MAX((blt.getTextWidth(lstr, &fontInfo)*11)/10,8)+addl; + return a; + } + return normalbmp.getWidth()+addl; +} + +int ButtonWnd::getHeight() +{ + int minh=0; + if (checked>0) + minh=checkbmp.getHeight(); + if (rightbmp.getBitmap()) + minh=MAX(rightbmp.getHeight(),minh); + if (normalbmp == NULL) + { + TextInfoCanvas blt(this); + Wasabi::FontInfo fontInfo; + fontInfo.pointSize = textsize; + int r = MAX(MAX((blt.getTextHeight(getName(), &fontInfo)*11)/10,minh),4); + return r; + } + return MAX(normalbmp.getHeight(),minh); +} + +void ButtonWnd::enableButton(int _enabled) { + _enabled = !!_enabled; + if (enabled != _enabled) invalidate(); + enabled = _enabled; + onEnable(enabled); +} + +int ButtonWnd::getEnabled() const { + return enabled; +} + +int ButtonWnd::onPaint(Canvas *canvas) +{ + PaintBltCanvas paintcanvas; + SkinBitmap *bmp; + RECT r; + int labelxoffs=0; + int offset = (enabled&&(getPushed()||getDown())) ? 1 : 0; + + if (checked) labelxoffs+=3+checkbmp.getWidth(); + + if (canvas == NULL) { + if (!paintcanvas.beginPaint(this)) return 0; + canvas = &paintcanvas; + } + BUTTONWND_PARENT::onPaint(canvas); + + bmp = normalbmp; + if (pushedbmp && (getDown() || getPushed())) bmp = pushedbmp; + else if ((getHilite() || userhilite) && hilitebmp) bmp = hilitebmp; + else if (activatedbmp && getActivatedButton()) bmp = activatedbmp; + + getClientRect(&r); + + RECT nr = r; +// RECT fcr = r; + if (use_base_texture) + { + if (!base_texture) + renderBaseTexture(canvas, r); + else { + RECT cr; + cr.left = xShift; + cr.top = yShift; + cr.right = cr.left + (r.right-r.left); + cr.bottom = cr.top + (r.bottom-r.top); + if (tile_base_texture) base_texture->blitTile(canvas, &r,xShift,yShift); + else base_texture->stretchToRectAlpha(canvas, &cr, &r, getPaintingAlpha()); + } + } + else + { + if (borders) + { + int sysobj = -1; + if (!enabled) + sysobj = dsoDisabled; + else + sysobj = (getPushed() || getDown()) ? dsoPushed : dsoNormal; + if (sysobj != -1) canvas->drawSysObject(&nr, sysobj, getPaintingAlpha()); + } + } + + if (checked>0) + { + RECT ar; + int c=(r.top+r.bottom)/2; + ar.left=r.left; + ar.top=c-checkbmp.getHeight()/2; + ar.bottom=ar.top+checkbmp.getHeight(); + ar.right=r.left+checkbmp.getWidth(); + checkbmp.getBitmap()->stretchToRectAlpha(canvas,&ar,getPaintingAlpha()); + } + if (rightbmp.getBitmap()) { + RECT ar; + int c=(r.top+r.bottom)/2; + ar.top=c-rightbmp.getHeight()/2; + ar.bottom=ar.top+rightbmp.getHeight(); + ar.right=r.right; + ar.left=ar.right-rightbmp.getWidth(); + + int alpha = getPaintingAlpha(); + if (!getEnabled()) alpha /= 2; + rightbmp.getBitmap()->stretchToRectAlpha(canvas, &ar, alpha); + } + + if (bmp != NULL) { + RECT br = r; + if (center_bitmap) { + int w = (r.right - r.left) - getWidth() - labelxoffs; + int h = (r.bottom - r.top) - getHeight(); + br.top = r.top + h/2 + offset; + br.bottom = br.top + getHeight(); + br.left = r.left + w/2 + labelxoffs + offset; + br.right = br.left + getWidth() - (rightbmp.getBitmap()?rightbmp.getWidth()+3:0); + } else { + br.left += labelxoffs; + br.right -= (rightbmp.getBitmap()?rightbmp.getWidth()+3:0); + } + int alpha2; + if (!hilitebmp && enabled && autodim) { + alpha2=128; + if (getHilite() || userhilite) alpha2=255; + } else alpha2 = enabled ? 255 : 64; + bmp->stretchToRectAlpha(canvas, &br,autodim ? (getPaintingAlpha()+alpha2)>>1 : getPaintingAlpha()); + } + + if (getName() != NULL) + { + Wasabi::FontInfo fontInfo; + fontInfo.opaque = false; + fontInfo.pointSize = textsize;; + + int textw, texth; + canvas->getTextExtent(getName(), &textw, &texth, &fontInfo); + + if (!enabled) + fontInfo.color = color_dimmed; + else if (userhilite) + fontInfo.color = color_hilite; + else + fontInfo.color = color_text; + int h=(r.bottom-r.top-texth)/2; + if (h<0) h=0; + + r.left += offset + labelxoffs; + r.right += offset - (rightbmp.getBitmap()?rightbmp.getWidth()+3:0); + r.top += offset+h; + r.bottom = r.top+texth; + + switch (alignment) + { + case TEXTALIGN_CENTER: + canvas->textOutCentered(&r, getName(), &fontInfo); + break; + + case TEXTALIGN_RIGHT: + canvas->textOut(r.right-textw, r.top, textw, texth, getName(), &fontInfo); + break; + + case TEXTALIGN_LEFT: + if (!wcsstr(getName(), L"\t")) + { + canvas->textOut(r.left, r.top, r.right-r.left, r.bottom-r.top, getName(), &fontInfo); + } + else + { + StringW lstr(getName()); + wchar_t *p=wcsstr(lstr.getNonConstVal(),L"\t"); + if (p) *p++=0; + else p=L""; + int tw=canvas->getTextWidth(p, &fontInfo); + canvas->textOut(r.left, r.top, r.right-r.left-tw, r.bottom-r.top, lstr, &fontInfo); + canvas->textOut(r.right-tw, r.top, tw, r.bottom-r.top, p, &fontInfo); + } + break; + + case TEXTALIGN_LEFT_ELLIPSIS: + if (!wcsstr(getName(),L"\t")) + { + canvas->textOutEllipsed(r.left, r.top, r.right-r.left, r.bottom-r.top, getName(), &fontInfo); + } + else + { + StringW lstr(getName()); + wchar_t *p=wcsstr(lstr.getNonConstVal(),L"\t"); + if (p) *p++=0; + else p=L""; + int tw=canvas->getTextWidth(p, &fontInfo); + canvas->textOutEllipsed(r.left, r.top, r.right-r.left-tw, r.bottom-r.top, lstr, &fontInfo); + canvas->textOutEllipsed(r.right-tw, r.top, tw, r.bottom-r.top, p, &fontInfo); + } + break; + } +/* + if (textjustify == BUTTONJUSTIFY_CENTER) + canvas->textOutCentered(&r, getName()); + else if (textjustify == BUTTONJUSTIFY_LEFT) + { + if (!STRSTR(getName(),"\t")) + canvas->textOutEllipsed(r.left, r.top, r.right-r.left, r.bottom-r.top, getName()); + else + { + char *lstr=STRDUP(getName()); + char *p=STRSTR(lstr,"\t"); + if (p) *p++=0; + else p=""; + int tw=canvas->getTextWidth(p); + canvas->textOutEllipsed(r.left, r.top, r.right-r.left-tw, r.bottom-r.top, lstr); + canvas->textOutEllipsed(r.right-tw, r.top, tw, r.bottom-r.top, p); + FREE(lstr); + } + } +*/ + } + + +/* if (enabled && gotFocus() && wantFocus()) { // SKIN ME + fcr.left+=3; + fcr.right-=3; + fcr.top+=3; + fcr.bottom-=3; + DrawFocusRect(canvas->getHDC(), &fcr); + }*/ + + return 1; +} + +void ButtonWnd::onLeftPush(int x, int y) +{ + notifyParent(ChildNotify::BUTTON_LEFTPUSH); + invalidate(); +} +void ButtonWnd::onRightPush(int x, int y) { + notifyParent(ChildNotify::BUTTON_RIGHTPUSH); + invalidate(); +} +void ButtonWnd::onLeftDoubleClick(int x, int y) { + notifyParent(ChildNotify::BUTTON_LEFTDOUBLECLICK); +} +void ButtonWnd::onRightDoubleClick(int x, int y) { + notifyParent(ChildNotify::BUTTON_RIGHTDOUBLECLICK); +} + +void ButtonWnd::setHilite(int h) { + h = !!h; + if (userhilite != h) { + userhilite = h; + invalidate(); + } +} + +int ButtonWnd::getHilite() { + return userhilite || BUTTONWND_PARENT::getHilite(); +} + +int ButtonWnd::getPushed() const { + return userdown || forcedown; +} + +void ButtonWnd::setPushed(int p) { + p = !!p; + if (userdown != p) + { + userdown=p; + invalidate(); + } +} + +int ButtonWnd::onResize() { + BUTTONWND_PARENT::onResize(); +// invalidate(); + return 1; +} + +void ButtonWnd::setCheckBitmap(const wchar_t *checkbm) { + checkbmp = checkbm; +} + +int ButtonWnd::setRightBitmap(const wchar_t *bitmap) { + rightbmp=bitmap; + return 1; +} + +void ButtonWnd::setActivatedButton(int a) { + if (activated != a) { + activated = a; + invalidate(); + onActivateButton(activated); + } +} + +void ButtonWnd::setActivatedNoCallback(int a) { + // also force invalidate. + activated = a; + invalidate(); +} + +int ButtonWnd::onActivateButton(int active) { + return 1; +} + +int ButtonWnd::getActivatedButton() { + return activated; +} + +void ButtonWnd::setBorders(int b) { + b = !!b; + if (borders != b) { + borders = b; + setRectRgn(borders); + invalidate(); + } +} + +void ButtonWnd::setBorderStyle(const wchar_t *style) { + if (style == NULL) style = L""; + if (borderstyle && !WCSICMP(borderstyle, style)) return; + +// borderstyle = style; + + using namespace DrawSysObj; + static struct { + const wchar_t *style; + int normal, pushed, disabled; + } chart[] = { + { L"button_normal", BUTTON, BUTTON_PUSHED, BUTTON_DISABLED }, + { L"osbutton_normal", OSBUTTON, OSBUTTON_PUSHED, OSBUTTON_DISABLED }, + { L"osbutton_close", OSBUTTON_CLOSE, OSBUTTON_CLOSE_PUSHED, OSBUTTON_CLOSE_DISABLED }, + { L"osbutton_minimize", OSBUTTON_MINIMIZE, OSBUTTON_MINIMIZE_PUSHED, OSBUTTON_MINIMIZE_DISABLED }, + { L"osbutton_maximize", OSBUTTON_MAXIMIZE, OSBUTTON_MAXIMIZE_PUSHED, OSBUTTON_MAXIMIZE_DISABLED }, + { NULL, BUTTON, BUTTON_PUSHED, BUTTON_DISABLED }, + }; + dsoNormal = dsoPushed = dsoDisabled = -1; + for (int i = 0; ; i++) { + if (chart[i].style == NULL) return; + if (!WCSICMP(chart[i].style, style)) { + borderstyle = chart[i].style; + dsoNormal = chart[i].normal; + dsoPushed = chart[i].pushed; + dsoDisabled = chart[i].disabled; + } + } + + invalidate(); +} + +const wchar_t *ButtonWnd::getBorderStyle() { + return borderstyle; +} + +void ButtonWnd::setInactiveAlpha(int a) { + inactivealpha=a; +} + +void ButtonWnd::setActiveAlpha(int a) { + activealpha=a; +} + +int ButtonWnd::onGetFocus() { + BUTTONWND_PARENT::onGetFocus(); +// invalidate(); + return 1; +} + +int ButtonWnd::onKillFocus() { + BUTTONWND_PARENT::onKillFocus(); +// invalidate(); + return 1; +} + +void ButtonWnd::setColors(const wchar_t *text, const wchar_t *hilite, const wchar_t *dimmed) { + color_text=text; + color_hilite=hilite; + color_dimmed=dimmed; + invalidate(); +} + +void ButtonWnd::setTextColor(const wchar_t *text) { + color_text=text; + invalidate(); +} + +void ButtonWnd::setTextHoverColor(const wchar_t *hilite) { + color_hilite=hilite; + invalidate(); +} + +void ButtonWnd::setTextDimmedColor(const wchar_t *dimmed) { + color_dimmed=dimmed; + invalidate(); +} + +int ButtonWnd::onEnable(int is) { + return BUTTONWND_PARENT::onEnable(is); +} + +int ButtonWnd::getPreferences(int what) { + switch (what) { + case SUGGESTED_W: { + if (!normalBmpStr.isempty()) return normalbmp.getWidth(); + return getWidth(); + } + case SUGGESTED_H: { + if (!normalBmpStr.isempty()) return normalbmp.getHeight(); + return getHeight(); + } + } + return BUTTONWND_PARENT::getPreferences(what); +} + +int ButtonWnd::onInit() { + int r = BUTTONWND_PARENT::onInit(); + currgn = normalrgn; + return r; +} + +int ButtonWnd::onChar(unsigned int c) +{ + switch (c) { +#ifdef _WIN32 + case VK_RETURN: + case VK_SPACE: + postDeferredCallback(DEFEREDCB_DOWN, 0, 0); + postDeferredCallback(DEFEREDCB_UP, 0, 250); + //return BUTTONWND_PARENT::onChar(c); + break; +#else +#warning port me +#endif + default: + return BUTTONWND_PARENT::onChar(c); + } + return 1; +} + + +int ButtonWnd::onDeferredCallback(intptr_t p1, intptr_t p2) { + switch (p1) { + case DEFEREDCB_DOWN: + forcedown = 1; + invalidate(); + break; + case DEFEREDCB_UP: + forcedown = 0; + invalidate(); + RECT r; + getClientRect(&r); + onLeftPush(r.left+(r.right-r.left)/2, r.top+(r.bottom-r.top)/2); + default: + return BUTTONWND_PARENT::onDeferredCallback(p1, p2); + } + return 1; +} + + diff --git a/Src/Wasabi/api/wnd/wndclass/buttwnd.h b/Src/Wasabi/api/wnd/wndclass/buttwnd.h new file mode 100644 index 00000000..d0367573 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/buttwnd.h @@ -0,0 +1,602 @@ +#ifndef _BUTTWND_H +#define _BUTTWND_H + +#include <wasabicfg.h> + +#include <bfc/common.h> +#include <tataki/canvas/canvas.h> +#include <tataki/bitmap/autobitmap.h> +#include <api/wnd/wndclass/guiobjwnd.h> +#include <tataki/color/skinclr.h> +#include <api/wnd/accessible.h> +#include <api/wnd/textalign.h> + +class api_region; + +#define DEFAULT_BUTTON_TEXT_SIZE 14 +/** + Button Text Alignment + Darkain: this was changed to use TextAlign +*/ +/* +typedef enum { + BUTTONJUSTIFY_LEFT, + BUTTONJUSTIFY_CENTER +} ButtonJustify; +*/ + +#define DEFEREDCB_DOWN 0x450 +#define DEFEREDCB_UP 0x451 + +#define BUTTONWND_PARENT GuiObjectWnd + +/** + A fully skinnable button. Has images for normal, hilited, activated states. + Plus images for a checked state. It may also be used to draw OS style buttons. + See setBorderStyle() for more details. + + @short Button control. + @author Nullsoft + @ver 1.0 + @see ButtBar +*/ +class ButtonWnd : public BUTTONWND_PARENT { +public: + /** + Sets defaults for ButtonWnd objects. + + @see ~ButtonWnd() + @param button_text The button's caption. + */ + ButtonWnd(const wchar_t *button_text=NULL); + + /** + Deletes components of ButtonWnd. + + @see ButtonWnd() + */ + virtual ~ButtonWnd(); + + /** + Paints the bitmap on canvas according + to current options (centering, tiling, stretching, title). + + @ret 0 for failure, 1 for success + @param canvas The canvas on which to paint. + */ + virtual int onPaint(Canvas *canvas); + + /** + Sets the bitmaps that will be used to render the button. + This includes bitmaps for various button states. Also enables + you to set the colorgroup (gammagroup) for the bitmaps. + + @ret 1 + @param _normal Bitmap for normal state. + @param _pushed Bitmap for pushed state. + @param _hilited Bitmap for hilited state. + @param _activated Bitmap for activated state. + @param colorgroup The colorgroup for the bitmaps (gammagroup). + */ + int setBitmaps(const wchar_t *normal, const wchar_t *pushed=NULL, const wchar_t *hilited=NULL, const wchar_t *activated=NULL); + + SkinBitmap *getNormalBitmap(); + + /** + Sets the bitmaps that will be used to render the button. + This includes bitmaps for various button states. Also enables + you to set the colorgroup (gammagroup) for the bitmaps. + + @ret 1 + @param hInst The parent window's instance handle. + @param _normal Bitmap for normal state. + @param _pushed Bitmap for pushed state. + @param _hilited Bitmap for hilited state. + @param _activated Bitmap for activated state. + @param colorgroup The colorgroup for the bitmaps (gammagroup). + */ + int setBitmaps(OSMODULEHANDLE hInst, int normal, int pushed, int hilited, int activated, const wchar_t *colorgroup=NULL); + + /** + Set the right bitmap to be used. + + @see setBitmaps() + @ret 1 + @param bitmap The name of the bitmap to use. + */ + int setRightBitmap(const wchar_t *bitmap); + + /** + Center the bitmap? + + @see setBitmaps() + @ret Normalized flag + @param centerit A non zero value will center the bitmap. + */ + int setBitmapCenter(int centerit); + + /** + Sets base texture and causes rerendering. + + @see setBaseTexture() + @param useit A non zero value will use the base texture. + */ + void setUseBaseTexture(int useit); + + /** + Sets bitmap for button, sets position for button, flags whether to tile the bitmap + + @see setUseBaseTexture() + @param bmp Skin bitmap for button + @param x Button position on x-coordinate + @param yButton position on y-coordinate + @param tile Flag + */ + void setBaseTexture(SkinBitmap *bmp, int x, int y, int tile=0); + + /** + Sets the colorgroup (gammagroup) for all the bitmaps associated with + this button. + + @param _colorgroup The colorgroup for the bitmaps. + */ + void setHInstanceColorGroup(const wchar_t *_colorgroup) { colorgroup = _colorgroup; } + + /** + Writes given text to button in given size and triggers rendering. + + @see getButtonText() + @assert Text string is not empty + @ret 1 + @param text Label text + @param size Size to render label text + */ + int setButtonText(const wchar_t *text, int size=DEFAULT_BUTTON_TEXT_SIZE); + + /** + Gets text from button. + + @see setButtonText() + @ret Button text string + */ + const wchar_t * getButtonText(); + + /** + Sets text to render at left, in center, or at right. + + @see setButtonText() + @see getButtonText() + @see ButtonJustify + @param jus BUTTONJUSTIFY_LEFT, left justified; BUTTONJUSTIFY_CENTER, centered; + */ +// void setTextJustification(ButtonJustify jus); + void setTextAlign(TextAlign align); + + TextAlign getTextAlign() { return alignment; } + + /** + Enables and disables wantfocus for the button. When disabled, the button can + never receive focus. + + @param want !0, enable focus; 0, disable focus; + */ + void setWantFocus(int want) { iwantfocus = !!want; } + + /** + Return the wantfocus + */ + virtual int wantFocus() const { return iwantfocus; } + + /** + Event is triggered when the mouse leaves the button's region. + Override this event to implement your own behavior. + */ + virtual void onLeaveArea(); + virtual void onEnterArea(); + + /** + Gets width of button, allowing for length of text plus button margin, if any. + + @see getHeight() + @ret Button width (in pixels). + */ + int getWidth(); // our preferred width and height (from bitmaps) + + /** + Gets height of button, allowing for height of text plus button margin, if any. + + @see getWidth() + @ret Button height (in pixels). + */ + int getHeight(); + + /** + Event is triggered when focus is given to the button. + Override this event to implement your own behavior. + + @see onKillFocus() + @ret 1 + */ + virtual int onGetFocus(); + + /** + Event is triggered when the button focus is lost. + Override this event to implement your own behavior. + + @see onGetFocus() + @ret 1 + */ + virtual int onKillFocus(); + + /** + Event is triggered when a key is pressed and the button + has focus. + + @ret 1, if you handle the event; + @param c The value of the key that was pressed. + */ + virtual int onChar(unsigned int c); + + /** + Saves new status and rerenders, if button enabled status changes. + + @see getEnabled() + @see onEnable() + @param _enabled 0, disabled; !0 enabled; + */ + void enableButton(int enabled); // can be pushed + + /** + Tells parent to handle left button click. + + @see onRightPush() + @param x Mouse click x-coordinate + @param y Mouse click y-coordinate + */ + virtual void onLeftPush(int x, int y); + + /** + Passes right mouse clicks to the parent. + + @see onLeftPush() + @param x Mouse click x-coordinate + @param y Mouse click y-coordinate + */ + virtual void onRightPush(int x, int y); + + /** + Passes left double click to parent. + + @see onRightDoubleClick() + @param x Mouse click x-coordinate + @param y Mouse click y-coordinate + */ + virtual void onLeftDoubleClick(int x, int y); + + /** + Passes right double click to parent + + @see onLeftDoubleClick() + @param x Mouse click x-coordinate + @param y Mouse click y-coordinate + */ + virtual void onRightDoubleClick(int x, int y); + + /** + Event is triggered when the button will be resized. + Override this event to implement your own behavior. + + The default behavior is to cause a repaint. + + @ret 1 + */ + virtual int onResize(); + + /** + Sets the region pointed at after each mouse move. + If the region has changed, it invalidate the region + so that it will be updated on the screen. + + @ret Status from parent class + @param x New x-coordinate of mouse cursor + @param y New y-coordinate of mouse cursor + */ + virtual int onMouseMove(int x, int y); // need to catch region changes + + /** + Event is triggered when the button is enabled or disabled. + Override this event to implement your own behavior. + + @see getEnabled() + @ret 1 + @param is The enable state (nonzero is enabled). + */ + virtual int onEnable(int is); + + /** + Returns the value of the enabled flag. + + @see enableButton() + @see onEnable() + @ret enabled + */ + virtual int getEnabled() const; + + /** + Get the preferences for this button. + This will enable you to read the suggested width and height + for the button. + + @ret Width or height of the normal bitmap, as requested, or a property from the parent class. + @param what SUGGESTED_W, will return the width; SUGGESTED_H, will return the height; + */ + virtual int getPreferences(int what); + + /** + Get the button state. This is the state caused by user interaction. + + @ret !0, pushed; 0, not pushed; + */ + virtual int userDown() { return userdown; } + + /** + + */ + virtual int wantClicks() { return getEnabled(); } + + /** + Set the bitmap to use when the button will be "checked". + This enables you to have checked buttons and menu items. + + @see setChecked() + @see getChecked() + @param checkbm The name of the bitmap to use. + */ + void setCheckBitmap(const wchar_t *checkbm); + + /** + Set the checked state of the button. + + @param c <0, not checked; 0, none, >0 checked; + */ + void setChecked(int c) { checked=c; }; // <0=nocheck, 0=none, >0=checked + + /** + Get the checked state of the button. + + @ret <0, not checked; 0, none; >0 checked; + */ + int getChecked() const { return checked; } + + /** + Triggers rerendering in the opposite + highlight state if the hilighting flag is changed. + + @see getHilite() + @param h + */ + void setHilite(int h); + + /** + + + @see setHilite() + @ret Is either highlighting flag set? + */ + int getHilite(); + + /** + Simulate a button push. You can use this method to simulate + menu pushing also. + + @see getPushed() + @param p A nonzero value will simulate a push. + */ + void setPushed(int p); // used by menus to simulate pushing + + /** + Get the pushed state of a button. + + @see setPushed() + @ret 0, not pushed; !0, pushed; + */ + int getPushed() const; // used by menus to simulate pushing + + /** + Sets the auto dim state. Autodim will dim the normal + bitmap if no hilite bitmap is provided. + + @param ad !0, autodim on; 0, autodim off; + */ + void setAutoDim(int ad) { autodim=!!ad; } // nonzero makes it dim if there's no hilite bitmap + + /** + Get the autodim state. + + @see setAutoDim() + @ret 0, autodim off; !0 autodim on; + */ + int getAutoDim() const { return autodim; } // nonzero makes it dim if there's no hilite bitmap + + /** + Set the active state of the button. + + @see getActivatedButton() + @see setActivatedNoCallback() + @param a !0, activate the button; 0, deactivate the button; + */ + virtual void setActivatedButton(int a); + + /** + Set the active state of the button, without generating a callback. + This means that the onActivated event will not fire for this button. + + @see getActivatedButton() + @see setActivatedButton() + @param a !0, activate the button; 0, deactivate the button; + */ + virtual void setActivatedNoCallback(int a); + + /** + Get the active state of the button. + + @see setActivatedButton() + @ret activated !0, active; 0, inactive; + */ + virtual int getActivatedButton(); + + /** + Render borders around the button? + + @param b !0, borders; 0, no borders; + */ + void setBorders(int b); + + /** + Sets the border style for the button. This + has no effect if no borders are being drawn. + + "button_normal" A normal button. + "osbutton_normal" A normal OS button (if in Windows, will show a std win32 button). + "osbutton_close" An OS close button. + "osbutton_minimize" An OS minimize button. + "osbutton_maximize" An OS maximize button. + + @see getBorderStyle() + @param style The style of button you want. + */ + void setBorderStyle(const wchar_t *style); + + /** + Get the border style of the button (if there is one). + If no border is drawn, this method always returns NULL. + + @see setBorderStyle() + @ret The border style. + */ + const wchar_t *getBorderStyle(); + + /** + Set the inactive alpha blending value. This is the alpha blending + value that will be used for blending when the button does NOT have focus. + + @param a The alpha value, range is from 0 (fully transparent) to 255 (fully opaque). + */ + void setInactiveAlpha(int a); + + /** + Set the active alpha blending value. This is the alpha blending value + that will be used for blending when the button HAS focus. + + @param a The alpha value, range is from 0 (fully transparent) to 255 (fully opaque). + */ + void setActiveAlpha(int a); + + /** + Sets the colors for various states of our button. This is + done via element id's which are in the skin xml or registered + as seperate xml. + + @param text Normal text color (window has focus but button is not active). + @param hilite Hilited text color (button has focus). + @param dimmed Dimmed text color (parent window doesn't even have focus). + */ + void setColors(const wchar_t *text=L"studio.button.text", const wchar_t *hilite=L"studio.button.hiliteText", const wchar_t *dimmed=L"studio.button.dimmedText"); + + /** + Deletes the regions and resets them to NULL. + + @see reloadResources() + */ + virtual void freeResources(); + + /** + Reinitializes regions for which there are bitmaps available. + + @see freeResources() + */ + virtual void reloadResources(); + + /** + Event is triggered when the is being activated. + Override this event to implement your own behavior. + + @see setActivatedButton() + @ret 1 + @param active The button's state (nonzero is active). + */ + virtual int onActivateButton(int active); + + /** + Returns the current region of the button. + + @see api_region + @ret The region of the button. + */ + virtual api_region *getRegion(); + + /** + Set the modal return. This is what will be returned + when the window is closed and the window is set to modal. + + @param r The return code you wish to set. + */ + virtual void setModalRetCode(int r); + + /** + Get the modal return code for the window. + + @ret The modal return code. + */ + virtual int getModalRetCode() const; + + /** + Event is triggered when the button is about to be initialized. + Override this event to implement your own behavior. + + @ret 1 + */ + virtual int onInit(); + virtual int onDeferredCallback(intptr_t p1, intptr_t p2); + + virtual void setTextColor(const wchar_t *text); + virtual void setTextHoverColor(const wchar_t *text); + virtual void setTextDimmedColor(const wchar_t *text); + + virtual void checkState(POINT *pt=NULL); + virtual void onCancelCapture(); + +private: + AutoSkinBitmap normalbmp, pushedbmp, hilitebmp, checkbmp, rightbmp, activatedbmp; + SkinBitmap *base_texture; + RegionI *normalrgn, *pushedrgn, *hirgn, *currgn, *activatedrgn; + int textsize; + TextAlign alignment; + SkinColor color_text, color_hilite, color_dimmed; + int retcode; + + StringW normalBmpStr, pushedBmpStr, hilitedBmpStr, activatedBmpStr; + + int folderstyle; + int autodim; + int userhilite; + int userdown; + int activated; + int enabled; + int borders; + const wchar_t *borderstyle; + int dsoNormal, dsoPushed, dsoDisabled; + + int iwantfocus; + int center_bitmap; + int use_base_texture; + + int checked; + int xShift, yShift, tile_base_texture; + + int inactivealpha, activealpha; + StringW colorgroup; + int forcedown; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/clickwnd.cpp b/Src/Wasabi/api/wnd/wndclass/clickwnd.cpp new file mode 100644 index 00000000..054915a2 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/clickwnd.cpp @@ -0,0 +1,319 @@ +#include "precomp.h" +#include <api/wnd/api_wnd.h> + +#include "clickwnd.h" +#include <api/wnd/notifmsg.h> +#include <api/wnd/wndclass/guiobjwnd.h> + + +enum +{ + CLICKWND_LBUTTONDOWN = 0, + CLICKWND_LBUTTONUP, + CLICKWND_RBUTTONDOWN, + CLICKWND_RBUTTONUP, +}; + +ClickWnd::ClickWnd() +{ + handleRight = TRUE; + button = -1; + mousedown = 0; + mcaptured = 0; + hilite = 0; + down = 0; + areacheck = 0; +} + +ClickWnd::~ClickWnd() +{ + BaseWnd::hintDestroying(); // so basewnd doesn't call onCancelCapture + if (getCapture()) endCapture(); +} + +void ClickWnd::setHandleRightClick(int tf) +{ + handleRight=tf; +} + +int ClickWnd::getHandleRightClick() +{ + return handleRight; +} + +int ClickWnd::onLeftButtonDown(int x, int y) +{ + notifyParent(ChildNotify::CLICKWND_LEFTDOWN, x, y); + CLICKWND_PARENT::onLeftButtonDown(x, y); + abortTip(); +#ifdef _WIN32 + ifc_window *dp = getDesktopParent(); + if (dp != NULL) + { + if (dp->wantActivation()) + { + SetActiveWindow(getRootParent()->gethWnd()); + SetFocus(getRootParent()->gethWnd()); + } + else + { + HWND w = dp->gethWnd(); + HWND owner = GetWindow(w, GW_OWNER); + if (owner != NULL) { + SetActiveWindow(owner); + SetFocus(owner); + } + } + } + else + { + SetActiveWindow(getRootParent()->gethWnd()); + } +#else +#warning port me or remove me +#endif + if (ptInRegion(x, y)) + return onButtonDown(CLICKWND_LBUTTONDOWN, x, y); + else + return 1; +} + +int ClickWnd::onRightButtonDown(int x, int y) +{ + notifyParent(ChildNotify::CLICKWND_RIGHTDOWN, x, y); + CLICKWND_PARENT::onRightButtonDown(x, y); + abortTip(); + if (!handleRight) return 1; + if (ptInRegion(x, y)) + return onButtonDown(CLICKWND_RBUTTONDOWN, x, y); + else + return 1; +} + +int ClickWnd::onLeftButtonUp(int x, int y) +{ + notifyParent(ChildNotify::CLICKWND_LEFTUP, x, y); + CLICKWND_PARENT::onLeftButtonUp(x, y); +//jf +// if (ptInRegion()) + return onButtonUp(CLICKWND_LBUTTONUP, x, y); +// else +// return 1; +} + +int ClickWnd::onRightButtonUp(int x, int y) +{ + notifyParent(ChildNotify::CLICKWND_RIGHTUP, x, y); + CLICKWND_PARENT::onRightButtonUp(x, y); + //jf + //if (ptInRegion()) + if (!handleRight) { + onRightPush(x, y); + return 1; + } + return onButtonUp(CLICKWND_RBUTTONUP, x, y); +// else +// return 1; +} + +int ClickWnd::onMouseMove(int x, int y) +{ + POINT pos, rpos={x,y}; + int mouseover; + + CLICKWND_PARENT::onMouseMove(x, y); + + pos=rpos; + clientToScreen(&pos); + + int lasthilite = hilite; + + mouseover = (WASABI_API_WND->rootWndFromPoint(&pos) == static_cast<ifc_window *>(this) && ptInRegion(x, y)); + if (!mouseover && (!mousedown +#ifdef _WIN32 + || !Std::keyDown(button?MK_RBUTTON:MK_LBUTTON) +#else +#warning port me +#endif + )) { + if (mcaptured || getCapture()) { + endCapture(); + mcaptured = 0; + } + mousedown = 0; + down = 0; + if (wantClickWndAutoInvalidate()) invalidate(); + if (hilite) _onLeaveArea(); + hilite = 0; + return 1; + } else if (!mouseover && hilite) { + hilite = 0; + _onLeaveArea(); + } else if (mouseover && !hilite) { + hilite = 1; + _onEnterArea(); + } + + if (!getCapture() && mouseover) { // capture to see when leave + _enterCapture(); + } + + int lastdown = down; + hilite = mouseover; +#ifdef WASABI_COMPILE_WNDMGR + int m = getGuiObject() ? getGuiObject()->guiobject_getMover() : 0; +#else + int m = 0; +#endif + if (!m) { + down = userDown() || (mouseover && mousedown); + } else + down = userDown() || mousedown; + + // FG> note to self now that i finally fixed this... : + // there is a potential bottleneck here, if for some reason this test is always true when moving the windows around like crazy. + if (down != lastdown || (hilite != lasthilite && !m)) { + if (wantClickWndAutoInvalidate()) invalidate(); + } + +//invalidate(); + return 1; +} + +void ClickWnd::_enterCapture() +{ + //gee!! if (!hilite) _onEnterArea(); + if (!getCapture()) beginCapture(); + mcaptured = 1; +} + +int ClickWnd::onButtonDown(int which, int x, int y) +{ + if (!wantClicks()) return 1; + + if (!getCapture()) { + _enterCapture(); + } + mousedown = 1; + down = 1; + button = -1; + if (which == CLICKWND_LBUTTONDOWN) button = 0; + else if (which == CLICKWND_RBUTTONDOWN) button = 1; + if (wantClickWndAutoInvalidate()) invalidate(); + + return 1; +} + +int ClickWnd::onButtonUp(int which, int x, int y) +{ + // make sure same button + if (button == 0 && which == CLICKWND_RBUTTONUP) return 1; + if (button == 1 && which == CLICKWND_LBUTTONUP) return 1; + + if (!down) { + if (mcaptured) { + endCapture(); + mcaptured = 0; + } + if (hilite) _onLeaveArea(); + hilite = 0; + mousedown = 0; + return 1; + } + + POINT pos={x,y}; + clientToScreen(&pos); + + int mouseover = (WASABI_API_WND->rootWndFromPoint(&pos) == (ifc_window *)this && ptInRegion(x, y)); + if (!mouseover) { + if (mcaptured) { + endCapture(); + mcaptured = 0; + } + if (hilite) _onLeaveArea(); + hilite = 0; + } + + // it was down, process the event + int a = down; + down = 0; + mousedown = 0; + if (wantClickWndAutoInvalidate()) invalidate(); + + if (a) { + if (button == 0) onLeftPush(x, y); + else if (button == 1) onRightPush(x, y); + } + + // we need to do this again (and get the new mouse pos) because onLeft/RightPush may have called a + // message loop and let the mouse leave without us being aware of it + Wasabi::Std::getMousePos(&x, &y); + pos.x = x; + pos.y = y; + screenToClient(&x, &y); + mouseover = (WASABI_API_WND->rootWndFromPoint(&pos) == (ifc_window *)this && ptInRegion(x, y)); + if (!mouseover && hilite) _onLeaveArea(); + else if (mouseover && !hilite) _onEnterArea(); + hilite = mouseover; + + return 1; +} + +void ClickWnd::onSetVisible( int show ) +{ + CLICKWND_PARENT::onSetVisible( show ); + if ( !show ) + { + if ( getCapture() ) + { + mcaptured = 0; + endCapture(); + } + down = 0; + mousedown = 0; + + if ( hilite ) + _onLeaveArea(); + + hilite = 0; + } +} + +void ClickWnd::_onEnterArea() +{ + if (areacheck == 0) { + areacheck++; + onEnterArea(); + } else + DebugString("onEnterArea check failed %08X \n", this); +} + +void ClickWnd::_onLeaveArea() +{ + if (areacheck == 1) { + areacheck--; + onLeaveArea(); + } else + DebugString("onLeaveArea check failed %08X\n", this); +} + +void ClickWnd::onEnterArea() +{ +// DebugString("onEnterArea %08X\n", this); +} + +void ClickWnd::onLeaveArea() +{ +// DebugString("onLeaveArea %08X\n", this); +} + +void ClickWnd::onCancelCapture() +{ + CLICKWND_PARENT::onCancelCapture(); + mcaptured=0; + down = 0; + mousedown = 0; + if (hilite) _onLeaveArea(); + hilite = 0; +} + diff --git a/Src/Wasabi/api/wnd/wndclass/clickwnd.h b/Src/Wasabi/api/wnd/wndclass/clickwnd.h new file mode 100644 index 00000000..101e8445 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/clickwnd.h @@ -0,0 +1,74 @@ +#ifndef _CLICKWND_H +#define _CLICKWND_H + +// this class defines clicking behavior, i.e. detecting mouse downs and ups +// and doing captures to determine clicks + +#include <bfc/common.h> +// benski> CUT: #include <api/wnd/wndclass/backbufferwnd.h> +#include <api/wnd/wndclass/abstractwndhold.h> + +#ifdef WASABI_COMPILE_SKIN +#define CLICKWND_PARENT AbstractWndHolder +#else +#define CLICKWND_PARENT ServiceWndHolder +#endif +// benski> CUT: #define CLICKWND_PARENT BackBufferWnd + +class NOVTABLE ClickWnd : public CLICKWND_PARENT { + +public: + ClickWnd(); + virtual ~ClickWnd(); + + void setHandleRightClick(int tf); + int getHandleRightClick(); + + // override these to get clicks! + virtual void onLeftPush(int x, int y) {} + virtual void onRightPush(int x, int y) {} + virtual void onLeftDoubleClick(int x, int y) {} + virtual void onRightDoubleClick(int x, int y) {} + + virtual void onEnterArea(); + virtual void onLeaveArea(); + + virtual void onSetVisible(int show); + virtual void onCancelCapture(); + virtual int isInClick() { return mousedown; } + +protected: + virtual int onLeftButtonDown(int x, int y); + virtual int onRightButtonDown(int x, int y); + virtual int onLeftButtonUp(int x, int y); + virtual int onRightButtonUp(int x, int y); + virtual int onMouseMove(int x, int y); + + // override this and return 0 to ignore clicks + virtual int wantClicks() { return 1; } + // override this and return 1 to force down-ness + virtual int userDown() { return 0; } + + virtual int getHilite() { return hilite; } // mouse is over, period + virtual int getDown() { return down; } // mouse is over and pushing down + + int onButtonDown(int which, int x, int y); + int onButtonUp(int which, int x, int y); + void _enterCapture(); + + virtual int wantClickWndAutoInvalidate() { return 1; } + +private: + void _onEnterArea(); + void _onLeaveArea(); + + int button; // 0 == left, 1 == right, which button was pushed + int handleRight:1; + int mousedown:1; + int mcaptured:1; // we are capturing the mouse + int hilite:1; // mouse is over but not down + int down:1; + int areacheck; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/ddrawwnd.cpp b/Src/Wasabi/api/wnd/wndclass/ddrawwnd.cpp new file mode 100644 index 00000000..d095da03 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/ddrawwnd.cpp @@ -0,0 +1,219 @@ +#include "precomp.h" +#include <process.h> + +#include "ddrawwnd.h" +#include "../bfc/canvas.h" +#include "../bfc/region.h" + +DDrawWnd::DDrawWnd() { + m_lpDD = NULL; + lpClipper = NULL; + m_lpRenderSurf = NULL; + m_lpPrimSurf = NULL; +} + +DDrawWnd::~DDrawWnd() { + deleteFrameBuffer(NULL); +} + +void DDrawWnd::deleteFrameBuffer(Canvas *canvas) { + if (m_lpRenderSurf) m_lpRenderSurf->Release(); + if (m_lpPrimSurf) m_lpPrimSurf->Release(); + if (lpClipper) lpClipper->Release(); + if (m_lpDD) m_lpDD->Release(); + m_lpRenderSurf = NULL; + m_lpPrimSurf = NULL; + m_lpDD = NULL; + lpClipper = NULL; + ddlist.removeItem(this); +} + +int DDrawWnd::onInit() { + DDRAWWND_PARENT::onInit(); + if (!allow_dd) return 1; + return 1; +} + +Canvas *DDrawWnd::createFrameBuffer(int _w, int _h) { + + if (!allow_dd) return DDRAWWND_PARENT::createFrameBuffer(_w, _h); + + if (virtualCanvas && !m_lpPrimSurf) + DDRAWWND_PARENT::deleteFrameBuffer(virtualCanvas); + + deleteFrameBuffer(NULL); + + int resize_h = 8; + int resize_w = 8; + + w = _w; + h = _h; + + if (DirectDrawCreate(NULL,&m_lpDD,NULL) != DD_OK) { + m_lpDD=NULL; + MessageBox(gethWnd(),"Error creating ddraw object","DDraw",0); + return NULL; + } + + int dbl=0; + + m_lpDD->SetCooperativeLevel(gethWnd(), DDSCL_NORMAL); + resize_w=(((w>>dbl)+3)&~3); +// g_noshoww=resize_w-(w>>dbl); + resize_h=h>>dbl; + + DDSURFACEDESC DDsd={sizeof(DDsd),}; + + DDsd.dwFlags = DDSD_CAPS; + DDsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + if (m_lpDD->CreateSurface(&DDsd, &m_lpPrimSurf, NULL) != DD_OK) { + m_lpPrimSurf=0; + return NULL; + } + + if (m_lpDD->CreateClipper(0, &lpClipper, NULL) != DD_OK ) { + m_lpPrimSurf->Release(); + m_lpPrimSurf=0; + return NULL; + } + + lpClipper->SetHWnd(0, gethWnd()); + m_lpPrimSurf->SetClipper(lpClipper); + + DDsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PITCH|DDSD_PIXELFORMAT; + DDsd.dwWidth=resize_w; + DDsd.dwHeight=resize_h; + DDsd.lPitch=resize_w*sizeof(int); + DDsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY; + DDsd.ddpfPixelFormat.dwSize = sizeof(DDsd.ddpfPixelFormat); + DDsd.ddpfPixelFormat.dwFlags=DDPF_RGB; + DDsd.ddpfPixelFormat.dwRGBBitCount = 32; + DDsd.ddpfPixelFormat.dwRBitMask=0xff0000; + DDsd.ddpfPixelFormat.dwGBitMask=0x00ff00; + DDsd.ddpfPixelFormat.dwBBitMask=0x0000ff; + if (m_lpDD->CreateSurface(&DDsd, &m_lpRenderSurf, NULL) != DD_OK) { + m_lpRenderSurf->Release(); + m_lpPrimSurf->Release(); + lpClipper->Release(); + m_lpRenderSurf=0; + m_lpPrimSurf=0; + lpClipper=0; + return NULL; + } + fb_canvas = new DDSurfaceCanvas(m_lpRenderSurf, w, h); + + ddlist.addItem(this); + + if (!thread) + startThread(); + + return fb_canvas; +} + +int DDrawWnd::virtualBeforePaint(api_region *r) { + if (!allow_dd) return DDRAWWND_PARENT::virtualBeforePaint(r); + EnterCriticalSection(&DDrawWnd::cs); + fb_canvas->enter(); + return 1; +} + +int DDrawWnd::virtualAfterPaint(api_region *r) { + if (!allow_dd) return DDRAWWND_PARENT::virtualAfterPaint(r); + fb_canvas->exit(); + LeaveCriticalSection(&DDrawWnd::cs); + return 1; +} + +void DDrawWnd::virtualCanvasCommit(Canvas *canvas, RECT *internalrect, double ra) { + if (!allow_dd) { DDRAWWND_PARENT::commitFrameBuffer(canvas, internalrect, ra); return; } + + internalrect->left = MAX(0, (int)internalrect->left); + internalrect->top = MAX(0, (int)internalrect->top); + internalrect->right = MAX(w, (int)internalrect->right); + internalrect->bottom = MAX(h, (int)internalrect->bottom); + + RECT wr; + RECT screenrect = *internalrect; + + getWindowRect(&wr); + screenrect.left += wr.left; + screenrect.top += wr.top; + screenrect.right += wr.left; + screenrect.bottom += wr.top; + + if (ra == 1.0) { + if (m_lpPrimSurf->Blt(&screenrect,m_lpRenderSurf,internalrect,DDBLT_WAIT,NULL) == DDERR_SURFACELOST) { + m_lpPrimSurf->Restore(); + } + } else { + RECT rcr=screenrect; + rcr.left = wr.left + (int)((double)internalrect->left*ra); + rcr.top = wr.top + (int)((double)internalrect->top*ra); + rcr.right = rcr.left + (int)((double)(internalrect->right-internalrect->left)*ra); + rcr.bottom = rcr.top + (int)((double)(internalrect->bottom-internalrect->top)*ra); + + if (m_lpPrimSurf->Blt(&rcr,m_lpRenderSurf,internalrect,DDBLT_WAIT,NULL) == DDERR_SURFACELOST) + m_lpPrimSurf->Restore(); + } +} + +void DDrawWnd::startThread() { + DWORD id; + quitthread=0; + InitializeCriticalSection(&cs); + thread = (HANDLE)_beginthreadex(NULL,0,renderThread,0,0,(unsigned int *)&id); +} + +void DDrawWnd::stopThread() { + quitthread = 1; +} + +unsigned int WINAPI DDrawWnd::renderThread(void *) { + while (!quitthread) { + for (int i=0;i<ddlist.getNumItems();i++) + ddlist.enumItem(i)->flushPaint(); + Sleep(MIN(MAX(sleep_val,1),100)); + } + _endthreadex(0); + return 1; +} + +void DDrawWnd::invalidate() { + if (!allow_dd) { DDRAWWND_PARENT::invalidate(); return; } + DDRAWWND_PARENT::deferedInvalidate(); +} + +void DDrawWnd::invalidateRect(RECT *r) { + if (!allow_dd) { DDRAWWND_PARENT::invalidateRect(r); return; } + DDRAWWND_PARENT::deferedInvalidateRect(r); +} + +void DDrawWnd::invalidateRgn(api_region *rgn) { + if (!allow_dd) { DDRAWWND_PARENT::invalidateRgn(rgn); return; } + DDRAWWND_PARENT::deferedInvalidateRgn(rgn); +} + +void DDrawWnd::validate() { + if (!allow_dd) { DDRAWWND_PARENT::validate(); return; } + DDRAWWND_PARENT::deferedValidate(); +} + +void DDrawWnd::validateRect(RECT *r) { + if (!allow_dd) { DDRAWWND_PARENT::validateRect(r); return; } + DDRAWWND_PARENT::deferedValidateRect(r); +} + +void DDrawWnd::validateRgn(api_region *rgn) { + if (!allow_dd) { DDRAWWND_PARENT::validateRgn(rgn); return; } + DDRAWWND_PARENT::deferedValidateRgn(rgn); +} + + +CRITICAL_SECTION DDrawWnd::cs; +HANDLE DDrawWnd::thread=NULL; +int DDrawWnd::quitthread=0; +PtrList<DDrawWnd> DDrawWnd::ddlist; +int DDrawWnd::allow_dd = 1; +int DDrawWnd::sleep_val = 10; + + diff --git a/Src/Wasabi/api/wnd/wndclass/ddrawwnd.h b/Src/Wasabi/api/wnd/wndclass/ddrawwnd.h new file mode 100644 index 00000000..3863131c --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/ddrawwnd.h @@ -0,0 +1,60 @@ +#ifndef __DDRAWWND_H +#define __DDRAWWND_H + +#include <ddraw.h> +#include "../bfc/basewnd.h" + +class DDSurfaceCanvas; +class DDrawWnd; +class api_region; + +#define DDRAWWND_PARENT BaseWnd + +class NOVTABLE DDrawWnd : public DDRAWWND_PARENT { + +public: + + DDrawWnd(); + virtual ~DDrawWnd(); + + virtual int virtualBeforePaint(api_region *r); + virtual int virtualAfterPaint(api_region *r); + + virtual void virtualCanvasCommit(Canvas *canvas, RECT *r, double ratio); + + virtual Canvas *createFrameBuffer(int w, int h); + virtual void deleteFrameBuffer(Canvas *canvas); + + virtual int onInit(); + + virtual void invalidate(); + virtual void invalidateRect(RECT *r); + virtual void invalidateRgn(api_region *rgn); + virtual void validate(); + virtual void validateRect(RECT *r); + virtual void validateRgn(api_region *rgn); + +private: + + void initDDraw(); + void startThread(); + void stopThread(); + + LPDIRECTDRAW m_lpDD; + LPDIRECTDRAWSURFACE m_lpRenderSurf, m_lpPrimSurf; + + DDSurfaceCanvas *fb_canvas; + int w, h; + LPDIRECTDRAWCLIPPER lpClipper; + static int allow_dd; + static int sleep_val; + static CRITICAL_SECTION cs; + static HANDLE thread; + static int quitthread; + static PtrList<DDrawWnd> ddlist; + + static unsigned int WINAPI renderThread(void *); +}; + +#endif + diff --git a/Src/Wasabi/api/wnd/wndclass/editwnd.cpp b/Src/Wasabi/api/wnd/wndclass/editwnd.cpp new file mode 100644 index 00000000..8bde39d8 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/editwnd.cpp @@ -0,0 +1,1001 @@ +#include <precomp.h> +#include "editwnd.h" + +#include <tataki/canvas/canvas.h> +#include <api/wnd/notifmsg.h> + +#include <bfc/assert.h> + +#define ID_EDITCHILD 12 + +enum { IDLETIMER = 8, DELETETIMER = 10 }; +#define IDLETIME 350 // comprimises suck ;) + + +#if UTF8 +#ifdef WANT_UTF8_WARNINGS +#pragma CHAT("mig", "all", "UTF8 is enabled in editwnd.cpp -- Things might be screwy till it's all debugged?") +#endif +# include <bfc/string/encodedstr.h> +#endif + +#ifdef WIN32 +#include <commctrl.h> +#endif + + +#ifdef WIN32 +static LRESULT CALLBACK static_editWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + EditWnd *editwnd = (EditWnd *)GetWindowLongPtrW(hWnd, GWLP_USERDATA); + if (editwnd == NULL) return DefWindowProcW(hWnd, uMsg, wParam, lParam); + return editwnd->editWndProc(hWnd, uMsg, wParam, lParam); +} +#endif + +EditWnd::EditWnd(wchar_t *buffer, int buflen) +{ + wantfocus = 1; + nextenterfaked = 0; + idleenabled = 1; + beforefirstresize = 1; + editWnd = NULL; + prevWndProc = NULL; + setBuffer(buffer, buflen); + maxlen = 0; + retcode = EDITWND_RETURN_NOTHING; + modal = 0; + autoenter = 0; + autoselect = 0; + outbuf = NULL; + // bordered = 0; + idletimelen = IDLETIME; + multiline = 0; + readonly = 0; + password = 0; + autohscroll = 1; + autovscroll = 1; + vscroll = 0; +#ifdef WIN32 + oldbrush = NULL; +#endif +#ifdef LINUX + selstart = selend = 0; + cursorpos = 0; + selectmode = 0; + viewstart = 0; +#endif +#ifdef WASABI_EDITWND_LISTCOLORS + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text")) + textcolor = L"wasabi.list.text"; + else + textcolor = L"wasabi.edit.text"; + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.background")) + backgroundcolor = L"wasabi.list.background"; + else + textcolor.setColor(WASABI_API_SKIN->skin_getBitmapColor(L"wasabi.list.background")); +#else + backgroundcolor = "wasabi.edit.background"; + textcolor = "wasabi.edit.text"; +#endif + selectioncolor = L"wasabi.edit.selection"; + setVirtual(0); +} + +EditWnd::~EditWnd() +{ + killTimer(IDLETIMER); +#ifdef WIN32 + if (oldbrush != NULL) + DeleteObject(oldbrush); + oldbrush = NULL; + if (editWnd != NULL) + { + SetWindowLong(editWnd, GWLP_USERDATA, (LONG_PTR)0); + SetWindowLongPtrW(editWnd, GWLP_WNDPROC, (LONG_PTR)prevWndProc); + DestroyWindow(editWnd); + } +#endif + notifyParent(ChildNotify::RETURN_CODE, retcode); +} + +int EditWnd::onInit() +{ + EDITWND_PARENT::onInit(); + +#ifdef WIN32 + RECT r = clientRect(); + + editWnd = CreateWindowW(L"EDIT", NULL, + WS_CHILD + | (autohscroll ? ES_AUTOHSCROLL : 0) + | (readonly ? ES_READONLY : 0) + | (multiline ? ES_MULTILINE : 0) + | (password ? ES_PASSWORD : 0) + | (autovscroll ? ES_AUTOVSCROLL : 0) + | (vscroll ? WS_VSCROLL : 0), + r.left, r.top, r.right - r.left, r.bottom - r.top, + gethWnd(), (HMENU)ID_EDITCHILD, + getOsModuleHandle(), NULL); + ASSERT(editWnd != NULL); + + if ((maxlen != 0) && (outbuf != NULL)) + { + setBuffer(outbuf, maxlen); + } + + // stash a pointer to us + SetWindowLongPtrW(editWnd, GWLP_USERDATA, (LONG_PTR)this); + // subclass the edit control -- either by 8 or by 16 + + prevWndProc = (WNDPROC)SetWindowLongPtrW(editWnd, GWLP_WNDPROC, (LONG_PTR)static_editWndProc); + + + SendMessageW(editWnd, WM_SETFONT, (WPARAM)GetStockObject(ANSI_VAR_FONT), FALSE); + ShowWindow(editWnd, !getStartHidden() ? SW_NORMAL : SW_HIDE); +#endif + + return 1; +} + +void EditWnd::onSetVisible(int show) +{ + EDITWND_PARENT::onSetVisible(show); + if (editWnd == NULL) return ; +#ifdef WIN32 + ShowWindow(editWnd, show ? SW_NORMAL : SW_HIDE); +#endif +} + +int EditWnd::onPaint(Canvas *canvas) +{ + // if (!bordered) return EDITWND_PARENT::onPaint(canvas); + + PaintCanvas paintcanvas; + if (canvas == NULL) + { + if (!paintcanvas.beginPaint(this)) return 0; + canvas = &paintcanvas; + } + EDITWND_PARENT::onPaint(canvas); + + RECT r; + getClientRect(&r); + canvas->fillRect(&r, backgroundcolor); //SKIN + +#ifdef LINUX + char *str = STRDUP((const char *)inbuf + viewstart); + canvas->setTextColor(textcolor); + canvas->setTextSize(r.bottom - r.top); + canvas->setTextOpaque(FALSE); + char save; + if (selstart != selend) + { + RECT selrect = r; + int start = MAX(MIN(selstart, selend) - viewstart, 0); + int end = MAX(MAX(selstart, selend) - viewstart, 0); + + save = str[ start ]; + str[start] = '\0'; + selrect.left = r.left + canvas->getTextWidth(str); + str[start] = save; + + save = str[ end ]; + str[end] = '\0'; + selrect.right = r.left + canvas->getTextWidth(str); + str[end] = save; + + canvas->fillRect(&selrect, selectioncolor); + } + + save = str[cursorpos - viewstart]; + str[cursorpos - viewstart] = '\0'; + RECT cursor = r; + cursor.left = cursor.right = r.left + canvas->getTextWidth(str); + str[cursorpos - viewstart] = save; + canvas->drawRect(&cursor, TRUE, 0xffffff); + + canvas->textOut(r.left, r.top, r.right - r.left, r.bottom - r.top, str); + + FREE(str); +#endif + + return 1; +} + +int EditWnd::onResize() +{ + EDITWND_PARENT::onResize(); +#ifdef WIN32 + RECT r = clientRect(); + if (1 /*bordered*/) + { + r.top++; + r.bottom--; + r.left++; + r.right--; + } + MoveWindow(editWnd, r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE); + + if (beforefirstresize) + { + ShowWindow(editWnd, SW_NORMAL); beforefirstresize = 0; + if (modal) + { + SetFocus(editWnd); + if (getAutoSelect()) + SendMessageW(editWnd, EM_SETSEL, 0, -1); + } + } +#endif + + return TRUE; +} + +#ifdef WIN32 +LRESULT EditWnd::wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_CTLCOLOREDIT: + { + HDC hdc = (HDC)wParam; + SetTextColor(hdc, textcolor); + SetBkColor(hdc, backgroundcolor); + if (oldbrush != NULL) + { + DeleteObject(oldbrush); + oldbrush = NULL; + } + oldbrush = CreateSolidBrush(backgroundcolor); + return (LRESULT)oldbrush; + } + + case WM_MOUSEACTIVATE: + WASABI_API_WND->popupexit_check(this); + break; + + case WM_COMMAND: + { + switch (HIWORD(wParam)) + { + case EN_CHANGE: + { + if (maxlen > 0) + { + GetWindowTextW(editWnd, outbuf, maxlen); + onEditUpdate(); + } + } + break; + case EN_SETFOCUS: + if (getAutoSelect()) + SendMessageW(editWnd, EM_SETSEL, (WPARAM)0, (LPARAM) - 1); + break; + case EN_KILLFOCUS: + onLoseFocus(); + break; + } + } + break; + } + + return EDITWND_PARENT::wndProc(hWnd, uMsg, wParam, lParam); +} +#endif + +void EditWnd::setBuffer(wchar_t *buffer, int len) +{ +#ifdef LINUX + if (buffer == NULL || len <= 1) + { + inbuf = ""; + return ; + } +#endif + if (buffer == NULL || len <= 1) return ; + ASSERT(len > 1); + ASSERT(len < 0x7ffe); + ASSERT((int)wcslen(buffer) <= len); + +#ifdef WIN32 +#define USE_INTERNAL_BUFFER 0 + +#if USE_INTERNAL_BUFFER + buffer8.setSize(len + 1); + outbuf = buffer8.getMemory(); + if (len) + { + STRNCPY(outbuf, buffer, len); + } + outbuf[len] = 0; +#else + outbuf = buffer; +#endif + + if (editWnd != NULL) + { + SetWindowTextW(editWnd, buffer); + + // This is going to be problematic. This is where utf8 sucks. + // Just how many characters CAN we save in our buffer, eh? + // (shrug) Oh well. Can't be helped. At most this many. + SendMessageW(editWnd, EM_LIMITTEXT, (WPARAM)len - 1, (LPARAM)0); + // hooray for halcyon7 + + /* if (getAutoSelect()) { + SetFocus(editWnd); + SendMessageW(editWnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1); + }*/ + } + + maxlen = len; +#else + outbuf = buffer; + maxlen = len; + inbuf = buffer; + cursorpos = len; + invalidate(); +#endif +} + +void EditWnd::selectAll() +{ +#ifdef WIN32 + PostMessage(editWnd, EM_SETSEL, 0, -1); +#else + selstart = 0; selend = inbuf.len(); +#endif +} + +void EditWnd::enter() +{ + onEnter(); +} + +void EditWnd::getBuffer(wchar_t *buf, int _len) +{ + if (_len > maxlen) _len = maxlen; + // SendMessageW(editWnd, WM_GETTEXT, (WPARAM)_len, (LPARAM)buf); + WCSCPYN(buf, outbuf, _len); +} + +void EditWnd::setModal(int _modal) +{ + modal = _modal; +} + +void setBorder(int border) +{ + // bordered = border; +} + +int EditWnd::isEditorKey(int vk) +{ + if (vk >= VK_F1) return 0; + if ((vk == VK_UP || vk == VK_DOWN) || ((Std::keyDown(VK_CONTROL) || Std::keyDown(VK_MENU)) && (vk == VK_LEFT || vk == VK_RIGHT))) + return 0; + if (vk == VK_RETURN && Std::keyDown(VK_CONTROL)) return 0; + if (vk == VK_CONTROL || vk == VK_MENU) return 0; + return 1; +} + +LRESULT EditWnd::editWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_KEYDOWN: + if (!isEditorKey((int)wParam) && !onKeyDown((int)wParam)) + { +#ifdef WASABI_COMPILE_WND + WASABI_API_WND->forwardOnKeyDown(this, (int) wParam, (int)lParam); +#endif + + } + break; + case WM_KEYUP: + if (!isEditorKey((int)wParam) && !onKeyUp((int)wParam)) + { +#ifdef WASABI_COMPILE_WND + WASABI_API_WND->forwardOnKeyUp(this, (int) wParam, (int)lParam); +#endif + + } + break; + case WM_CHAR: + if (!(wParam == VK_RETURN && nextenterfaked && !autoenter)) + { + notifyParent(ChildNotify::EDITWND_KEY_PRESSED, wParam); + onChar((TCHAR)wParam); + } + if (wParam == VK_RETURN) + { + if (!(nextenterfaked && !autoenter)) + if (onEnter()) return 0; + nextenterfaked = 0; + return 0; + } + else if (wParam == VK_ESCAPE) + { + if (onAbort()) return 0; + } + else if (wParam == VK_TAB && multiline) + { + return 0; + } + break; + case WM_SETFOCUS: + onSetRootFocus(this); + // fall thru + case WM_KILLFOCUS: + invalidate(); + break; + } +#ifdef WIN32 + return CallWindowProc(prevWndProc, hWnd, uMsg, wParam, lParam); +#else + DebugString("portme -- EditWnd::editWndProc\n"); + return 0; +#endif +} + +void EditWnd::timerCallback(int id) +{ + switch (id) + { + case IDLETIMER: + killTimer(IDLETIMER); + if (idleenabled) onIdleEditUpdate(); + break; + case DELETETIMER: + killTimer(DELETETIMER); + delete this; + break; + default: + EDITWND_PARENT::timerCallback(id); + } +} + +void EditWnd::onEditUpdate() +{ +#ifdef LINUX + STRNCPY(outbuf, inbuf, maxlen); + outbuf[maxlen] = '\0'; + + RECT r; + getClientRect(&r); + + SysCanvas sysc; + sysc.setTextSize(r.bottom - r.top); + sysc.getTextWidth(inbuf); + + char *str = STRDUP(inbuf); + + if (cursorpos < viewstart) + viewstart = cursorpos; + + char save = str[cursorpos]; + str[cursorpos] = '\0'; + while (sysc.getTextWidth(str + viewstart) > r.right - r.left) + { + viewstart++; + } + str[cursorpos] = save; + + invalidate(); +#endif + killTimer(IDLETIMER); + setTimer(IDLETIMER, idletimelen); + notifyParent(ChildNotify::EDITWND_DATA_MODIFIED); +} + +void EditWnd::onIdleEditUpdate() +{ + notifyParent(ChildNotify::EDITWND_DATA_MODIFIED_ONIDLE); +} + +int EditWnd::onEnter() +{ + notifyParent(ChildNotify::EDITWND_ENTER_PRESSED); + if (modal) + { + retcode = EDITWND_RETURN_OK; + delete this; + //CUT setTimer(DELETETIMER, 1); + return 1; + } + return 0; +} + +int EditWnd::onAbort() +{ + notifyParent(ChildNotify::EDITWND_CANCEL_PRESSED); + if (modal) + { + retcode = EDITWND_RETURN_CANCEL; + delete this; + //CUT setTimer(DELETETIMER, 1); + return 1; + } + return 0; +} + +int EditWnd::onLoseFocus() +{ // fake an onEnter() +#ifdef WIN32 + if (autoenter) + { + nextenterfaked = 1; + PostMessage(editWnd, WM_CHAR, VK_RETURN, 0); + } +#else + invalidate(); + selstart = selend = 0; +#endif + return 0; +} + +void EditWnd::setAutoEnter(int a) +{ + autoenter = a; +} + +void EditWnd::setAutoSelect(int a) +{ + autoselect = a; +}; + +void EditWnd::setIdleTimerLen(int ms) +{ + if (ms < 0) ms = 0; + idletimelen = ms; +} + +int EditWnd::getTextLength() +{ // TOTALLY NONPORTABLE AND TOTALLY DIRTY +#ifdef WIN32 + HFONT font = (HFONT)SendMessageW(editWnd, WM_GETFONT, 0, 0); + + HDC sdc = GetDC(NULL); + HDC dc = CreateCompatibleDC(sdc); + ReleaseDC(NULL, sdc); + + HFONT oldfont = (HFONT)SelectObject(dc, font); + + SIZE s; + GetTextExtentPoint32W(dc, outbuf, wcslen(outbuf), &s); + SelectObject(dc, oldfont); + DeleteDC(dc); + return s.cx + SendMessageW(editWnd, EM_GETMARGINS, 0, 0)*2 + 2; +#else + if (inbuf.isempty()) + return 0; + + RECT r; + getClientRect(&r); + + SysCanvas sysc; + sysc.setTextSize(r.bottom - r.top); + return sysc.getTextWidth(inbuf); +#endif +} + +HWND EditWnd::getEditWnd() +{ + return editWnd; +} + +#ifndef WIN32 +enum { + ES_MULTILINE, + ES_WANTRETURN, + ES_AUTOHSCROLL, + ES_AUTOVSCROLL, + WS_VSCROLL, +}; +#endif + +void EditWnd::setMultiline(int ml) +{ + multiline = ml; + setStyle(ES_MULTILINE | ES_WANTRETURN, ml); +} + +void EditWnd::setReadOnly(int ro) +{ + readonly = ro; + setStyle(ES_READONLY, ro); +} + +void EditWnd::setPassword(int pw) +{ + password = pw; + setStyle(ES_PASSWORD, pw); +} + +void EditWnd::setAutoHScroll(int hs) +{ + autohscroll = hs; + setStyle(ES_AUTOHSCROLL, hs); +} + +void EditWnd::setAutoVScroll(int vs) +{ + autovscroll = vs; + setStyle(ES_AUTOVSCROLL, vs); +} + +void EditWnd::setVScroll(int vs) +{ + vscroll = vs; + setStyle(WS_VSCROLL, vs); +} + +void EditWnd::setStyle(LONG style, int set) +{ +#ifdef WIN32 + if (editWnd) + { + LONG s = GetWindowLong(editWnd, GWL_STYLE); + if (set) s |= style; + else s &= ~style; + SetWindowLong(editWnd, GWL_STYLE, s); + } +#else + DebugString("portme -- EditWnd::setStyle\n"); +#endif +} + +int EditWnd::onGetFocus() +{ + int r = EDITWND_PARENT::onGetFocus(); +#ifdef WIN32 + if (editWnd != NULL) + SetFocus(editWnd); +#endif + return r; +} + +int EditWnd::wantFocus() +{ + return wantfocus; +} + +int EditWnd::gotFocus() +{ + return (GetFocus() == editWnd); +} + +void EditWnd::setBackgroundColor(COLORREF c) +{ + backgroundcolor.setColor(c); +} + +void EditWnd::setTextColor(COLORREF c) +{ + textcolor.setColor(c); +} + +void EditWnd::invalidate() +{ + EDITWND_PARENT::invalidate(); + InvalidateRect(editWnd, NULL, TRUE); +} + +#ifdef LINUX +int EditWnd::textposFromCoord(int x, int y) +{ + RECT r; + getClientRect(&r); + + SysCanvas canvas; + + canvas.setTextColor(textcolor); + canvas.setTextSize(r.bottom - r.top); + canvas.setTextOpaque(FALSE); + + x -= r.left; + + int i; + + char *str = STRDUP(inbuf); + + if (x > canvas.getTextWidth(str + viewstart)) + return inbuf.len(); + + for (i = viewstart + 1; str[i]; i++) + { + char save = str[i]; + str[i] = '\0'; + if (x < canvas.getTextWidth(str + viewstart)) + { + str[i] = save; + break; + } + str[i] = save; + } + + FREE(str); + + return i - 1; +} + +int EditWnd::onLeftButtonDown(int x, int y) +{ + EDITWND_PARENT::onLeftButtonDown(x, y); + + // Add check for double/triple click... + + cursorpos = textposFromCoord(x, y); + selstart = selend = cursorpos; + + selectmode = 1; + + return 1; +} + +int EditWnd::onLeftButtonUp(int x, int y) +{ + EDITWND_PARENT::onLeftButtonUp(x, y); + + selectmode = 0; + + return 1; +} + +int EditWnd::onMouseMove(int x, int y) +{ + EDITWND_PARENT::onMouseMove(x, y); + + switch (selectmode) + { + case 0: + // Do nothing + break; + case 1: + selend = textposFromCoord(x, y); + cursorpos = selend; + onEditUpdate(); + break; + default: + DebugString("selectmode %d not available\n", selectmode); + break; + } + + return selectmode; +} + +int EditWnd::onKeyDown(int key) +{ + EDITWND_PARENT::onKeyDown(key); + + if (Std::keyDown(VK_CONTROL)) + { + switch (key) + { + case 'a': + case 'A': + selectAll(); + break; + + default: + return 0; + } + } + else + { + switch (key) + { + case XK_Home: + if (Std::keyDown(VK_SHIFT)) + { + if (selstart == selend) + { + selstart = selend = cursorpos; + } + selend = 0; + } + else + { + selstart = selend = 0; + } + cursorpos = 0; + break; + + case XK_End: + if (Std::keyDown(VK_SHIFT)) + { + if (selstart == selend) + { + selstart = selend = cursorpos; + } + selend = inbuf.len(); + } + else + { + selstart = selend = 0; + } + cursorpos = inbuf.len(); + break; + + case XK_Right: + if (Std::keyDown(VK_SHIFT)) + { + if (selstart == selend) + { + selstart = selend = cursorpos; + } + selend++; + if (selend > inbuf.len()) selend = inbuf.len(); + } + else + { + selstart = selend = 0; + } + cursorpos++; + if (cursorpos > inbuf.len()) cursorpos = inbuf.len(); + break; + + case XK_Left: + if (Std::keyDown(VK_SHIFT)) + { + if (selstart == selend) + { + selstart = selend = cursorpos; + } + selend--; + if (selend < 0) selend = 0; + } + else + { + selstart = selend = 0; + } + cursorpos--; + if (cursorpos < 0) cursorpos = 0; + break; + + case XK_Escape: + onAbort(); + break; + + case XK_Return: + onEnter(); + break; + + case XK_Delete: + if (selstart != selend) + { + int start = MIN(selstart, selend); + int end = MAX(selstart, selend); + + String add; + + if (end < inbuf.len()) + { + add = (const char *)inbuf + end; + } + else + { + add = ""; + } + + inbuf.trunc(start); + inbuf += add; + + cursorpos = start; + selstart = selend = 0; + + } + else + { + if (cursorpos >= 0) + { + if (cursorpos < inbuf.len() - 1) + { + String tmp = inbuf; + tmp.trunc(cursorpos); + inbuf = tmp + ((const char *)inbuf + cursorpos + 1); + } + else if (cursorpos == inbuf.len() - 1) + { + inbuf.trunc(cursorpos); + } + } + } + break; + + case VK_BACK: + if (selstart != selend) + { + int start = MIN(selstart, selend); + int end = MAX(selstart, selend); + + String add; + + if (end < inbuf.len()) + { + add = (const char *)inbuf + end; + } + else + { + add = ""; + } + + inbuf.trunc(start); + inbuf += add; + + cursorpos = start; + selstart = selend = 0; + } + else + { + if (cursorpos > 0) + { + if (cursorpos >= inbuf.len()) + { + inbuf.trunc(cursorpos - 1); + cursorpos--; + } + else + { + String tmp = inbuf; + tmp.trunc(cursorpos - 1); + inbuf = tmp + ((const char *)inbuf + cursorpos); + cursorpos--; + } + } + } + break; + + default: + if (key < 0x20 || key > 0x7e) + return 0; + + if (selstart != selend) + { + int start = MIN(selstart, selend); + int end = MAX(selstart, selend); + + String add; + + if (end < inbuf.len()) + { + add = (const char *)inbuf + end; + } + else + { + add = ""; + } + + inbuf.trunc(start); + inbuf += add; + + cursorpos = start; + selstart = selend = 0; + } + + String tmp; + + if (cursorpos >= inbuf.len()) + { + tmp = ""; + } + else + { + tmp = (const char *)inbuf + cursorpos; + } + + inbuf.trunc(cursorpos); + + inbuf += (char)key; + inbuf += tmp; + + cursorpos++; + } + } + + onEditUpdate(); + + return 1; +} +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/editwnd.h b/Src/Wasabi/api/wnd/wndclass/editwnd.h new file mode 100644 index 00000000..735debc2 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/editwnd.h @@ -0,0 +1,134 @@ +//NONPORTABLE +#ifndef _EDITWND_H +#define _EDITWND_H + +#include <api/wnd/wndclass/guiobjwnd.h> +#include <tataki/color/skinclr.h> +#include <api/wnd/usermsg.h> +#include <bfc/common.h> + +#define EDITWND_PARENT GuiObjectWnd +class EditWnd : public EDITWND_PARENT { +public: + EditWnd(wchar_t *buffer=NULL, int buflen=0); + virtual ~EditWnd(); + + virtual int onInit(); + virtual int onPaint(Canvas *canvas); + virtual int onResize(); +#ifdef WIN32 + virtual LRESULT wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +#endif + + // mig: Made these virtual to allow to be accessed by + // EditWndString object in editwndstring.h + virtual void setBuffer(wchar_t *buffer, int len); + virtual void getBuffer(wchar_t *outbuf, int len); + + virtual const wchar_t *getBufferPtr() { return outbuf; } + virtual int getBufferLength() { return maxlen; } + virtual void setBackgroundColor(ARGB32 c); + virtual void setTextColor(ARGB32 c); + + void setModal(int modal); //if modal, deletes self on enter + void setAutoEnter(int a); //fake an onEnter event when lose focus + int getAutoEnter() { return autoenter; } + void setAutoSelect(int a); //true==grab the focus on init + void setIdleTimerLen(int ms); // how many ms keys are idle before send msg + virtual void onSetVisible(int show); + virtual int onGetFocus(); + virtual int wantFocus(); + virtual void setWantFocus(int w) { wantfocus = w; } + virtual void selectAll(); + virtual void enter(); + virtual void setIdleEnabled(int i) { idleenabled = i; } + virtual int getIdleEnabled() { return idleenabled; } + + void setBorder(int border); + int getTextLength(); + + HWND getEditWnd(); + virtual int handleRatio() { return 0; } + virtual int getAutoSelect() { return autoselect; } + + void setMultiline(int ml); + void setReadOnly(int ro); + void setPassword(int pw); + void setAutoHScroll(int hs); + void setAutoVScroll(int vs); + void setVScroll(int vs); + int isEditorKey(int vk); + virtual void invalidate(); + + virtual int gotFocus(); + + // the wndproc for the edit box + virtual LRESULT editWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +protected: + virtual void timerCallback(int id); + + // call down on these if you override them + virtual void onEditUpdate(); + virtual void onIdleEditUpdate(); + virtual int onEnter(); // user hit enter.. return 1 to close window + virtual int onAbort(); // user hit escape.. return 1 to close window + virtual int onLoseFocus(); // different from onKillFocus() from BaseWnd! + + void setStyle(LONG style, int set); + +#ifdef LINUX + virtual int onLeftButtonDown( int x, int y ); + virtual int onLeftButtonUp( int x, int y ); + virtual int onMouseMove( int x, int y ); + virtual int onKeyDown(int key); +#endif + +private: +#ifdef LINUX + int textposFromCoord( int x, int y ); +#endif + + HWND editWnd; + WNDPROC prevWndProc; + int maxlen; + int retcode; + int idletimelen; + int modal; + int bordered; + int autoenter; + int beforefirstresize; + int autoselect; + int multiline; + int readonly; + int password; + int idleenabled; + int autohscroll,autovscroll,vscroll; + int nextenterfaked; + SkinColor backgroundcolor, textcolor, selectioncolor; +#ifdef LINUX + int selstart, selend; + int cursorpos; + int selectmode; + int viewstart; +#endif +#ifdef WIN32 + HBRUSH oldbrush; +#endif + + // Basically, we're redoing the functionality of EditWndString + // (the bigger version), so we'll probably erase EditWndString + // completely as an object. + MemBlock<wchar_t> buffer8; + wchar_t *outbuf; + int wantfocus; +#ifdef LINUX + StringW inbuf; +#endif +}; + +#define EDITWND_RETURN_NOTHING 0 // user didn't do nothing +#define EDITWND_RETURN_OK 1 // user hit return +#define EDITWND_RETURN_CANCEL 2 // user hit escape or something + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/editwndstring.cpp b/Src/Wasabi/api/wnd/wndclass/editwndstring.cpp new file mode 100644 index 00000000..f296bda8 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/editwndstring.cpp @@ -0,0 +1,17 @@ +#include <precomp.h> +#include "editwndstring.h" + +// =========================================================================== +// +// NULLSOFT WASABI SDK MODULE & EXAMPLE CODE +// +// File: editwndstring.cpp +// +//!## Purpose: The EditWndString object both extends the EditWnd object to +//!## simplify and streamline the use of an EditWnd in your wasabi +//!## components. In addition, this module serves as a tutorial +//!## to instruct wasabi developers on how to properly extend our +//!## current objects to provide new functionality. +// +// + diff --git a/Src/Wasabi/api/wnd/wndclass/editwndstring.h b/Src/Wasabi/api/wnd/wndclass/editwndstring.h new file mode 100644 index 00000000..843524bf --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/editwndstring.h @@ -0,0 +1,24 @@ +#ifndef _EDITWNDSTRING_H +#define _EDITWNDSTRING_H + +#include <api/wnd/wndclass/editwnd.h> +#include <bfc/memblock.h> + +class EditWndString : public EditWnd +{ +public: + void setBuffer(wchar_t *buffer, int len=0) + { + b.setSize(len+1); + wchar_t *bufmem=b.getMemory(); + if(len) + wcsncpy(bufmem,buffer,len); + bufmem[len]=0; + EditWnd::setBuffer(bufmem,len); + } + const wchar_t *getBuffer() { return b.getMemory(); } +private: + MemBlock<wchar_t> b; +}; + +#endif//_EDITWNDSTRING_H
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/embeddedxui.cpp b/Src/Wasabi/api/wnd/wndclass/embeddedxui.cpp new file mode 100644 index 00000000..8e47f29e --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/embeddedxui.cpp @@ -0,0 +1,143 @@ +#include "precomp.h" +#include "embeddedxui.h" + +EmbeddedXuiObject::EmbeddedXuiObject() { + embedded = NULL; + myxuihandle = newXuiHandle(); + getScriptObject()->vcpu_setInterface(embeddedXuiGuid, (void *)static_cast<EmbeddedXuiObject *>(this)); + getScriptObject()->vcpu_setClassName(L"ObjectEmbedded"); // this is the script class name + getScriptObject()->vcpu_setController(embeddedXuiController); +} + +EmbeddedXuiObject::~EmbeddedXuiObject() { + paramlist.deleteAll(); +} + +void EmbeddedXuiObject::onNewContent() { + embeddedxui_onNewEmbeddedContent(); +} + +void EmbeddedXuiObject::embeddedxui_onNewEmbeddedContent() { + embedded = NULL; + const wchar_t *id = embeddedxui_getEmbeddedObjectId(); + if (id != NULL && *id) { + GuiObject *myself = getGuiObject(); + embedded = myself->guiobject_findObject(id); + if (embedded != NULL && embedded != myself) { + foreach(paramlist) + EmbeddedXuiObjectParam *p = paramlist.getfor(); + embedded->guiobject_setXmlParam(p->param, p->value); + endfor; +#ifdef WASABI_COMPILE_CONFIG + syncCfgAttrib(); +#endif + } + } +} + +int EmbeddedXuiObject::onUnknownXuiParam(const wchar_t *xmlattributename, const wchar_t *value) { + int r = EMBEDDEDXUIOBJECT_PARENT::onUnknownXuiParam(xmlattributename, value); + paramlist.addItem(new EmbeddedXuiObjectParam(xmlattributename, value)); + if (embedded) + r = embedded->guiobject_setXmlParam(xmlattributename, value); + return r; +} + +int EmbeddedXuiObject::onInit() +{ + int r = EMBEDDEDXUIOBJECT_PARENT::onInit(); + const wchar_t *id = embeddedxui_getContentId(); + if (id != NULL && *id) + setContent(id); + return r; +} + +#ifdef WASABI_COMPILE_CONFIG +int EmbeddedXuiObject::onReloadConfig() { + int r = EMBEDDEDXUIOBJECT_PARENT::onReloadConfig(); + syncCfgAttrib(); + return r; +} +#endif + +#ifdef WASABI_COMPILE_CONFIG +void EmbeddedXuiObject::syncCfgAttrib() +{ + if (embedded == NULL) return; + CfgItem *item = getGuiObject()->guiobject_getCfgItem(); + const wchar_t *attrib = getGuiObject()->guiobject_getCfgAttrib(); + if (item != embedded->guiobject_getCfgItem() || + attrib != embedded->guiobject_getCfgAttrib()) { + embedded->guiobject_setCfgAttrib(item, attrib); + } +} +#endif + +// ----------------------------------------------------------------------- +// Script Object + +EmbeddedXuiScriptController _embeddedXuiController; +EmbeddedXuiScriptController *embeddedXuiController = &_embeddedXuiController; + +// -- Functions table ------------------------------------- +function_descriptor_struct EmbeddedXuiScriptController::exportedFunction[] = { + {L"getEmbeddedObject", 0, (void*)EmbeddedXuiScriptController::EmbeddedXui_getEmbeddedObject}, +}; + +ScriptObject *EmbeddedXuiScriptController::instantiate() { + EmbeddedXuiObject *ex = new EmbeddedXuiObject; + ASSERT(ex != NULL); + return ex->getScriptObject(); +} + +void EmbeddedXuiScriptController::destroy(ScriptObject *o) { + EmbeddedXuiObject *ex= static_cast<EmbeddedXuiObject *>(o->vcpu_getInterface(embeddedXuiGuid)); + ASSERT(ex != NULL); + delete ex; +} + +void *EmbeddedXuiScriptController::encapsulate(ScriptObject *o) { + return NULL; // no encapsulation for DropDownlist yet +} + +void EmbeddedXuiScriptController::deencapsulate(void *o) { +} + +int EmbeddedXuiScriptController::getNumFunctions() { + return sizeof(exportedFunction) / sizeof(function_descriptor_struct); +} + +const function_descriptor_struct *EmbeddedXuiScriptController::getExportedFunctions() { + return exportedFunction; +} + + +scriptVar EmbeddedXuiScriptController::EmbeddedXui_getEmbeddedObject(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) { + SCRIPT_FUNCTION_INIT + EmbeddedXuiObject *ex = static_cast<EmbeddedXuiObject*>(o->vcpu_getInterface(embeddedXuiGuid)); + ScriptObject *_o = NULL; + if (ex) { + GuiObject *go = ex->embeddedxui_getEmbeddedObject(); + if (go != NULL) + _o = go->guiobject_getScriptObject(); + } + return MAKE_SCRIPT_OBJECT(_o); +} + +ScriptObject *EmbeddedXuiScriptController::cast(ScriptObject *o, GUID g) { + EmbeddedXuiObject *exo = static_cast<EmbeddedXuiObject *>(o->vcpu_getInterface(embeddedXuiGuid)); + if (!exo) return NULL; + GuiObject *go = exo->embeddedxui_getEmbeddedObject(); + if (go != NULL) { + ScriptObject *eo = go->guiobject_getScriptObject(); + if (eo != NULL) { + void *i = eo->vcpu_getInterface(g); + if (i != NULL) + return eo; + } + } + return NULL; +} + + +ScriptObjectController *EmbeddedXuiScriptController::getAncestorController() { return WASABI_API_MAKI->maki_getController(guiObjectGuid); }
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/embeddedxui.h b/Src/Wasabi/api/wnd/wndclass/embeddedxui.h new file mode 100644 index 00000000..38b187d5 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/embeddedxui.h @@ -0,0 +1,88 @@ +#ifndef __EMBEDDEDXUIOBJECT_H +#define __EMBEDDEDXUIOBJECT_H + +#include <wasabicfg.h> +#include <api/script/api_maki.h> +#include <api/wnd/wndclass/guiobjwnd.h> +#include <api/script/scriptguid.h> +#include <api/script/scriptobj.h> +#include <api/script/objcontroller.h> + +#define EMBEDDEDXUIOBJECT_PARENT GuiObjectWnd + + +class EmbeddedXuiObjectParam { + public: + + + EmbeddedXuiObjectParam(const wchar_t *p, const wchar_t *v) : param(p), value(v) {} + + virtual ~EmbeddedXuiObjectParam() {} + + StringW param; + StringW value; +}; + + +class EmbeddedXuiObject : public EMBEDDEDXUIOBJECT_PARENT { + public: + + EmbeddedXuiObject(); + + + virtual ~EmbeddedXuiObject(); + + virtual void onNewContent(); + + virtual int onInit(); + + + virtual void embeddedxui_onNewEmbeddedContent(); + virtual const wchar_t *embeddedxui_getContentId() { return NULL; } + virtual const wchar_t *embeddedxui_getEmbeddedObjectId() { return NULL; } + + virtual int onUnknownXuiParam(const wchar_t *xmlattributename, const wchar_t *value); + + +#ifdef WASABI_COMPILE_CONFIG + virtual int onReloadConfig(); +#endif + virtual GuiObject *embeddedxui_getEmbeddedObject() { return embedded; } + + private: + +#ifdef WASABI_COMPILE_CONFIG + void syncCfgAttrib(); +#endif + + int myxuihandle; + PtrList<EmbeddedXuiObjectParam> paramlist; + GuiObject *embedded; +}; + +// ----------------------------------------------------------------------- +class EmbeddedXuiScriptController: public ScriptObjectControllerI +{ +public: + virtual const wchar_t *getClassName() { return L"ObjectEmbedder"; } + virtual const wchar_t *getAncestorClassName() { return L"GuiObject"; } + virtual ScriptObjectController *getAncestorController(); + virtual int getNumFunctions(); + virtual const function_descriptor_struct *getExportedFunctions(); + virtual GUID getClassGuid() { return embeddedXuiGuid; } + virtual ScriptObject *instantiate(); + virtual void destroy(ScriptObject *o); + virtual void *encapsulate(ScriptObject *o); + virtual void deencapsulate(void *o); + virtual ScriptObject *cast(ScriptObject *o, GUID g); + +private: + + static function_descriptor_struct exportedFunction[]; + static scriptVar EmbeddedXui_getEmbeddedObject(SCRIPT_FUNCTION_PARAMS, ScriptObject *o); +}; + +extern COMEXP EmbeddedXuiScriptController *embeddedXuiController; + + +#endif
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/foreignwnd.cpp b/Src/Wasabi/api/wnd/wndclass/foreignwnd.cpp new file mode 100644 index 00000000..e6e490e3 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/foreignwnd.cpp @@ -0,0 +1,60 @@ +#include "precomp.h" +#include "foreignwnd.h" + +PtrListQuickSorted<ForeignWndProc, ForeignWndProcComparator> ForeignWnd::foreignwndprocs; + +LRESULT CALLBACK foreignWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + ForeignWndProc *wp = ForeignWnd::foreignwndprocs.findItem((const wchar_t *)hwnd); + if (wp) + { + if (uMsg == WM_SHOWWINDOW) // more ? + wp->wnd->wndProc(hwnd, uMsg, wParam, lParam); + return CallWindowProc(wp->oldWindowProc, hwnd, uMsg, wParam, lParam); + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +ForeignWnd::ForeignWnd(OSWINDOWHANDLE wndhandle, OSMODULEHANDLE module, int subclass) +{ + thishwnd = wndhandle; + setOSModuleHandle(module); + +#ifdef EXPERIMENTAL_INDEPENDENT_AOT + DebugString("ForeignWnd::ForeignWnd(OSWINDOWHANDLE wndhandle): There might be problems with GWL_USERDATA assumptions when EXPERIMENTAL_INDEPENDENT_AOT is on, lone should audit\n"); +#endif + + // access protected basewnd member + hwnd = wndhandle; + + setForeignWnd(1); + + if (subclass) + { + oldWindowProc = (WNDPROC) GetWindowLongPtrW(wndhandle, GWLP_WNDPROC); + wndprocentry = new ForeignWndProc; + wndprocentry->wnd = this; + wndprocentry->hwnd = thishwnd; + wndprocentry->oldWindowProc = oldWindowProc; + foreignwndprocs.addItem(wndprocentry); + SetWindowLongPtrW(thishwnd, GWLP_WNDPROC, (LONG_PTR)foreignWindowProc); + } + else + { + oldWindowProc = NULL; + wndprocentry = NULL; + } +} + +ForeignWnd::~ForeignWnd() +{ + if (wndprocentry && oldWindowProc) + { + foreignwndprocs.removeItem(wndprocentry); + delete wndprocentry; + wndprocentry = NULL; + SetWindowLongPtrW(thishwnd, GWLP_WNDPROC, (LONG_PTR)oldWindowProc); + oldWindowProc = NULL; + } +} + diff --git a/Src/Wasabi/api/wnd/wndclass/foreignwnd.h b/Src/Wasabi/api/wnd/wndclass/foreignwnd.h new file mode 100644 index 00000000..023cb6b1 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/foreignwnd.h @@ -0,0 +1,56 @@ +#ifndef __FOREIGNWND_H +#define __FOREIGNWND_H + +#ifdef _WIN32 +#include <api/wnd/basewnd.h> + +class ForeignWnd; + +class ForeignWndProc +{ +public: + ForeignWnd *wnd; + HWND hwnd; + WNDPROC oldWindowProc; +}; + +class ForeignWndProcComparator +{ +public: + // comparator for sorting + static int compareItem(ForeignWndProc *p1, ForeignWndProc* p2) + { + return CMP3(p1->hwnd, p2->hwnd); + } + // comparator for searching + static int compareAttrib(const wchar_t *attrib, ForeignWndProc *item) + { + return CMP3((HWND)attrib, item->hwnd); + } +}; + +class ForeignWnd : public BaseWnd +{ +public: + // takes over an existing OSWINDOWHANDLE and wraps a BaseWnd around it + // but does not changes the windowproc, nor does it inserts the wnd + // into the system list. It does not either (under windows anyway) + // sets the userdata windowlong to the object pointer. + // if subclass is true, we subclas the windowproc and process events + // as if the window was a real rootwnd + + ForeignWnd(OSWINDOWHANDLE w, OSMODULEHANDLE m, int subclass = 0); + + virtual ~ForeignWnd(); + + static PtrListQuickSorted<ForeignWndProc, ForeignWndProcComparator> foreignwndprocs; + +private: + WNDPROC oldWindowProc; + HWND thishwnd; + ForeignWndProc *wndprocentry; +}; +#else +#warning port me +#endif +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/framewnd.cpp b/Src/Wasabi/api/wnd/wndclass/framewnd.cpp new file mode 100644 index 00000000..26e56537 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/framewnd.cpp @@ -0,0 +1,777 @@ +#include <precomp.h> + +#include "framewnd.h" +#include <api/wnd/notifmsg.h> +#include <bfc/bfc_assert.h> +#include <tataki/canvas/canvas.h> +#include <api/wnd/PaintCanvas.h> +#include <bfc/wasabi_std_wnd.h> + +#define DC_FWFOCUS 0x5122 + +FrameWnd::FrameWnd() +{ +// sizer = NULL; + SNAP=1; + snapoffsety=0; + snapoffsetx=0; + nchild = 0; + for (int i = 0; i < MAXCHILD; i++) { + children[i] = NULL; + rwchildren[i] = NULL; + hidey[i] = 0; + windowshaded[i] = 0; + } + vert = DIVIDER_UNDEFINED; + divideside = SDP_FROMLEFT; + pullbarpos = PULLBAR_HALF; + minwidth = PULLBAR_QUARTER-PULLBAR_EIGHTH; + maxwidth = PULLBAR_HALF; + resizeable = 0; + slidemode = FRAMEWND_SQUISH; + prevpullbarpos = -1; + maxpixels=0; + minpixels=0; + noMaxRestriction = false; + ZERO(sizerRect); + + h_bitmap = L"wasabi.framewnd.horizontaldivider"; + v_bitmap = L"wasabi.framewnd.verticaldivider"; + h_grabber = L"wasabi.framewnd.horizontalgrabber"; + v_grabber = L"wasabi.framewnd.verticalgrabber"; + ws_bitmap = L"wasabi.framewnd.windowshade"; + resizing = 0; +} + +void FrameWnd::Set_v_bitmap(const wchar_t *new_v_bitmap) +{ + v_bitmap=new_v_bitmap; + if (isInited()) + { + invalidate(); + onResize(); + } +} + +void FrameWnd::Set_v_grabber(const wchar_t *new_v_grabber) + { + v_grabber=new_v_grabber; + if (isInited()) + { + invalidate(); + onResize(); + } +} + +FrameWnd::~FrameWnd() { +#ifdef WASABI_COMPILE_CONFIG + if (getId() != NULL) { + StringPrintfW buf(L"FrameWnd/ws,%s", getId()); + WASABI_API_CONFIG->setIntPrivate(buf, windowshaded[0]); + } +#endif + + if (children[0]) // do we have any basewnd ? + for (int i = 0; i < nchild; i++) delete children[i]; +} + +int FrameWnd::onInit() { + int i; + + FRAMEWND_PARENT::onInit(); + + ASSERT(vert != DIVIDER_UNDEFINED || nchild == 0); + + // have to set children for frame windows + + // fill in members + nchild = 0; + + // make children create their windows + for (i = 0; i < MAXCHILD; i++) { + if (rwchildren[i] != NULL) { + if (rwchildren[i]->init(this) != 0) { + rwchildren[i]->setParent(this); + nchild++; + } + } + } + prevpullbarpos = pullbarpos; + + if (nchild >= MAXCHILD) { + int which = (divideside == SDP_FROMLEFT) ? 0 : 1; + rwchildren[which]->bringToFront(); + } + +#ifdef WASABI_COMPILE_CONFIG + if (getId() != NULL) { + StringPrintfW buf(L"FrameWnd/ws,%s", getId()); + int ws = WASABI_API_CONFIG->getIntPrivate(buf, /*rwchildren[0] && rwchildren[0]->childNotify(NULL, CHILD_WINDOWSHADE_CAPABLE)*/ 0); + if (ws) { + windowshade(0, !ws); + windowshade(0, ws); + pullbarpos = 0; + } + } +#endif + + return 1; +} + +int FrameWnd::getCursorType(int x, int y) { + RECT r; + getClientRect(&r); + POINT pt={x,y}; + if (y > r.top + getLabelHeight() && Wasabi::Std::pointInRect(sizerRect, pt)) { + if (vert == DIVIDER_HORIZONTAL) return BASEWND_CURSOR_NORTHSOUTH; + else return BASEWND_CURSOR_EASTWEST; + } + return BASEWND_CURSOR_POINTER; +} + +int FrameWnd::setChildren(BaseWnd *newchild1, BaseWnd *newchild2) { + return _setChildren(newchild1, newchild2, newchild1, newchild2); +} + +int FrameWnd::setChildrenRootWnd(ifc_window *child1, ifc_window *child2/* =NULL */) { + return _setChildren(child1, child2, NULL, NULL); +} + +int FrameWnd::_setChildren(ifc_window *child1, ifc_window *child2, BaseWnd *child1b, BaseWnd *child2b) { + if (child1b) { // we can delete them later + children[0] = child1b; + children[1] = child2b; + } + rwchildren[0] = child1; + rwchildren[1] = child2; + nchild = 0; + if (rwchildren[0] != NULL) nchild++; + if (rwchildren[1] != NULL) nchild++; + + ASSERTPR(nchild >= 1, "framewnd must have one or more children"); + + if (isInited()) { + invalidate(); + onResize(); + } + return nchild; +} + +ifc_window *FrameWnd::enumChild(int which) { + if (which < 0 || which >= MAXCHILD) return NULL; + return rwchildren[which]; +} + +int FrameWnd::childNotify(ifc_window *which, int msg, intptr_t param1, intptr_t param2) { +// ASSERT(which == rwchildren[0] || which == rwchildren[1] || which == NULL); + switch (msg) { + case ChildNotify::FRAMEWND_SETTITLEWIDTH: + if (pullbarpos == param1) return 0; + ASSERT(param1 >= 0); + if (which == rwchildren[0]) { + // rwchildren[1]->invalidate(); //FG> removed due to change in redraw layout + // rwchildren[1]->repaint(); + ASSERT(divideside == SDP_FROMLEFT); + } else { + // rwchildren[0]->invalidate(); + // rwchildren[0]->repaint(); + ASSERT(divideside == SDP_FROMRIGHT); + } + pullbarpos = param1; + // do it + onResize(); + return 1; + + case ChildNotify::HIDEYHIDEY: + if (which == rwchildren[0]) hidey[0] = 1; + else if (which == rwchildren[1]) hidey[1] = 1; + which->setVisible(FALSE); + onResize(); + return 1; + + case ChildNotify::UNHIDEYHIDEY: + if (which == rwchildren[0]) hidey[0] = 0; + else if (which == rwchildren[1]) hidey[1] = 0; + which->setVisible(TRUE); + onResize(); + return 1; + + case ChildNotify::FRAMEWND_QUERY_SLIDE_MODE: + return getSlideMode(); + + case ChildNotify::FRAMEWND_SET_SLIDE_MODE: + setSlideMode((FrameWndSlideMode)param1); + break; + case ChildNotify::GOTFOCUS: + case ChildNotify::KILLFOCUS: + invalidateLabel(); + break; + } + + return FRAMEWND_PARENT::childNotify(which, msg, param1, param2); +} + +/*int FrameWnd::forceFocus() { + if (!canShowFocus()) return 0; // we aren't showing a label + int v = 0; + if (nchild > 0 && rwchildren[0] != NULL) { + if (!rwchildren[0]->canShowFocus()) v |= rwchildren[0]->gotFocus(); + } + if (nchild > 1 && rwchildren[1] != NULL) { + if (!rwchildren[1]->canShowFocus()) v |= rwchildren[1]->gotFocus(); + } + return v; +}*/ + +void FrameWnd::setDividerType(FrameWndDividerType type) { + vert = type; + ASSERT(vert == DIVIDER_VERTICAL || vert == DIVIDER_HORIZONTAL); + if (isInited()) + onResize(); +} + +FrameWndDividerType FrameWnd::getDividerType() { + return vert; +} + +int FrameWnd::ConvertPixToProp() { + RECT r; + int w; + getClientRect(&r); + if(vert == DIVIDER_VERTICAL) { + w = r.right-r.left; + } else { + w = r.bottom-r.top; + } + w = (pullbarpos * PULLBAR_FULL) / w; + return w; +} + +int FrameWnd::convertPropToPix(int prop) { + RECT r; + int w; + + getClientRect(&r); + if(vert == DIVIDER_VERTICAL) { + w = r.right-r.left; + } else { + w = r.bottom-r.top; + } + return (w * prop) / PULLBAR_FULL; +} + +int FrameWnd::setDividerPosNoCfg(int from, int pos) { + divideside = from; + + ASSERT(pos >= 0); + pullbarpos = pos; + if (isInited()) + onResize(); + + StringPrintfW buf(L"FrameWnd/%s,p", getId()); + WASABI_API_CONFIG->setIntPrivate(buf, pullbarpos); + + return 1; +} + +int FrameWnd::setDividerPos(int from, int pos) { +#ifdef WASABI_COMPILE_CONFIG + if (getId() != NULL) { + StringPrintfW buf(L"FrameWnd/%s,p", getId()); + pos = WASABI_API_CONFIG->getIntPrivate(buf, pos); + if (pos <= 0) pos = 0; + else if (pos >= PULLBAR_FULL) pos = PULLBAR_FULL; + } +#endif + return setDividerPosNoCfg(from, pos); +} + +void FrameWnd::getDividerPos(int *from, int *pos) { + if (from != NULL) *from = divideside; + if (pos != NULL) *pos = pullbarpos; +} + +int FrameWnd::setResizeable(int is) { + int prev = resizeable; + resizeable = is; + return prev; +} + +void FrameWnd::setMinWidth(int min) { + //ASSERT(min >= 0); + minpixels = min; +} + +void FrameWnd::setMaxWidth(int max) +{ + //ASSERT(max >= 0); + maxpixels=max; + noMaxRestriction = (max == 0); + //maxwidth = max; +} + +void FrameWnd::setSlideMode(FrameWndSlideMode mode) { + slidemode = mode; + if (isInited()) + onResize(); +} + +FrameWndSlideMode FrameWnd::getSlideMode() { + return slidemode; +} + +int FrameWnd::dragEnter(ifc_window *sourceWnd) { + ifc_window *ch = getWindowShadedChild(); + if (ch == NULL) return FRAMEWND_PARENT::dragEnter(sourceWnd); + return ch->getDragInterface()->dragEnter(sourceWnd); +} + +int FrameWnd::dragOver(int x, int y, ifc_window *sourceWnd) { + ifc_window *ch = getWindowShadedChild(); + if (ch == NULL) return FRAMEWND_PARENT::dragOver(x, y, sourceWnd); + return ch->getDragInterface()->dragOver(-1, -1, sourceWnd); +} + +int FrameWnd::dragLeave(ifc_window *sourceWnd) { + ifc_window *ch = getWindowShadedChild(); + if (ch == NULL) return FRAMEWND_PARENT::dragLeave(sourceWnd); + return ch->getDragInterface()->dragLeave(sourceWnd); +} + +int FrameWnd::dragDrop(ifc_window *sourceWnd, int x, int y) { + ifc_window *ch = getWindowShadedChild(); + if (ch == NULL) return FRAMEWND_PARENT::dragDrop(sourceWnd, x, y); + return ch->getDragInterface()->dragDrop(sourceWnd, x, y); +} + +int FrameWnd::onResize() +{ + int rt = FRAMEWND_PARENT::onResize(); + if (!isInited()) return rt; + RECT r; + int sizerwidth = SIZERWIDTH; + + if (!isInited()) { + prevpullbarpos = pullbarpos; + return 1; // no window to resize + } + + getClientRect(&r); + + ASSERT(nchild >= 0); + if (nchild == 0) { + prevpullbarpos = pullbarpos; + return 1; + } + + if (hidey[0] && hidey[1]) return 0; // both windows are hiding + + // if we have only one child, it takes up all the room + if (hidey[0]) { + rwchildren[1]->resize(r.left, r.top, r.right-r.left, r.bottom-r.top); + return 1; + } else if (hidey[1]) { + rwchildren[0]->resize(r.left, r.top, r.right-r.left, r.bottom-r.top); + return 1; + } + + if (nchild == 1) { + if (rwchildren[0] != NULL) rwchildren[0]->resize(r.left, r.top, r.right-r.left, r.bottom-r.top); + else if (rwchildren[1] != NULL) rwchildren[1]->resize(r.left, r.top, r.right-r.left, r.bottom-r.top); + return 1; + } + +#ifdef ASSERTS_ENABLED + for (int i = 0; i < nchild; i++) { + ASSERT(rwchildren[i] != NULL); + } +#endif + + if (!resizeable) sizerwidth = 0; + + // resize the subwindows + + int w; + if (vert == DIVIDER_VERTICAL) { + w = r.right-r.left; + } else { + w = r.bottom-r.top; + } + int clientwidth = w; // the logical width + + switch (pullbarpos) { + case PULLBAR_FULL: /*w = w;*/ break; + case PULLBAR_HALF: w = w/2; break; + case PULLBAR_QUARTER: w = w/4; break; + case PULLBAR_THREEQUARTER: w = w - w/4; break; + case PULLBAR_EIGHTH: w = w/8; break; + default: w = pullbarpos; break; + } + + // maxpixels holds normally a negative or zero value! + if (divideside == SDP_FROMRIGHT) + { + w = (clientwidth - w); + if (maxpixels < 1 && w < -maxpixels) w = -maxpixels; // Martin> This fixes an ugly drawing overlap + // TODO: check non-relative width as well, imoh we should rewrite this function from scrap. + } + else // FROMLEFT + { + if (maxpixels < 1 && w > clientwidth + maxpixels) + w = clientwidth + maxpixels; + if (w < minpixels) + w = minpixels; + } + + RECT r1, r2; + + if (slidemode == FRAMEWND_COVER) { // cover mode + + ASSERTPR(vert == DIVIDER_VERTICAL, "finish implementing"); + + if (divideside == SDP_FROMRIGHT) { + Wasabi::Std::setRect(&r1, r.left, r.top, r.right-r.left, r.bottom-r.top); //FG> delay resize + Wasabi::Std::setRect(&r2, r.left+w, r.top, r.left+clientwidth - w, r.bottom-r.top); + } else { + Wasabi::Std::setRect(&r1, r.left, r.top, r.left+w, r.bottom-r.top); //FG> delay resize + Wasabi::Std::setRect(&r2, r.left, r.top, r.right-r.left, r.bottom-r.top); + } + + sizerRect.top = r.top; + sizerRect.bottom = r.bottom; + sizerRect.left = r.left + w; + sizerRect.right = r.left + w + sizerwidth; + + } else { // squish mode + // left-right + if (vert == DIVIDER_VERTICAL) { + sizerRect.top = r.top; + sizerRect.bottom = r.bottom; + if (divideside == SDP_FROMLEFT) { // from left + + //FG> Warning, this is using a rect for x,y,W,H and NOT l,r,t,b + Wasabi::Std::setRect(&r1, r.left, r.top, w, r.bottom-r.top); + Wasabi::Std::setRect(&r2, r.left+w+sizerwidth, r.top, (r.right-r.left)-(w+sizerwidth), r.bottom-r.top); + + sizerRect.left = r.left+w; + sizerRect.right = sizerRect.left + sizerwidth; + } + else { // from right + + //FG> Warning, this is using a rect for x,y,W,H and NOT l,r,t,b + Wasabi::Std::setRect(&r1, r.left, r.top, w-sizerwidth, r.bottom-r.top); + Wasabi::Std::setRect(&r2, r.left+w, r.top, (r.right-r.left)-w, r.bottom-r.top); + + sizerRect.left = r.left+w-sizerwidth; + sizerRect.right = r.left+w; + } + } else { + // top-bottom + + //FG> Warning, this is using a rect for x,y,W,H and NOT l,r,t,b + Wasabi::Std::setRect(&r1, r.left, r.top, r.right-r.left, w); + Wasabi::Std::setRect(&r2, r.left, r.top+w+sizerwidth, r.right-r.left, (r.bottom-r.top)-(w+sizerwidth)); + + sizerRect.top = r.top+w; + sizerRect.bottom = r.top+w+sizerwidth; + sizerRect.left = r.left; + sizerRect.right = r.right; + } + } + + //FG> Choose resizing order. optimizes redraw by avoiding temporary overlap of rwchildren + bool reverse = false; + if (vert == DIVIDER_VERTICAL) { + RECT o; + rwchildren[0]->getNonClientRect(&o); + reverse = (r1.right > o.right); + } else { + RECT o; + rwchildren[0]->getNonClientRect(&o); + reverse = (r1.bottom > o.bottom); + } + + //FG> actually resize rwchildren + //FG> Warning, this is using a rect for x,y,W,H and NOT l,r,t,b + if (reverse) { + rwchildren[1]->resize(r2.left, r2.top, r2.right, r2.bottom); + rwchildren[0]->resize(r1.left, r1.top, r1.right, r1.bottom); + } else { + rwchildren[0]->resize(r1.left, r1.top, r1.right, r1.bottom); + rwchildren[1]->resize(r2.left, r2.top, r2.right, r2.bottom); + } + + onResizeChildren(r1, r2); + +// RECT ri = sizerRect; +#if 0 + if (vert == DIVIDER_HORIZONTAL) { + ri.left -= 2; + ri.right += 2; + } else { + ri.top -= 2; + ri.bottom += 2; + } +#endif +// invalidateRect(&ri); + invalidate(); + repaint(); + + prevpullbarpos = pullbarpos; + + return 1; +} + +void FrameWnd::onResizeChildren(RECT leftr, RECT rightr) { +} + +int FrameWnd::onPaint(Canvas *canvas) { + + RECT d; + getClientRect(&d); + if ((d.left >= d.right) || (d.top >= d.bottom)) { + return FRAMEWND_PARENT::onPaint(canvas); + } + + RECT cr; +// PaintBltCanvas paintcanvas; + PaintCanvas paintcanvas; + + // if only 1 child, we don't paint anything + if (nchild <= 1) return FRAMEWND_PARENT::onPaint(canvas); + + if (canvas == NULL) { + if (!paintcanvas.beginPaint(this)) return 0; + canvas = &paintcanvas; + } + FRAMEWND_PARENT::onPaint(canvas); + + getClientRect(&cr); + + if (wantRenderBaseTexture() || !isVirtual()) + renderBaseTexture(canvas, cr); + + if (resizeable) { + RECT r = sizerRect; + if (vert == DIVIDER_HORIZONTAL) { + r.left -= 2; + r.right += 2; + } else { + r.top -= 2; + r.bottom += 2; + } + + AutoSkinBitmap &bitmap = (vert == DIVIDER_VERTICAL) ? v_bitmap : h_bitmap; + bitmap.stretchToRectAlpha(canvas, &r); + + if (vert == DIVIDER_VERTICAL) { + int h = sizerRect.bottom - sizerRect.top; + int gh = v_grabber.getHeight(); + if (h > gh) { + RECT rr = sizerRect; + rr.top += (h - gh) / 2; + rr.bottom -= (h - gh) / 2; + v_grabber.stretchToRectAlpha(canvas, &rr); + } + } else { + int w = sizerRect.right - sizerRect.left; + int gw = h_grabber.getWidth(); + if (w > gw) { + RECT rr = sizerRect; + rr.left += (w - gw) / 2; + rr.right -= (w - gw) / 2; + h_grabber.stretchToRectAlpha(canvas, &rr); + } + } + + if (windowshaded[0]) { + RECT wr = cr; + if (vert == DIVIDER_VERTICAL) { + wr.right = r.left; + } else if (vert == DIVIDER_HORIZONTAL) { + wr.bottom = r.top; + } + + ws_bitmap.stretchToRect(canvas, &wr); + } + + } + + return 1; +} + +int FrameWnd::onLeftButtonDown(int x, int y) { + FRAMEWND_PARENT::onLeftButtonDown(x, y); + if (!resizeable) return 1; + POINT p = { x, y }; + if (Wasabi::Std::pointInRect(sizerRect, p)) { + beginCapture(); + RECT r; + getClientRect(&r); + x -= r.left; + y -= r.top; + snapoffsety= y - (y % SNAP); + snapoffsetx= x - (x % SNAP); + resizing = 1; + return 1; + } + return 0; +} + +int FrameWnd::onMouseMove(int x, int y) { + int pos, mpos; + RECT r; + + if (!resizing) return 1; + + FRAMEWND_PARENT::onMouseMove(x,y); + + prevpullbarpos = pullbarpos; + + getClientRect(&r); + x -= r.left; + y -= r.top; + + if (vert == DIVIDER_VERTICAL) { + pos = r.right - r.left; + if ((x - (x % SNAP)) == snapoffsetx) + return 1; + mpos=x; + snapoffsetx=(x - (x % SNAP)); + } else { + pos = r.bottom - r.top; + if ((y - (y % SNAP)) == snapoffsety) + return 1; + mpos=y; + snapoffsety=y - (y % SNAP); + } + ASSERT(pos != 0); + if (mpos < 0) mpos = 0; + if (mpos > pos) mpos = pos; + + if(divideside == SDP_FROMLEFT) { + pullbarpos = mpos; + } else { + pullbarpos = pos-mpos; + } + + int realMinPixels; + if (minpixels) + { + realMinPixels=minpixels; + if (minpixels<0) + realMinPixels = (r.bottom - r.top) + minpixels; + } + else + realMinPixels = convertPropToPix(minwidth); + + if (divideside == SDP_FROMLEFT) + { + if (pullbarpos < realMinPixels) + { + if (rwchildren[0] != NULL && rwchildren[0]->childNotify(NULL, ChildNotify::FRAMEWND_WINDOWSHADE_CAPABLE, 0, 0)) { + pullbarpos = 0; + windowshade(0, TRUE); + } else { + pullbarpos = realMinPixels; + } + } else { + windowshade(0, FALSE); + } + } else if (divideside == SDP_FROMRIGHT) { + if (pullbarpos < realMinPixels) { + if (rwchildren[1] != NULL /* && rwchildren[1]->childNotify(NULL, CHILD_WINDOWSHADE_CAPABLE) */) { + pullbarpos = /*convertPropToPix(PULLBAR_FULL)-*/0; + windowshade(1, TRUE); + } else { + pullbarpos = realMinPixels; + } + } else { + windowshade(1, FALSE); + } + } + + if (!windowshaded[0] && !windowshaded[1]) { +// if (pullbarpos > pos-convertPropToPix(minwidth)) +// pullbarpos = pos-convertPropToPix(minwidth); + int realMaxPixels; + if (maxpixels || noMaxRestriction) + { + realMaxPixels=maxpixels; + if (maxpixels<0 || noMaxRestriction) + { + if (vert == DIVIDER_VERTICAL) + realMaxPixels = (r.right - r.left) + maxpixels; + else + realMaxPixels = (r.bottom - r.top) + maxpixels; + } + + } + else + realMaxPixels=convertPropToPix(maxwidth); + + if (pullbarpos > realMaxPixels) + pullbarpos = realMaxPixels; + } + + ASSERT(pullbarpos >= 0); + + if (pullbarpos != prevpullbarpos && isInited()) + onResize(); + + return 1; +} + +int FrameWnd::onLeftButtonUp(int x, int y) { + FRAMEWND_PARENT::onLeftButtonUp(x, y); + if (resizing) { + endCapture(); + resizing = 0; +#ifdef WASABI_COMPILE_CONFIG + if (getId() != NULL) { + StringPrintfW buf(L"FrameWnd/%s,p", getId()); + WASABI_API_CONFIG->setIntPrivate(buf, pullbarpos); + } +#endif + return 1; + } + return 0; +} + +void FrameWnd::windowshade(int which, int shaded) { + ASSERT(which == 0 || which == 1); + if (!!windowshaded[which] == !!shaded) return; + if (rwchildren[which] == NULL) return; + rwchildren[which]->childNotify(NULL, ChildNotify::FRAMEWND_WINDOWSHADE_ENABLE, shaded, 0); + windowshaded[which] = shaded; + rwchildren[which]->setVisible(!shaded); +} + +ifc_window *FrameWnd::getWindowShadedChild() { + if (nchild != 2) return NULL; + if (!(windowshaded[0] | windowshaded[1])) return NULL; + return windowshaded[0] ? rwchildren[0] : rwchildren[1]; +} + +int FrameWnd::onGetFocus() { + postDeferredCallback(DC_FWFOCUS, 0); + return 1; +} + +int FrameWnd::onDeferredCallback(intptr_t p1, intptr_t p2) { + switch (p1) { + case DC_FWFOCUS: + if (rwchildren[0]) rwchildren[0]->setFocus(); + break; + default: + return FRAMEWND_PARENT::onDeferredCallback(p1, p2); + } + return 1; +} + + +void FrameWnd::setSnap(int snap) +{ + if (snap>0) + SNAP=snap; +}
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/framewnd.h b/Src/Wasabi/api/wnd/wndclass/framewnd.h new file mode 100644 index 00000000..506426e6 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/framewnd.h @@ -0,0 +1,124 @@ +//NONPORTABLE +#ifndef _FRAMEWND_H +#define _FRAMEWND_H + +#include <bfc/common.h> +#include <api/wnd/wndclass/labelwnd.h> +#include <tataki/bitmap/autobitmap.h> + +#define MAXCHILD 2 // this had better never not be 2 + +typedef enum { + DIVIDER_HORIZONTAL, DIVIDER_VERTICAL, DIVIDER_UNDEFINED = -1 +} FrameWndDividerType; +enum { SDP_FROMLEFT, SDP_FROMRIGHT }; +#define SDP_FROMTOP SDP_FROMLEFT +#define SDP_FROMBOTTOM SDP_FROMRIGHT + +typedef enum { + FRAMEWND_SQUISH, + FRAMEWND_COVER +} FrameWndSlideMode; + +#define SIZERWIDTH 8 + +// this window holds other basewnd derived classes +#define FRAMEWND_PARENT LabelWnd +class FrameWnd : public FRAMEWND_PARENT { +public: + FrameWnd(); + virtual ~FrameWnd(); + + virtual int onInit(); + + virtual int getCursorType(int x, int y); + + virtual int onPaint(Canvas *canvas); + virtual int onResize(); + + virtual int onLeftButtonDown(int x, int y); + virtual int onMouseMove(int x, int y); // only called when mouse captured + virtual int onLeftButtonUp(int x, int y); + + virtual int childNotify(ifc_window *which, int msg, intptr_t param1, intptr_t param2); + +// virtual int forceFocus(); + + // unique to this class + int setChildren(BaseWnd *child1, BaseWnd *child2=NULL); + int setChildrenRootWnd(ifc_window *child1, ifc_window *child2=NULL); + ifc_window *enumChild(int which); + // horizontal or vertical? + void setDividerType(FrameWndDividerType type); + FrameWndDividerType getDividerType(); + // where is the divider? + int setDividerPos(int from, int pos); + // this version doesn't check the cfg file for last position + int setDividerPosNoCfg(int from, int pos); + void getDividerPos(int *from, int *pos); + + int setResizeable(int is); + void setMinWidth(int min); + void setMaxWidth(int max); + void setSnap(int snap); + + virtual int onGetFocus(); + virtual int onDeferredCallback(intptr_t p1, intptr_t p2); + + // cover or squish + void setSlideMode(FrameWndSlideMode mode); + FrameWndSlideMode getSlideMode(); + + virtual void onResizeChildren(RECT leftr, RECT rightr); + + // drag and drops are forwarded into windowshaded windows + virtual int dragEnter(ifc_window *sourceWnd); + virtual int dragOver(int x, int y, ifc_window *sourceWnd); + virtual int dragLeave(ifc_window *sourceWnd); + virtual int dragDrop(ifc_window *sourceWnd, int x, int y); + +protected: + int convertPropToPix(int prop); + int ConvertPixToProp(); + + void windowshade(int which, int shaded); + ifc_window *getWindowShadedChild(); + + void Set_v_bitmap(const wchar_t *new_v_bitmap); + void Set_v_grabber(const wchar_t *new_v_grabber); + +private: + int _setChildren(ifc_window *child1, ifc_window *child2, BaseWnd *child1b, BaseWnd *child2b); + int nchild; + BaseWnd *children[MAXCHILD]; + ifc_window *rwchildren[MAXCHILD]; + int hidey[MAXCHILD]; + int windowshaded[MAXCHILD]; + + FrameWndDividerType vert; + + int resizeable; + FrameWndSlideMode slidemode; + + int divideside; + int pullbarpos; // 0..65536 + int prevpullbarpos; + int minwidth, maxwidth; + int maxpixels; + boolean noMaxRestriction; + int minpixels; + int snapoffsetx, snapoffsety; + int SNAP; + RECT sizerRect; + + AutoSkinBitmap h_bitmap, v_bitmap, h_grabber, v_grabber, ws_bitmap; + int resizing; +}; + +#define PULLBAR_FULL 65536L +#define PULLBAR_HALF (PULLBAR_FULL/2) +#define PULLBAR_QUARTER (PULLBAR_FULL/4) +#define PULLBAR_THREEQUARTER (PULLBAR_FULL-PULLBAR_QUARTER) +#define PULLBAR_EIGHTH (PULLBAR_FULL/8) + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/gradientwnd.cpp b/Src/Wasabi/api/wnd/wndclass/gradientwnd.cpp new file mode 100644 index 00000000..f34b31f9 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/gradientwnd.cpp @@ -0,0 +1,82 @@ +#include <precomp.h> + +#include "gradientwnd.h" + + +// NOTE +// works now ;) + +GradientWnd::GradientWnd() +: bitmap(4,4, getOsWindowHandle()) +{ + cache_w = cache_h = 4; + last_w = last_h = -1; + recreate = 1; + setReverseColors(TRUE); +} + +GradientWnd::~GradientWnd() +{ + WASABI_API_SYSCB->syscb_deregisterCallback(static_cast<SkinCallbackI*>(this)); +} + +int GradientWnd::onInit () +{ + int r = GRADIENTWND_PARENT::onInit(); + WASABI_API_SYSCB->syscb_registerCallback(static_cast<SkinCallbackI*>(this)); + return r; +} + +int GradientWnd::onPaint(Canvas *canvas) +{ + ASSERT(canvas != NULL); + RECT cr = clientRect(); + + int w = cr.right - cr.left, h = cr.bottom - cr.top; + if (w && h) + { + if (w != last_w || h != last_h) + { + recreate=1; + } + if (w > cache_w || h > cache_h) + { + cache_w = max(w, cache_w); + cache_h = max(h, cache_h); + // round up to nearest 4 + cache_w = (cache_w+3) & ~3; + cache_h = (cache_h+3) & ~3; + + bitmap.DestructiveResize(cache_w,cache_h,32); + recreate = 1; + } + + if (recreate) + { + ARGB32 *bits = static_cast<ARGB32*>(bitmap.getBits()); + renderGradient(bits, w, h, /*pitch=*/cache_w); + last_w = w; + last_h = h; + recreate=0; + } + RECT src = {0,0,w,h}; + bitmap./*getSkinBitmap()->*/blitToRect(canvas, &src, &cr, getPaintingAlpha()); + //bitmap./*getSkinBitmap()->*/blitAlpha(canvas, cr.left, cr.top, getPaintingAlpha()); + } + return 1; +} + +void GradientWnd::onParamChange() +{ + invalidate(); + recreate = 1; +} + +int GradientWnd::skincb_onColorThemeChanged(const wchar_t *newcolortheme) +{ + // TODO: This will refresh after ca 1 sec - we need an instand redraw + invalidate(); + recreate = 1; + return 0; +} + diff --git a/Src/Wasabi/api/wnd/wndclass/gradientwnd.h b/Src/Wasabi/api/wnd/wndclass/gradientwnd.h new file mode 100644 index 00000000..c7c02695 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/gradientwnd.h @@ -0,0 +1,31 @@ +#ifndef _GRADIENTWND_H +#define _GRADIENTWND_H + +#include <api/wnd/wndclass/guiobjwnd.h> +#include <bfc/draw/gradient.h> +#include <tataki/canvas/bltcanvas.h> + +#define GRADIENTWND_PARENT GuiObjectWnd +class GradientWnd : public GRADIENTWND_PARENT, public Gradient, public SkinCallbackI { +public: + GradientWnd(); + virtual ~GradientWnd(); + + virtual int onPaint(Canvas *canvas); + +protected: + virtual void onParamChange(); + +private: + int recreate; + + int last_w, last_h; + int cache_w, cache_h; + BltCanvas bitmap; + +protected: + int onInit(); + int skincb_onColorThemeChanged(const wchar_t *newcolortheme); +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/guiobjwnd.cpp b/Src/Wasabi/api/wnd/wndclass/guiobjwnd.cpp new file mode 100644 index 00000000..fcb70d0c --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/guiobjwnd.cpp @@ -0,0 +1,476 @@ +#include <precomp.h> +#include <api/script/api_maki.h> +#include <api/wnd/wndclass/guiobjwnd.h> +#include <api/script/scriptobj.h> +#include <api/script/scriptguid.h> +#include <api/service/svcs/svc_droptarget.h> +#include <api/wnd/notifmsg.h> +#include <api/script/objects/rootobject.h> +#include <api/locales/xlatstr.h> + +XMLParamPair GuiObjectWnd::params[] = + { + {GUIOBJECT_ACTIVEALPHA, L"ACTIVEALPHA"}, + {GUIOBJECT_ALPHA, L"ALPHA"}, + {GUIOBJECT_SETANCHOR, L"ANCHOR"}, +#ifdef USEAPPBAR + {GUIOBJECT_APPBAR, L"APPBAR"}, +#endif + {GUIOBJECT_CFGATTR, L"CFGATTRIB"}, + {GUIOBJECT_SETCURSOR, L"CURSOR"}, + {GUIOBJECT_DROPTARGET, L"DROPTARGET"}, + {GUIOBJECT_ENABLED, L"ENABLED"}, + {GUIOBJECT_FITTOPARENT, L"FITPARENT"}, + {GUIOBJECT_FOCUSONCLICK, L"FOCUSONCLICK"}, + {GUIOBJECT_GHOST, L"GHOST"}, + {GUIOBJECT_H, L"H"}, + {GUIOBJECT_ID, L"ID"}, + {GUIOBJECT_INACTIVEALPHA, L"INACTIVEALPHA"}, + {GUIOBJECT_MOVE, L"MOVE"}, + {GUIOBJECT_SETNOCONTEXTMENU, L"NOCONTEXTMENU"}, + {GUIOBJECT_SETNODBLCLICK, L"NODBLCLICK"}, + {GUIOBJECT_SETNOLEFTCLICK, L"NOLEFTCLICK"}, + {GUIOBJECT_SETNOMOUSEMOVE, L"NOMOUSEMOVE"}, + {GUIOBJECT_SETNORIGHTCLICK, L"NORIGHTCLICK"}, + {GUIOBJECT_NOTIFY, L"NOTIFY"}, + {GUIOBJECT_NOTIFY, L"NOTIFY0"}, + {GUIOBJECT_NOTIFY, L"NOTIFY1"}, + {GUIOBJECT_NOTIFY, L"NOTIFY2"}, + {GUIOBJECT_NOTIFY, L"NOTIFY3"}, + {GUIOBJECT_NOTIFY, L"NOTIFY4"}, + {GUIOBJECT_NOTIFY, L"NOTIFY5"}, + {GUIOBJECT_NOTIFY, L"NOTIFY6"}, + {GUIOBJECT_NOTIFY, L"NOTIFY7"}, + {GUIOBJECT_NOTIFY, L"NOTIFY8"}, + {GUIOBJECT_NOTIFY, L"NOTIFY9"}, + {GUIOBJECT_RECTRGN, L"RECTRGN"}, + {GUIOBJECT_SYSREGION, L"REGIONOP"}, + {GUIOBJECT_RELATH, L"RELATH"}, + {GUIOBJECT_RELATW, L"RELATW"}, + {GUIOBJECT_RELATX, L"RELATX"}, + {GUIOBJECT_RELATY, L"RELATY"}, + {GUIOBJECT_RENDERBASETEXTURE, L"RENDERBASETEXTURE"}, + {GUIOBJECT_SYSMETRICSX, L"SYSMETRICSX"}, + {GUIOBJECT_SYSMETRICSY, L"SYSMETRICSY"}, + {GUIOBJECT_SYSMETRICSW, L"SYSMETRICSW"}, + {GUIOBJECT_SYSMETRICSH, L"SYSMETRICSH"}, + {GUIOBJECT_SYSREGION, L"SYSREGION"}, + {GUIOBJECT_TABORDER, L"TABORDER"}, + {GUIOBJECT_TOOLTIP, L"TOOLTIP"}, + {GUIOBJECT_TRANSLATE, L"TRANSLATE"}, + {GUIOBJECT_USERDATA, L"USERDATA"}, + {GUIOBJECT_VISIBLE, L"VISIBLE"}, + {GUIOBJECT_W, L"W"}, + {GUIOBJECT_WANTFOCUS, L"WANTFOCUS"}, + {GUIOBJECT_X, L"X"}, + {GUIOBJECT_SETX1, L"X1"}, + {GUIOBJECT_SETX2, L"X2"}, + {GUIOBJECT_Y, L"Y"}, + {GUIOBJECT_SETY1, L"Y1"}, + {GUIOBJECT_SETY2, L"Y2"}, + }; + +GuiObjectWnd::GuiObjectWnd() +{ + my_gui_object = static_cast<GuiObject *>(WASABI_API_MAKI->maki_encapsulate(guiObjectGuid, getScriptObject())); + getScriptObject()->vcpu_setInterface(xmlObjectGuid, static_cast<XmlObject *>(this)); + getScriptObject()->vcpu_setInterface(guiObjectWndGuid, static_cast<GuiObjectWnd *>(this)); +#ifdef USEAPPBAR + getScriptObject()->vcpu_setInterface(appBarGuid, static_cast<AppBar*>(this)); +#endif + my_gui_object->guiobject_setRootWnd(this); + getScriptObject()->vcpu_setClassName(L"GuiObject"); + getScriptObject()->vcpu_setController(WASABI_API_MAKI->maki_getController(guiObjectGuid)); + cfg_reentry = 0; + xuihandle = newXuiHandle(); + CreateXMLParameters(xuihandle); +} + +void GuiObjectWnd::CreateXMLParameters(int master_handle) +{ + int numParams = sizeof(params) / sizeof(params[0]); + hintNumberOfParams(xuihandle, numParams); + for (int i = 0;i < numParams;i++) + addParam(xuihandle, params[i], XUI_ATTRIBUTE_IMPLIED); +} + +GuiObjectWnd::~GuiObjectWnd() +{ + WASABI_API_MAKI->maki_deencapsulate(guiObjectGuid, my_gui_object); + my_gui_object = NULL; +} + +const wchar_t *GuiObjectWnd::getTip() +{ + switch(wantTranslation()) + { + case 1: + return _(tip); + case 2: +return __(tip); + default: + return tip; + } +} + +int GuiObjectWnd::onRightButtonDown(int x, int y) +{ + if (!my_gui_object) return 1; + GUIOBJECTWND_PARENT::onRightButtonDown(x, y); + my_gui_object->guiobject_onRightButtonDown(x, y); + return 1; +} + +int GuiObjectWnd::onRightButtonUp(int x, int y) +{ + if (!my_gui_object) return 1; + GUIOBJECTWND_PARENT::onRightButtonUp(x, y); + my_gui_object->guiobject_onRightButtonUp(x, y); + return 1; +} + +int GuiObjectWnd::onLeftButtonDown(int x, int y) +{ + if (!my_gui_object) return 1; + GUIOBJECTWND_PARENT::onLeftButtonDown(x, y); + my_gui_object->guiobject_onLeftButtonDown(x, y); + return 1; +} + +int GuiObjectWnd::onLeftButtonUp(int x, int y) +{ + if (!my_gui_object) return 1; + GUIOBJECTWND_PARENT::onLeftButtonUp(x, y); + my_gui_object->guiobject_onLeftButtonUp(x, y); + return 1; +} + +int GuiObjectWnd::onMouseMove(int x, int y) +{ + if (!my_gui_object) return 1; + GUIOBJECTWND_PARENT::onMouseMove(x, y); + my_gui_object->guiobject_onMouseMove(x, y); + return 1; +} + +int GuiObjectWnd::wantTranslation() +{ + if (!my_gui_object) return 1; + return my_gui_object->guiobject_wantTranslation(); +} + +int GuiObjectWnd::onLeftButtonDblClk(int x, int y) +{ + if (!my_gui_object) return 1; + GUIOBJECTWND_PARENT::onLeftButtonDblClk(x, y); + my_gui_object->guiobject_onLeftButtonDblClk(x, y); + return 1; +} + +int GuiObjectWnd::onRightButtonDblClk(int x, int y) +{ + if (!my_gui_object) return 1; + GUIOBJECTWND_PARENT::onRightButtonDblClk(x, y); + my_gui_object->guiobject_onRightButtonDblClk(x, y); + return 1; +} + +// Martin> For the next two functions, we need to ensure that we don't kill volume change if nothing is done +int GuiObjectWnd::onMouseWheelDown (int click, int line) +{ + if (!my_gui_object) return 1; + int ret = GUIOBJECTWND_PARENT::onMouseWheelDown(click, line); + if (!ret) ret = my_gui_object->guiobject_onMouseWheelDown(click, line); + return ret; +} + +int GuiObjectWnd::onMouseWheelUp (int click, int line) +{ + if (!my_gui_object) return 1; + int ret = GUIOBJECTWND_PARENT::onMouseWheelUp(click, line); + if (!ret) ret = my_gui_object->guiobject_onMouseWheelUp(click, line); + return ret; +} + +int GuiObjectWnd::onResize() +{ + if (!my_gui_object) return 1; + GUIOBJECTWND_PARENT::onResize(); + if (!isInited()) return 1; + ifc_window *w = my_gui_object->guiobject_getRootWnd(); + RECT r; + w->getClientRect(&r); + my_gui_object->guiobject_onResize(r.left, r.top, r.right - r.left, r.bottom - r.top); + return 1; +} + +void GuiObjectWnd::onEnterArea() +{ + if (!my_gui_object) return ; + GUIOBJECTWND_PARENT::onEnterArea(); + my_gui_object->guiobject_onEnterArea(); +} + +void GuiObjectWnd::onLeaveArea() +{ + if (!my_gui_object) return ; + GUIOBJECTWND_PARENT::onLeaveArea(); + my_gui_object->guiobject_onLeaveArea(); +} + +void GuiObjectWnd::onSetVisible(int show) +{ + if (!my_gui_object) + return ; + + GUIOBJECTWND_PARENT::onSetVisible(show); + my_gui_object->guiobject_onSetVisible(show); +} + +int GuiObjectWnd::onEnable(int en) +{ + if (!my_gui_object) return 1; + GUIOBJECTWND_PARENT::onEnable(en); + my_gui_object->guiobject_onEnable(en); + return 1; +} + +GuiObject *GuiObjectWnd::getGuiObject() +{ + return my_gui_object; +} + +RootObject *GuiObjectWnd::getRootObject() +{ + return my_gui_object->guiobject_getRootObject(); +} + +int GuiObjectWnd::dragDrop(ifc_window *sourceWnd, int x, int y) +{ + int r = DropTargetEnum::throwDrop(my_gui_object->guiobject_getDropTarget(), sourceWnd, x, y); + if (r == 0) + { + ifc_window *p = getParent(); + if (p != NULL) + { + DragInterface *d = p->getDragInterface(); + if (d != NULL) + return d->dragDrop(sourceWnd, x, y); + } + } + return r; +} + +int GuiObjectWnd::dragEnter(ifc_window *sourceWnd) +{ + my_gui_object->guiobject_dragEnter(sourceWnd); + return 1; +} + +int GuiObjectWnd::dragOver(int x, int y, ifc_window *sourceWnd) +{ + my_gui_object->guiobject_dragOver(x, y, sourceWnd); + return 1; +} + +int GuiObjectWnd::dragLeave(ifc_window *sourceWnd) +{ + my_gui_object->guiobject_dragLeave(sourceWnd); + return 1; +} + +int GuiObjectWnd::onActivate() +{ + if (!my_gui_object) return 1; + GUIOBJECTWND_PARENT::onActivate(); + invalidate(); + return 1; +} + +int GuiObjectWnd::onDeactivate() +{ + if (!my_gui_object) return 1; + GUIOBJECTWND_PARENT::onDeactivate(); + invalidate(); + return 1; +} + +void GuiObjectWnd::onCancelCapture() +{ + if (!my_gui_object) return ; + GUIOBJECTWND_PARENT::onCancelCapture(); + my_gui_object->guiobject_onCancelCapture(); +} + +int GuiObjectWnd::setXuiParam(int _xuihandle, int attrid, const wchar_t *name, const wchar_t *value) +{ + if (_xuihandle == xuihandle) + { + switch (attrid) + { + case GUIOBJECT_FOCUSONCLICK: + setFocusOnClick(WTOI(value)); + break; + default: + getGuiObject()->guiobject_setXmlParamById(attrid, value); + break; + } + } + return 0; +} + +int GuiObjectWnd::onUnknownXmlParam(const wchar_t *param, const wchar_t *value) +{ + return onUnknownXuiParam(param, value); +} + +int GuiObjectWnd::setXmlParamById(int xmlhandle, int attrid, const wchar_t *name, const wchar_t *value) +{ + return setXuiParam(xmlhandle, attrid, name, value); +} + +void *GuiObjectWnd::getInterface(GUID interface_guid) +{ + void *r = GUIOBJECTWND_PARENT::getInterface(interface_guid); + if (r) return r; + return getRootObject()->rootobject_getScriptObject()->vcpu_getInterface(interface_guid); +} + +int GuiObjectWnd::onAction(const wchar_t *action, const wchar_t *param, int x, int y, intptr_t p1, intptr_t p2, void *data, size_t datalen, ifc_window *source) +{ +#ifdef WASABI_COMPILE_CONFIG + if (!_wcsicmp(action, L"reload_config") && isInited()) + { + if (cfg_reentry) return 1; + cfg_reentry = 1; + int r = onReloadConfig(); + cfg_reentry = 0; + return r; + } +#endif + int rt = GUIOBJECTWND_PARENT::onAction(action, param, x, y, p1, p2, data, datalen, source); + getGuiObject()->guiobject_onAction(action, param, x, y, p1, p2, data, datalen, source); + return rt; +} + +void GuiObjectWnd::setContent(const wchar_t *groupid_orguid, int autoresizefromcontent) +{ +#ifdef WASABI_COMPILE_SKIN + + // abstract_setAllowDeferredContent(0); + abstract_setContent(groupid_orguid, autoresizefromcontent); +#endif +} + +void GuiObjectWnd::setContentBySkinItem(SkinItem *item, int autoresizefromcontent) +{ + // abstract_setAllowDeferredContent(0); +#ifdef WASABI_COMPILE_SKIN + abstract_setContentBySkinItem(item, autoresizefromcontent); +#endif +} + +void GuiObjectWnd::abstract_onNewContent() +{ + +#ifdef WASABI_COMPILE_SKIN + GUIOBJECTWND_PARENT::abstract_onNewContent(); +#endif + onNewContent(); +#ifdef WASABI_COMPILE_CONFIG + if (getGuiObject()->guiobject_hasCfgAttrib()) + onReloadConfig(); +#endif +} + +GuiObject *GuiObjectWnd::findObject(const wchar_t *object_id) +{ + return getGuiObject()->guiobject_findObject(object_id); +} + +#ifdef WASABI_COMPILE_SCRIPT +ScriptObject *GuiObjectWnd::findScriptObject(const wchar_t *object_id) +{ + GuiObject *fo = getGuiObject()->guiobject_findObject(object_id); + if (fo != NULL) return fo->guiobject_getScriptObject(); + return NULL; +} +#endif +const wchar_t *GuiObjectWnd::getId() +{ + if (my_gui_object) + return my_gui_object->guiobject_getId(); + return GUIOBJECTWND_PARENT::getId(); +} + +int GuiObjectWnd::onPostOnInit() +{ + int r = GUIOBJECTWND_PARENT::onPostOnInit(); +#ifdef WASABI_COMPILE_CONFIG + if (getGuiObject()->guiobject_hasCfgAttrib()) + onReloadConfig(); +#endif + return r; +} + +int GuiObjectWnd::onInit() +{ + int r = GUIOBJECTWND_PARENT::onInit(); + getGuiObject()->guiobject_onInit(); + return r; +} + +int GuiObjectWnd::onChar(unsigned int c) +{ + if (!my_gui_object) return 1; + int r = GUIOBJECTWND_PARENT::onChar(c); + getGuiObject()->guiobject_onChar(c); + return r; +} + +int GuiObjectWnd::onKeyDown(int vkcode) +{ + if (!my_gui_object) return 1; + int r = GUIOBJECTWND_PARENT::onKeyDown(vkcode); + getGuiObject()->guiobject_onKeyDown(vkcode); + return r; +} + +int GuiObjectWnd::onKeyUp(int vkcode) +{ + if (!my_gui_object) return 1; + int r = GUIOBJECTWND_PARENT::onKeyUp(vkcode); + getGuiObject()->guiobject_onKeyUp(vkcode); + return r; +} + +int GuiObjectWnd::onGetFocus() +{ + if (!my_gui_object) return 1; + int r = GUIOBJECTWND_PARENT::onGetFocus(); + getGuiObject()->guiobject_onGetFocus(); + return r; +} + +int GuiObjectWnd::onKillFocus() +{ + if (!my_gui_object) return 1; + int r = GUIOBJECTWND_PARENT::onKillFocus(); + getGuiObject()->guiobject_onKillFocus(); + return r; +} + +int GuiObjectWnd::onAcceleratorEvent(const wchar_t *name) +{ + int r = GUIOBJECTWND_PARENT::onAcceleratorEvent(name); + getGuiObject()->guiobject_onAccelerator(name); + return r; +} + +int GuiObjectWnd::wantFocus() +{ + if (GUIOBJECTWND_PARENT::wantFocus()) return 1; + if (my_gui_object) + return my_gui_object->guiobject_wantFocus(); + return 0; +} diff --git a/Src/Wasabi/api/wnd/wndclass/guiobjwnd.h b/Src/Wasabi/api/wnd/wndclass/guiobjwnd.h new file mode 100644 index 00000000..f172c92f --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/guiobjwnd.h @@ -0,0 +1,237 @@ +#ifndef __GUIOBJWND_H +#define __GUIOBJWND_H + +#if defined(WIN32) || defined(WIN64) +#define USEAPPBAR +#endif + +#ifdef USEAPPBAR +#include <api/wnd/wndclass/appbarwnd.h> +#define GUIOBJECTWND_PARENT AppBarWnd +#else +#include <api/wnd/wndclass/clickwnd.h> +#define GUIOBJECTWND_PARENT ClickWnd +#endif + +#include <api/script/scriptobj.h> +#include <api/script/objects/guiobject.h> +#include <api/script/objects/rootobj.h> +#include <api/skin/xmlobject.h> +#include <api/service/svcs/svc_xuiobject.h> + +// {E5760861-5489-4ffc-BE02-061D9DA6CD1B} +const GUID guiObjectWndGuid = + { 0xe5760861, 0x5489, 0x4ffc, { 0xbe, 0x2, 0x6, 0x1d, 0x9d, 0xa6, 0xcd, 0x1b } }; + +#define XUI_ATTRIBUTE_IMPLIED XML_ATTRIBUTE_IMPLIED +#define XUI_ATTRIBUTE_REQUIRED XML_ATTRIBUTE_REQUIRED + +class GuiObjectWnd : public GUIOBJECTWND_PARENT, public RootObjectInstance, public XmlObjectI +{ +public: + GuiObjectWnd(); + virtual ~GuiObjectWnd(); + +#ifdef WASABI_COMPILE_CONFIG + virtual int onReloadConfig() { return 1; } +#endif + + // XmlObject + + virtual int setXmlParamById(int xmlhandle, int attrid, const wchar_t *name, const wchar_t *value); + virtual int onUnknownXmlParam(const wchar_t *param, const wchar_t *value); + virtual int newXuiHandle() { return newXmlHandle(); } + + // ClickWnd + + virtual int onRightButtonDown(int x, int y); + virtual int onRightButtonUp(int x, int y); + virtual int onLeftButtonDown(int x, int y); + virtual int onLeftButtonUp(int x, int y); + virtual int onMouseMove(int x, int y); + virtual int onLeftButtonDblClk(int x, int y); + virtual int onRightButtonDblClk(int x, int y); + virtual int onMouseWheelUp(int click, int lines); + virtual int onMouseWheelDown(int click, int lines); + virtual int onResize(); + virtual int onActivate(); + virtual int onDeactivate(); + virtual void onEnterArea(); + virtual void onLeaveArea(); + virtual void onSetVisible(int show); + virtual int onEnable(int en); + virtual void onCancelCapture(); + virtual int dragDrop(ifc_window *sourceWnd, int x, int y); + virtual int acceptExternalDrops() { return 1; } + virtual int dragEnter(ifc_window *sourceWnd); + virtual int dragOver(int x, int y, ifc_window *sourceWnd); + virtual int dragLeave(ifc_window *sourceWnd); + virtual void *getInterface(GUID g); + virtual int onAction(const wchar_t *action, const wchar_t *param, int x, int y, intptr_t p1, intptr_t p2, void *data, size_t datalen, ifc_window *source); + virtual const wchar_t *getId(); + virtual int onAcceleratorEvent(const wchar_t *name); + + virtual void setContent(const wchar_t *groupid_orguid, int autoresizefromcontent = 0); + virtual void setContentBySkinItem(SkinItem *item, int autoresizefromcontent = 0); + virtual void onNewContent() {} + + // AbstractWndHolder + virtual void abstract_onNewContent(); + + + virtual int onUnknownXuiParam(const wchar_t *param, const wchar_t *value) { return 0; } + + virtual GuiObject *findObject(const wchar_t *object_id); +#ifdef WASABI_COMPILE_SCRIPT + virtual ScriptObject *findScriptObject(const wchar_t *object_id); +#endif +#ifdef WASABI_COMPILE_SKIN + virtual GuiObject *getContent() { return abstract_getContent(); } + virtual ScriptObject *getContentScriptObject() { return abstract_getContentScriptObject(); } + virtual ifc_window *getContentRootWnd() { return abstract_getContentRootWnd(); } +#endif + + // BaseWnd + + virtual int onInit(); + virtual int onPostOnInit(); + virtual int onChar(unsigned int c); + virtual int onKeyDown(int vkcode); + virtual int onKeyUp(int vkcode); + + virtual int onGetFocus(); + virtual int onKillFocus(); + virtual int wantFocus(); + + const wchar_t *getTip(); + // GuiObjectWnd + int wantTranslation(); + GuiObject *getGuiObject(); + RootObject *getRootObject(); + int cfg_reentry; + + // XuiObject + virtual int setXuiParam(int _xuihandle, int attrid, const wchar_t *name, const wchar_t *value); + +protected: + /*static */void CreateXMLParameters(int master_handle); +private: + GuiObject *my_gui_object; + int xuihandle; + static XMLParamPair params[]; + + +public: + + enum { + GUIOBJECT_ID = 0, + GUIOBJECT_ALPHA, + GUIOBJECT_ACTIVEALPHA, + GUIOBJECT_INACTIVEALPHA, + GUIOBJECT_SYSREGION, + GUIOBJECT_RECTRGN, + GUIOBJECT_TOOLTIP, + GUIOBJECT_SYSMETRICSX, + GUIOBJECT_SYSMETRICSY, + GUIOBJECT_SYSMETRICSW, + GUIOBJECT_SYSMETRICSH, + GUIOBJECT_MOVE, + GUIOBJECT_RENDERBASETEXTURE, + GUIOBJECT_CFGATTR, + GUIOBJECT_X, + GUIOBJECT_Y, + GUIOBJECT_W, + GUIOBJECT_H, + GUIOBJECT_VISIBLE, + GUIOBJECT_ENABLED, + GUIOBJECT_RELATX, + GUIOBJECT_RELATY, + GUIOBJECT_RELATW, + GUIOBJECT_RELATH, + GUIOBJECT_DROPTARGET, + GUIOBJECT_GHOST, + GUIOBJECT_NOTIFY, + GUIOBJECT_FOCUSONCLICK, + GUIOBJECT_TABORDER, + GUIOBJECT_WANTFOCUS, + GUIOBJECT_SETNODBLCLICK, + GUIOBJECT_SETNOLEFTCLICK, + GUIOBJECT_SETNORIGHTCLICK, + GUIOBJECT_SETNOMOUSEMOVE, + GUIOBJECT_SETNOCONTEXTMENU, + GUIOBJECT_SETX1, + GUIOBJECT_SETY1, + GUIOBJECT_SETX2, + GUIOBJECT_SETY2, + GUIOBJECT_SETANCHOR, + GUIOBJECT_SETCURSOR, + GUIOBJECT_FITTOPARENT, + GUIOBJECT_USERDATA, +#ifdef USEAPPBAR + GUIOBJECT_APPBAR, +#endif + GUIOBJECT_TRANSLATE, + GUIOBJECT_NUMPARAMS + }; + + +}; + +template <class T, const wchar_t XMLTAG[], char SVCNAME[]> +class XuiObjectSvc : public svc_xuiObjectI +{ +public: + static const wchar_t *xuisvc_getXmlTag() { return XMLTAG; } + + int testTag(const wchar_t *xmltag) + { + if (!WCSICMP(xmltag, XMLTAG)) return 1; + return 0; + } + GuiObject *instantiate(const wchar_t *xmltag, ifc_xmlreaderparams *params = NULL) + { + if (testTag(xmltag)) + { + T * obj = new T; + ASSERT(obj != NULL); + return obj->getGuiObject(); + } + return NULL; + } + void destroy(GuiObject *g) + { + T *obj = static_cast<T *>(g->guiobject_getRootWnd()); + delete obj; + } + static const char *getServiceName() { return SVCNAME; } +}; + +template <class T> +class XuiObjectSvc2 : public svc_xuiObjectI +{ +public: + static const wchar_t *xuisvc_getXmlTag() { return T::xuiobject_getXmlTag(); } + int testTag(const wchar_t *xmltag) + { + if (!WCSICMP(xmltag, T::xuiobject_getXmlTag())) return 1; + return 0; + } + GuiObject *instantiate(const wchar_t *xmltag, ifc_xmlreaderparams *params = NULL) + { + if (testTag(xmltag)) + { + T * obj = new T; + ASSERT(obj != NULL); + return obj->getGuiObject(); + } + return NULL; + } + void destroy(GuiObject *g) + { + T *obj = static_cast<T *>(g->guiobject_getRootWnd()); + delete obj; + } + static const char *getServiceName() { return T::xuiobject_getServiceName(); } +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/itemlistwnd.cpp b/Src/Wasabi/api/wnd/wndclass/itemlistwnd.cpp new file mode 100644 index 00000000..1ff97732 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/itemlistwnd.cpp @@ -0,0 +1,286 @@ +#include "precomp.h" + +#include "itemlistwnd.h" +#include "../../common/metatags.h" + +#include "../../studio/api.h" + +#include "../skinclr.h" +#include "../../common/filename.h" + +#include "../../common/dragitemi.h" +#include "../../common/contextmenu.h" + +#define NIFTY_OUTLINE + +static SkinColor current("wasabi.itemlist.outline.current"); +static SkinColor selborder("wasabi.itemlist.selborder"); +static SkinColor focus("wasabi.itemlist.outline.focus"); + +ItemListColumn_MetaTag::ItemListColumn_MetaTag(const char *newtag, int centered, const char *label) + : ItemListColumn() { + if (label == NULL) label = newtag; + setLabel(label); + tag = newtag; + center = centered; + datatype = api->metadb_getMetaDataType(tag); +} + +ItemListColumn_Callback::ItemListColumn_Callback(ItemListWnd *_parent, LPARAM _lparam, const char *name) +: ItemListColumn(name) +{ + parent = _parent; + lparam = _lparam; +} + +void ItemListColumn_Callback::render(int pos, const char *playstring, Canvas &c, RECT &r) { + parent->userRender(pos, playstring, c, r, lparam); +} + +void ItemListColumn_Callback::columnToText(int pos, const char *playstring, char *str, int maxlen) { + parent->userColumnToText(pos, playstring, lparam, str, maxlen); +} + +void ItemListColumn_MetaTag::render(int pos, const char *playstring, Canvas &canvas, RECT &r) { + char buf[4096]=""; + if (api->metadb_getMetaData(playstring, tag, buf, 4096) <= 1) return; + api->metadb_renderData(&canvas, r, buf, datatype, center); +} + +void ItemListColumn_MetaTag::columnToText(int pos, const char *playstring, char *str, int maxlen) { + api->metadb_getMetaData(playstring, tag, str, maxlen); +} + +const char *ItemListColumn_MetaTag::getTag() { + return tag; +} + +void ItemListColumn_Numbered::render(int pos, const char *playstring, Canvas &c, RECT &r) { + StringPrintf buf("%d.", pos+1); + c.textOutEllipsed(r.left, r.top, r.right - r.left, r.bottom-r.top, buf); +} + +void ItemListColumn_Numbered::columnToText(int pos, const char *playstring, char *str, int maxlen) { + StringPrintf buf("%d", pos+1); + STRNCPY(str, buf, maxlen); +} + +ItemListWnd::ItemListWnd() { + keep = NULL; + item_invalidate_border++; +} + +ItemListWnd::~ItemListWnd() { + api->syscb_deregisterCallback(static_cast<MetaCallbackI *>(this)); +} + +int ItemListWnd::onInit() { + + ITEMLISTWND_PARENT::onInit(); + + api->syscb_registerCallback(static_cast<MetaCallbackI *>(this)); + + return 1; +} + +int ItemListWnd::insertColumn(ItemListColumn *column, int width, int pos) { + column->setWidth(width); + return ITEMLISTWND_PARENT::insertColumn(column, pos); +} + +int ItemListWnd::onBeginDrag(int iItem) { + + if (!wantAutoDrag()) return ITEMLISTWND_PARENT::onBeginDrag(iItem); + + // add all the entries + int n, i, c = 0; + n = getNumItems(); + if (n == 0) return 0; // empty list + + ASSERT(keep == NULL); + keep = new PtrList<FilenameNC>(); + + // count up the items + for (i = 0; i < n; i++) { + if (getItemSelected(i)) { // expose all the pointers we can + LPARAM lparam = getItemData(i); + if (!addMoreDragTypes(i)) { + FilenameNC *fn = keep->addItem(new FilenameNC(convertlParam(lparam))); + addDragItem(DD_FILENAME, static_cast<Filename*>(fn)); + } + c++; + } + } + + if (keep->getNumItems() == 0 || c <= 0) { + delete keep; keep = NULL; + return 0; + } + + handleDrag(); + + return 1; +} + +int ItemListWnd::dragComplete(int success) { + ASSERT(keep != NULL); + + keep->deleteAll(); + delete keep; keep = NULL; + + return 1; +} + +int ItemListWnd::ownerDraw(Canvas *canvas, int pos, RECT *r, LPARAM lParam, int isselected, int isfocused) { + const char *playstring = convertlParam(lParam); + if (playstring == NULL) return 0; + COLORREF bgcolor = isfocused ? getFocusColor(lParam) : getSelBgColor(lParam); + COLORREF fgcolor = getTextColor(lParam); + int iscur = getSelected(lParam); + + RECT box; + canvas->getClipBox(&box); + + if (!getBgBitmap()) { + RECT r2 = *r; + r2.left = box.left; + RegionI *reg = new RegionI(&r2); + canvas->selectClipRgn(reg); + delete reg; + canvas->fillRect(r, getBgColor()); + } + + canvas->setTextColor(fgcolor); + +// ASSERT(cols.getNumItems() == getNumColumns()); + + if (isselected) { + RECT mr = *r; + canvas->fillRect(&mr, bgcolor); +/*au gratin potatoes/broccoli +chicken^2 +salad, croutons, cheese, ranch +fries */ +#ifdef NIFTY_OUTLINE + int prevsel = getItemSelected(pos-1); + int nextsel = getItemSelected(pos+1); + mr.bottom--; + canvas->pushPen(selborder); + canvas->lineDraw(mr.left, mr.top, mr.left, mr.bottom); + canvas->lineDraw(mr.right-1, mr.top, mr.right-1, mr.bottom); + if (!prevsel) canvas->lineDraw(mr.left, mr.top, mr.right, mr.top); + if (!nextsel) canvas->lineDraw(mr.left, mr.bottom, mr.right, mr.bottom); + canvas->popPen(); +#endif + } + + if (isfocused || iscur) { + int pentype = isfocused ? FALSE : TRUE; + int boxcolor = iscur ? current : focus; + canvas->drawRect(r, pentype, boxcolor); + } + + canvas->pushTextSize(getFontSize()); + + int x = 1+r->left; + for (int i = 0; i < getNumColumns(); i++) { + ItemListColumn *col = (ItemListColumn *)enumListColumn(i); + RECT ir; + ir.left = x; + ir.right = x + getColumnWidth(i); + ir.top = r->top; + ir.bottom = r->bottom; + if (ir.right >= box.left && ir.bottom >= box.top && ir.left <= box.right && ir.top <= box.bottom) { + col->render(pos, playstring, *canvas, ir); + } + x = ir.right; + } + canvas->popTextSize(); + return 1; +} + +void ItemListWnd::convertlParamColumn(int col, int pos, LPARAM param, char *str, int maxlen) { + ASSERT(str != NULL); + ItemListColumn *cl = (ItemListColumn *)enumListColumn(col); + const char *playstring = convertlParam(param); + cl->columnToText(pos, playstring, str, maxlen); +} + + +int ItemListWnd::onContextMenu(int x, int y) { + PtrList<FilenameNC> filenames; + DragItemI *dragitem = NULL; + + if ((dragitem = itemlistwnd_getDragItem(x, y)) == NULL) { + DragItemT<Filename> *di = new DragItemT<Filename>; + int n = getNumItems(); + for (int i = 0; i < n; i++) { + if (getItemSelected(i)) { + LPARAM lparam = getItemData(i); + const char *playstring = convertlParam(lparam); + di->addDatum(filenames.addItem(new FilenameNC(playstring))); + } + } + dragitem = di; + } + + ContextMenu cm(this, filenames.getNumItems() ? dragitem : NULL, false); + + PtrList<DragItemI> drags; + for (int i = 0; ; i++) { + DragItemI *aitem = itemlistwnd_getSecondDragItem(i); + if (aitem == NULL) break; + drags.addItem(aitem); + if (cm.getNumCommands()) cm.addSeparator(); + cm.addDragItem(aitem); + } + itemlistwnd_addCustomContextMenuCommands(&cm); + int r = cm.popAtMouse(); + if (r < -10) itemlistwnd_contextMenuResult(r); + drags.deleteAll(); + + filenames.deleteAll(); + + delete dragitem; + + return 1; +} + +#if 0 +int ItemListWnd::onRightClick(int itemnum, int _x, int _y) { + // figure out which column they clicked on + int x, l = 0, r = 0; + for (int i = 0; i < cols.getNumItems(); i++) { + l = r; + r += getColumnWidth(i); + if (_x >= l && y <= r) break; + } + if (i >= cols.getNumItems()) return 0; + PlayItem *item = convertlParam(itemnum); + if (!allowEdition(item, cols[i]->getTag())) return 0; + + return 0; +} +#endif + +void ItemListWnd::metacb_onItemChange(const char *playstring, const char *tag) { + int n = getNumItems(); + for (int i = 0; i < n; i++) { + const char *ps = convertlParam(getItemData(i)); + if (ps != NULL && STREQL(ps, playstring)) { + onItemChange(i, ps); + invalidateItem(i); + } + } +} + +void ItemListWnd::metacb_onItemDel(const char *playstring) { + int n = getNumItems(); + for (int i = 0; i < n; i++) { + const char *ps = convertlParam(getItemData(i)); + if (ps != NULL && STREQL(ps, playstring)) { + onItemDel(i, ps); + invalidateItem(i); + } + } +} diff --git a/Src/Wasabi/api/wnd/wndclass/itemlistwnd.h b/Src/Wasabi/api/wnd/wndclass/itemlistwnd.h new file mode 100644 index 00000000..b2245d9c --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/itemlistwnd.h @@ -0,0 +1,426 @@ +//PORTABLE +#ifndef _ITEMLIST_H +#define _ITEMLIST_H + +#include "listwnd.h" +#include "../canvas.h" +#include "../named.h" +#include "../ptrlist.h" +#include "../string.h" +#include "../../studio/metacb.h" + +class FilenameNC; +class DragItemI; +class ContextMenu; + +// this class just handles rendering the various properties of playitems +// in a listwnd... the rest is up to you, just override the convert fn + +// abstract base class to render something in a column for a playstring + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class NOVTABLE ItemListColumn : public ListColumn { +protected: + + /** + Method + + @see + @ret + @param + */ + ItemListColumn(const wchar_t *name=NULL) : ListColumn(name) {} +public: + + /** + Method + + @see + @ret + @param + */ + virtual ~ItemListColumn() {} + + + /** + Method + + @see + @ret + @param + */ + virtual void render(int pos, const wchar_t *playstring, Canvas &c, RECT &r)=0; + + /** + Method + + @see + @ret + @param + */ + + virtual void columnToText(int pos, const wchar_t *playstring, wchar_t *str, int maxlen)=0; +}; + +#define ITEMLISTWND_PARENT ListWnd + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class ItemListWnd : public ListWnd, private MetaCallbackI { +friend class ItemListColumn_Callback; +public: + + /** + Method + + @see + @ret + @param + */ + ItemListWnd(); + + /** + Method + + @see + @ret + @param + */ + virtual ~ItemListWnd(); + + + /** + Method + + @see + @ret + @param + */ + virtual int onInit(); + + + /** + Method + + @see + @ret + @param + */ + int insertColumn(ItemListColumn *column, int width, int pos=-1); + +protected: + // override and return 0 to suppress auto-dragging from window + + /** + Method + + @see + @ret + @param + */ + virtual int wantAutoDrag() { return 1; } + // handles auto-adding all selected rows and calls addDragTypes + // so you can add more via addDragItem() + + /** + Method + + @see + @ret + @param + */ + virtual int onBeginDrag(int); + // if you return 0, the Filename version will be auto-added, otherwise not + + /** + Method + + @see + @ret + @param + */ + virtual int addMoreDragTypes(int pos) { return 0; } + + + /** + Method + + @see + @ret + @param + */ + virtual int dragComplete(int success); + + // tell ListWnd we do our own drawing + + /** + Method + + @see + @ret + @param + */ + virtual int ownerDraw(Canvas *canvas, int pos, RECT *r, LPARAM lParam, int isselected, int isfocused); + + // ItemListColumn_Callback calls this to do its rendering, lParam is what you + // gave it to pass back to you + + /** + Method + + @see + @ret + @param + */ + virtual void userRender(int pos, const wchar_t *playstring, Canvas &c, RECT &r, LPARAM lParam) {} + + // ItemListColumn_Callback calls this to get the column text, lParam is what you + // gave it to pass back to you + + /** + Method + + @see + @ret + @param + */ + virtual void userColumnToText(int pos, const wchar_t *playstring, LPARAM lParam, wchar_t *str, int maxlen) {} + + // override this to turn the ownerdraw into a playstring + virtual const wchar_t *convertlParam(LPARAM lParam)=0; + virtual void convertlParamColumn(int col, int pos, LPARAM lParam, wchar_t *str, int maxlen); + + // override this and return 1 if you want a "current" box around item + + /** + Method + + @see + @ret + @param + */ + virtual int getSelected(LPARAM lParam) { return 0; } + +// virtual int onRightClick(int itemnum, int x, int y); + // automatically generated context menu (uses Filename) + + /** + Method + + @see + @ret + @param + */ + virtual int onContextMenu(int x, int y); + + // return optional DragItemI for context menu (will be deleted for you) + virtual DragItemI *itemlistwnd_getDragItem(int x, int y) { return NULL; } + virtual DragItemI *itemlistwnd_getSecondDragItem(int n) { return NULL; } + virtual void itemlistwnd_addCustomContextMenuCommands(ContextMenu *cm) { } + virtual void itemlistwnd_contextMenuResult(int res) { } + + // return TRUE if it's ok to edit in place + + /** + Method + + @see + @ret + @param + */ + virtual int allowEdition(const wchar_t *playstring, wchar_t *field) { return 0; } + + + /** + Method + + @see + @ret + @param + */ + virtual void resort() { + //TODO> implement me! + } + +protected: + // implement this if you want to know when an item's metadata changed + + /** + Method + + @see + @ret + @param + */ + virtual void onItemChange(int pos, const wchar_t *playstring) { } + + /** + Method + + @see + @ret + @param + */ + virtual void onItemDel(int pos, const wchar_t *playstring) { } + + + /** + Method + + @see + @ret + @param + */ + virtual void metacb_onItemChange(const wchar_t *playstring, const wchar_t *tag); + + /** + Method + + @see + @ret + @param + */ + virtual void metacb_onItemDel(const wchar_t *); + +private: + PtrList<FilenameNC> *keep; +}; + +// column class to ask ItemListWnd to do the rendering + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class ItemListColumn_Callback : public ItemListColumn { +public: + + /** + Method + + @see + @ret + @param + */ + ItemListColumn_Callback(ItemListWnd *_parent, LPARAM _lparam, const wchar_t *name=NULL); + + + /** + Method + + @see + @ret + @param + */ + virtual void render(int pos, const wchar_t *playstring, Canvas &c, RECT &r); + virtual void columnToText(int pos, const wchar_t *playstring, wchar_t *str, int maxlen); + +private: + ItemListWnd *parent; + LPARAM lparam; +}; + +// column class to render a metatag + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class ItemListColumn_MetaTag : public ItemListColumn { +public: + + /** + Method + + @see + @ret + @param + */ + ItemListColumn_MetaTag(const wchar_t *tag, int center=0, const wchar_t *label=NULL); + + /** + Method + + @see + @ret + @param + */ + virtual ~ItemListColumn_MetaTag() {} + + + /** + Method + + @see + @ret + @param + */ + virtual void render(int pos, const wchar_t *playstring, Canvas &c, RECT &r); + virtual void columnToText(int pos, const wchar_t *playstring, wchar_t *str, int maxlen); + + const wchar_t *getTag(); + +private: + StringW tag; + int center; + int datatype; +}; + +// this just renders the position of the item, starting from 1 + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see + */ +class ItemListColumn_Numbered : public ItemListColumn { +public: + + /** + Method + + @see + @ret + @param + */ + ItemListColumn_Numbered(int _offset=0) : offset(_offset) {} + + /** + Method + + @see + @ret + @param + */ + virtual void render(int pos, const wchar_t *playstring, Canvas &c, RECT &r); + virtual void columnToText(int pos, const wchar_t *playstring, wchar_t *str, int maxlen); + +private: + int offset; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/labelwnd.cpp b/Src/Wasabi/api/wnd/wndclass/labelwnd.cpp new file mode 100644 index 00000000..d9aedd0f --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/labelwnd.cpp @@ -0,0 +1,203 @@ +#include <precomp.h> + +#include "labelwnd.h" + +#include <api/locales/xlatstr.h> +#include <api/wnd/paintsets.h> + +#include <tataki/canvas/bltcanvas.h> +#include <tataki/color/skinclr.h> +#include <api/wnd/notifmsg.h> +#include <tataki/region/region.h> +#include <api/wnd/PaintCanvas.h> + +static SkinColor labelfg(L"wasabi.labelwnd.foreground"); +static SkinColor labelbg(L"wasabi.labelwnd.background", L"Text backgrounds"); + +#define DEF_LABEL_HEIGHT 0 +#define DEF_LABEL_FONTSIZE 16 + +LabelWnd::LabelWnd() { + show_label = FALSE; + labelsize = DEF_LABEL_FONTSIZE; + labelHeight = 0; + margin=0; + setVirtual(0); +} + +void LabelWnd::getClientRect(RECT *r) { + LABELWND_PARENT::getClientRect(r); + r->top += getLabelHeight(); +} + +int LabelWnd::onResize() { + LABELWND_PARENT::onResize(); + invalidateLabel(); +/* if (getLabelHeight() <= 0) return 0; + RECT r,ir; + LABELWND_PARENT::getClientRect(&r); + LABELWND_PARENT::getNonClientRect(&ir); + ir.bottom = ir.top + getLabelHeight()+margin; + invalidateRect(&ir);*/ + return 1; +} + +int LabelWnd::onPaint(Canvas *canvas) { + if (getLabelHeight() <= 0) return LABELWND_PARENT::onPaint(canvas); + + PaintBltCanvas paintcanvas; + if (canvas == NULL) { + if (!paintcanvas.beginPaintNC(this)) return 0; + canvas = &paintcanvas; + } + + RECT r; + LabelWnd::getNonClientRect(&r); + + if (canvas->isFixedCoords()) { // handle possible double buffer + // convert to canvas coords + r.right -= r.left; r.left = 0; + r.bottom -= r.top; r.top = 0; + } + + r.bottom = r.top + getLabelHeight(); + + if (margin) { + r.left+=margin; + r.right-=margin; + r.bottom+=margin*2; + } + + LABELWND_PARENT::onPaint(canvas); + int got_focus = gotFocus() || forceFocus(); + + if (wantRenderBaseTexture()) { + WASABI_API_WND->skin_renderBaseTexture(getBaseTextureWindow(), canvas, r, this); + } + +#ifdef WASABI_COMPILE_PAINTSETS + WASABI_API_WND->paintset_render(Paintset::LABEL, canvas, &r, got_focus ? 255 : 64); +#endif + Wasabi::FontInfo fontInfo; + fontInfo.opaque=false; + fontInfo.pointSize = getLabelHeight()-1; + const wchar_t *name = getName(); + if (name == NULL || *name == '\0') + name = L"Label"; +#define LEFTMARGIN 3 + fontInfo.color = labelbg; + const wchar_t *xname = name; + + switch(wantTranslation()) + { + case 1: + xname = _(name); + break; + case 2: + xname = __(name); + break; + } + + canvas->textOut(r.left+LEFTMARGIN+1, r.top+1, xname, &fontInfo); + fontInfo.color = labelfg; + canvas->textOut(r.left+LEFTMARGIN, r.top, xname, &fontInfo); + + return 1; +} + +void LabelWnd::onSetName() { + LABELWND_PARENT::onSetName(); + // make sure label gets repainted + if (isInited()) { + RECT r; + LabelWnd::getNonClientRect(&r); + r.bottom = r.top + getLabelHeight(); + invalidateRect(&r); + } +} + +//CUTint LabelWnd::childNotify(api_window *child, int msg, intptr_t param1, intptr_t param2) { +//CUT switch (msg) { +//CUT case CHILD_WINDOWSHADE_CAPABLE: return show_label; +//CUT case CHILD_WINDOWSHADE_ENABLE: return TRUE; +//CUT } +//CUT return LABELWND_PARENT::childNotify(child, msg, param1, param2); +//CUT} + +int LabelWnd::showLabel(int show) { + show_label = show; + setFontSize(-1); + if (isPostOnInit()) { + onResize(); + } + return 1; +} + +int LabelWnd::getLabelHeight() { + return show_label ? labelHeight : 0; +} + +void LabelWnd::setMargin(int newmargin) { + margin = newmargin; + RECT r; + getNonClientRect(&r); + r.bottom = getLabelHeight()+margin; + invalidateRect(&r); +} + +int LabelWnd::onGetFocus() { + LABELWND_PARENT::onGetFocus(); + invalidateLabel(); + return 1; +} + +int LabelWnd::onKillFocus() { + LABELWND_PARENT::onKillFocus(); + invalidateLabel(); + return 1; +} + +void LabelWnd::invalidateLabel() { + if (labelHeight <= 0) return; + RECT ncr; + RECT cr; +// RECT lr; + LabelWnd::getNonClientRect(&ncr); + LabelWnd::getClientRect(&cr); + RegionI nonClientRgn(&ncr); + RegionI clientRgn(&cr); + nonClientRgn.subtractRgn(&clientRgn); + invalidateRgn(&nonClientRgn); +// SubtractRect(&lr, &ncr, &cr); // PORT ME +// invalidateRect(&lr); +} + +int LabelWnd::wantFocus() { + return (labelHeight > 0); +} + +void LabelWnd::reloadResources() +{ + LABELWND_PARENT::reloadResources(); + if (isPostOnInit()) + onResize(); + invalidateLabel(); +} + +int LabelWnd::setFontSize(int size) +{ + LABELWND_PARENT::setFontSize(size); + TextInfoCanvas blt(this); + Wasabi::FontInfo fontInfo; +#ifndef WASABINOMAINAPI + fontInfo.pointSize = labelsize+api->metrics_getDelta(); +#else + fontInfo.pointSize = labelsize; + //MULTIAPI-FIXME: not handling delta +#endif + labelHeight = blt.getTextHeight(&fontInfo) + 1; + invalidate(); + if (isPostOnInit()) onResize(); + return 1; +} + diff --git a/Src/Wasabi/api/wnd/wndclass/labelwnd.h b/Src/Wasabi/api/wnd/wndclass/labelwnd.h new file mode 100644 index 00000000..f6425514 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/labelwnd.h @@ -0,0 +1,46 @@ +#ifndef _LABELWND_H +#define _LABELWND_H + +#include <bfc/common.h> +#include <api/wnd/wndclass/guiobjwnd.h> + +#define LABELWND_PARENT GuiObjectWnd +class LabelWnd : public LABELWND_PARENT { +protected: + LabelWnd(); +public: + + virtual void getClientRect(RECT *); + virtual int onResize(); + virtual int onPaint(Canvas *canvas); + virtual int onGetFocus(); + virtual int onKillFocus(); + virtual void invalidateLabel(); + virtual int wantFocus(); + virtual int wantRenderBaseTexture() { return 1; } + + // override & return 1 to force painting label with focus all the time + virtual int forceFocus() { return 0; } + + virtual void onSetName(); + virtual void setMargin(int newmargin); + + virtual int setFontSize(int size); + +//CUT virtual int childNotify(api_window *child, int msg, intptr_t param1=0, intptr_t param2=0); + + int showLabel(int show); + int getLabelHeight(); + + void reloadResources(); + +private: + int show_label, labelsize; + int labelHeight; + int margin; +}; + +// use this if you want a generic labelwnd (but try not to) +class LabelWndI : public LabelWnd { }; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/listwnd.cpp b/Src/Wasabi/api/wnd/wndclass/listwnd.cpp new file mode 100644 index 00000000..5da770be --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/listwnd.cpp @@ -0,0 +1,2151 @@ +#pragma warning(disable:4355) + +#include <precomp.h> + +#include "listwnd.h" + +#include <bfc/wasabi_std.h> +#include <api/wnd/usermsg.h> +#include <bfc/ptrlist.h> +#include <tataki/color/skinclr.h> +#include <api/wnd/notifmsg.h> + +#include <bfc/assert.h> + +#include <api/locales/xlatstr.h> +#include <api/wnd/accessible.h> +#include <bfc/string/StringW.h> +#include <new> +#define DEF_TEXT_SIZE 14 // Default text size + +#define ITEMLIST_INC 4092 +#define LISTWND_DRAG_TIMERID 0x8972 +#define LISTWND_DRAG_MARGIN 12 +#define LISTWND_DRAG_TIMERDELAY 100 +#define DRAGSKIP_START 5 + +#define X_SHIFT 2 +#define Y_SHIFT 2 +#define DRAG_THRESHOLD 4 +#define COLUMNS_DEFAULT_HEIGHT 12 +#define COLUMNS_DEFAULT_WIDTH 96 +#define COLUMNS_MARGIN 2 +#define COLUMNS_RESIZE_THRESHOLD 4 +#define COLUMNS_MIN_WIDTH 1 +#define COLSEPHEIGHT 1 + +static SkinColor textColor(L"studio.list.text");// todo: have own color in skin.cpp +static SkinColor bgcolor(L"studio.list.background");// todo: have own color in skin.cpp +static SkinColor color_item_selected_fg(L"studio.list.item.selected.fg"); // RGB(0, 0, 128) +static SkinColor color_item_selected(L"studio.list.item.selected"); // RGB(0, 0, 128) +static SkinColor color_item_focused(L"studio.list.item.focused");// RGB(0, 128, 128) +static SkinColor color_item_focusrect(L"studio.list.item.focused");// RGB(0, 128, 128) +static SkinColor color_headers(L"studio.list.column.background");//RGB(0, 152, 208) +static SkinColor columnTextColor(L"studio.list.column.text"); +static SkinColor columnSepColor(L"studio.list.column.separator"); + +typedef struct +{ + wchar_t *label; + int column; +} listSubitemStruct; + +class listItem +{ +friend ListWnd; +public: + ListWnd *getList() const { return listwnd; } +protected: + listItem() + { + data = 0; + subitems = NULL; + listwnd = NULL; + } + ~listItem() + { + if (subitems != NULL) + { + for (int i=0;i<subitems->getNumItems();i++) + { + listSubitemStruct *subitem = subitems->enumItem(i); + if (subitem->label) + FREE(subitem->label); + } + subitems->freeAll(); + delete subitems; + } + } + void setList(ListWnd *newlist) { listwnd=newlist; } + + StringW label; + LPARAM data; + PtrList<listSubitemStruct> *subitems; + ListWnd *listwnd; + AutoSkinBitmap icon; +}; + +class CompareListItem { +public: + static int compareItem(listItem *p1, listItem *p2); +}; + +int CompareListItem::compareItem(listItem *p1, listItem *p2) { + return p1->getList()->sortCompareItem(p1, p2); +} + + +XMLParamPair ListWnd::params[] = { + {LIST_ANTIALIAS, L"ANTIALIAS"}, + {LIST_BACKGROUND, L"BACKGROUND"}, + {LIST_TILE, L"TILE"}, + {LIST_NOCOLHEADER, L"NOCOLHEADER"}, +}; + +ListWnd::ListWnd() +: selItemList(this) +{ + xuihandle = newXuiHandle(); + CreateXMLParameters(xuihandle); + + if (WASABI_API_SKIN->skin_getVersion() >= 1) { + textColor.setElementName(L"wasabi.list.text"); + textColor.setColorGroup(L""); + bgcolor.setElementName(L"wasabi.list.background"); + bgcolor.setColorGroup(L""); + color_item_selected_fg.setElementName(L"wasabi.list.text.selected"); + color_item_selected_fg.setColorGroup(L""); + color_item_selected.setElementName(L"wasabi.list.text.selected.background"); + color_item_selected.setColorGroup(L""); + color_headers.setElementName(L"wasabi.list.column.background"); + color_headers.setColorGroup(L""); + columnTextColor.setElementName(L"wasabi.list.column.text"); + columnTextColor.setColorGroup(L""); + columnSepColor.setElementName(L"wasabi.list.column.frame.bottom"); + columnSepColor.setColorGroup(L""); + color_item_focused.setColorGroup(L""); + color_item_focusrect.setColorGroup(L""); + } + selectonupdown = 1; + setAutoSort(FALSE); + setOwnerDraw(FALSE); + setSortDirection(0); + setSortColumn(0); + lastcolsort=-1; + dragtimeron = 0; + dragskip = DRAGSKIP_START; + dragskipcount = 0; + item_invalidate_border = 0; + + metrics_ok = FALSE; + setFontSize(DEF_TEXT_SIZE); + redraw = TRUE; + columnsHeight = COLUMNS_DEFAULT_HEIGHT; + lastItemFocused = NULL; + lastItemFocusedPos = -1; + lastAddedItem = NULL; + selectionStart = -1; + colresize = -1; + resizing_col = FALSE; + processbup = FALSE; + bdown = FALSE; + nodrag = FALSE; + showColumnsHeaders = TRUE; + rectselecting=0; + preventMultipleSelection = 0; +//CUT autoresizecol0 = 0; + wantautodeselect = 1; + registerAcceleratorSection(L"listwnd"); + hoverselect = 0; + firstItemVisible = -1; + lastItemVisible = -1; + showicons = 0; + iconWidth = -1; // If it's still negative use itemHeight instead -- better user getIconWidth() + iconHeight = -1; + antialias=1; + hasUserBg = false; +} + +void ListWnd::CreateXMLParameters(int master_handle) +{ + //LISTWND_PARENT::CreateXMLParameters(master_handle); + int numParams = sizeof(params) / sizeof(params[0]); + hintNumberOfParams(xuihandle, numParams); + for (int i = 0;i < numParams;i++) + addParam(xuihandle, params[i], XUI_ATTRIBUTE_IMPLIED); +} + +ListWnd::~ListWnd() { + deleteAllItems(); + columnsList.deleteAll(); + nodrag=FALSE; +} + +int ListWnd::setXuiParam(int _xuihandle, int xmlattributeid, const wchar_t *xmlattributename, const wchar_t *value) +{ + if (_xuihandle != xuihandle) + return ScrlBkgWnd::setXuiParam(_xuihandle, xmlattributeid, xmlattributename, value); + + switch (xmlattributeid) + { + case LIST_ANTIALIAS: + antialias = WTOI(value); + break; + case LIST_BACKGROUND: + { + SkinBitmap __bmp(value); + if (!__bmp.isInvalid()) + { + this->setBgBitmap(value); + hasUserBg = true; + } + else + { + this->setBgBitmap(L"wasabi.list.background"); + hasUserBg = false; + } + } + this->invalidate(); + break; + case LIST_TILE: + this->wantTileBg = WTOI(value)?1:0; + this->invalidate(); + break; + case LIST_NOCOLHEADER: + setShowColumnsHeaders(!WTOI(value)); + break; + default: + return 0; + } + return 1; +} + +int ListWnd::onInit() { + + LISTWND_PARENT::onInit(); + + if (!hasUserBg) setBgBitmap(L"wasabi.list.background"); + setLineHeight(getItemHeight()); + + return 1; +} + +int ListWnd::onPostOnInit() { + LISTWND_PARENT::onPostOnInit(); + recalcHeaders(); + return 1; +} + +int ListWnd::onPaint(Canvas *canvas) +{ + + PaintCanvas paintcanvas; + PaintBltCanvas paintbcanvas; + + if (canvas == NULL) { + if (needDoubleBuffer()) { + if (!paintbcanvas.beginPaintNC(this)) return 0; + canvas = &paintbcanvas; + } else { + if (!paintcanvas.beginPaint(this)) return 0; + canvas = &paintcanvas; + } + } + + LISTWND_PARENT::onPaint(canvas); + + RegionI clip; + canvas->getClipRgn(&clip); + api_region *orig; + orig = clip.clone(); + + drawColumnHeaders(canvas); + + if (getColumnsHeight() > 0) { + RECT cr; + getClientRect(&cr); + cr.bottom = cr.top; + cr.top -= getColumnsHeight(); + clip.subtractRect(&cr); + canvas->selectClipRgn(&clip); + } + + firstItemVisible = -1; + lastItemVisible = -1; + + //drawSubItems(canvas, x, &y, items, r.top, r.bottom, 0); + drawItems(canvas); + + canvas->selectClipRgn(orig); // reset cliping region; + + clip.disposeClone(orig); + + return 1; +} + +int ListWnd::onResize() { + LISTWND_PARENT::onResize(); + recalcHeaders(); + return 1; +} + +int ListWnd::getNumItems(void) { + return itemList.getNumItems(); +} + +void ListWnd::drawItems(Canvas *canvas) { + + RECT r, c; + getClientRect(&r); + + RegionI clip; + if (!canvas->getClipRgn(&clip)) + { + clip.setRect(&r); + } + + if (GetClipBox(canvas->getHDC(), &c) == NULLREGION) { + getClientRect(&c); + } + +// DebugString("%d %d %d %d\n", c.left, c.top, c.right, c.bottom); + +// float f,l; + calcBounds(); + + //int first, last; + +/* RECT s=c; + + s.bottom = min(s.bottom, r.bottom); + s.top = max(s.top, getLabelHeight()+(showColumnsHeaders ? getColumnsHeight() : 0)); + + f = ((float)(s.top - getLabelHeight() - (showColumnsHeaders ? getColumnsHeight() : 0) - getYShift() + getScrollY())) / (float)itemHeight; + l = ((float)(s.bottom - getLabelHeight() - (showColumnsHeaders ? getColumnsHeight() : 0) - getYShift() + getScrollY())) / (float)itemHeight; + first = (int)f; + first = max(0,first); + last = (int)l; + if ((float)((int)l) != l) { + last++; + }*/ + + int g=0; + + for (int i=firstItemVisible;i<=lastItemVisible && i<itemList.getNumItems();i++) { + RECT ir={0,0,0,0}; + getItemRect(i, &ir); + int a=ir.right; + ir.right = r.right; + if (!clip.doesIntersectRect(&ir)) + continue; + ir.right=a; + g++; + + LPARAM itemdata = getItemData(i); + int itemselected = getItemSelected(i); + int itemfocused = getItemFocused(i); + + onPreItemDraw(canvas, i, &ir, itemdata, itemselected, itemfocused); + + int sel = getItemSelected(i); + canvas->pushPen(PS_SOLID, 1, getColumnSepColor()); + + if (!ownerDraw(canvas, i, &ir, itemdata, itemselected, itemfocused)) + { + Wasabi::FontInfo fontInfo; + fontInfo.antialias = getTextAntialias(itemdata); + fontInfo.bold = getTextBold(itemdata); + fontInfo.italic = !!getTextItalic(itemdata); + fontInfo.opaque = false; + fontInfo.color = sel ? getSelFgColor(itemdata) : getTextColor(itemdata); + fontInfo.pointSize = getFontSize(); + int x = -getScrollX(); + if (sel) + canvas->fillRect(&ir, getSelBgColor(itemdata)); + if (getItemFocused(i)) + canvas->fillRect(&ir, getFocusColor(itemdata)); + if (needFocusRect(itemdata)) + canvas->drawRect(&ir, 1, getFocusRectColor(itemdata)); + if (showicons) + { + SkinBitmap *icon = getItemIcon(i); + if (icon) + { + RECT dst={x+X_SHIFT+r.left+1, ir.top+1, x+X_SHIFT+r.left+getIconWidth()+1, ir.top+getIconHeight()+1}; + icon->stretchToRect(canvas, &dst); + } + x += getIconWidth()+1; + } + int xsep = wantColSepOnItems()?COLSEPHEIGHT:0; + for (int j=0;j<columnsList.getNumItems();j++) { + ListColumn *col = columnsList[j]; + RECT cr=ir; + cr.left = x+X_SHIFT+r.left; + cr.right = cr.left + col->getWidth()-X_SHIFT*2+xsep; + if (j > 0 && wantColSepOnItems()) { + canvas->moveTo(x, ir.top); + canvas->lineTo(x, ir.top+getItemHeight()); + } + switch (col->getAlignment()) { + case COL_LEFTALIGN: + canvas->textOutEllipsed(cr.left+2, cr.top, cr.right-cr.left-4, cr.bottom-cr.top, getSubitemText(i, j), &fontInfo); + break; + case COL_CENTERALIGN: { + RECT _cr = {cr.left+2, cr.top, cr.right-4, cr.bottom}; + canvas->textOutCentered(&_cr, getSubitemText(i, j), &fontInfo); + break; + } + case COL_RIGHTALIGN: { + const wchar_t *txt = getSubitemText(i, j); + int __x = cr.left; + int __y = cr.top; + int fw = canvas->getTextWidth(txt, &fontInfo); + int aw = cr.right-cr.left-4; + __x -= fw-aw; + canvas->textOut(__x, __y, txt, &fontInfo); + break; + } + } + x += col->getWidth()+xsep; + } + if (wantColSepOnItems()) { + canvas->moveTo(x, ir.top); + canvas->lineTo(x, ir.top+getItemHeight()); + } + } + + canvas->popPen(); + onPostItemDraw(canvas, i, &ir, itemdata, itemselected, itemfocused); + } +/* + OutputDebugString("%d items draw\n", g); +*/ +} + +int ListWnd::wantColSepOnItems() { + return 0; +} + +int ListWnd::getXShift() { + if (wantColSepOnItems()) return X_SHIFT; else return 0; +} + +int ListWnd::getFirstItemSelected() { + return getNextItemSelected(-1); +} + +int ListWnd::getNextItemSelected(int lastpos) { + if (lastpos < -1) lastpos = -1; + for (int i=lastpos+1;i<itemList.getNumItems();i++) + if (getItemSelected(i)) + return i; + return -1; +} + +void ListWnd::calcBounds() { + lastComplete = TRUE; + firstComplete = TRUE; + float f,l; + RECT r; + getClientRect(&r); + + f = ((float)(getScrollY()-Y_SHIFT) / getItemHeight()); + l = ((float)(getScrollY()-Y_SHIFT+(r.bottom-r.top)) / getItemHeight()) - 1.0f; + + firstItemVisible = (int)f; + lastItemVisible = (int)l; + + if ((float)((int)l) != l) { + lastItemVisible++; + lastComplete = FALSE; + } + if ((float)((int)f) != f && f >= 0) { + firstComplete = FALSE; + } +} + +// Draws tiled background +void ListWnd::drawBackground(Canvas *canvas) +{ + LISTWND_PARENT::drawBackground(canvas); + drawColumnHeaders(canvas); +} + +void ListWnd::drawColumnHeaders(Canvas *c) +{ + if (columnsList.getNumItems() == 0 || !showColumnsHeaders) return; + + RECT r; + getClientRect(&r); + r.top -= getColumnsHeight(); + r.bottom = r.top + getColumnsHeight(); + if (renderRatioActive()) + r.left -= (int)((double)getScrollX()*getRenderRatio()); + else + r.left-=getScrollX(); + + c->fillRect(&r, color_headers); + int x = r.left + X_SHIFT/*+ 1*/; + + if (showicons) + x += getIconWidth()+1 + 2; + + Wasabi::FontInfo fontInfo; + fontInfo.color = columnTextColor; + fontInfo.opaque = false; + fontInfo.pointSize = getColumnsHeight(); + c->pushPen(PS_SOLID, 1, getColumnSepColor()); + + for (int i=0;i<columnsList.getNumItems();i++) + { + ListColumn *col = columnsList[i]; + int width = col->getWidth(); + if (i > 0) { + c->moveTo(x, r.top); + c->lineTo(x, r.top+getColumnsHeight()); + } + RECT ch; + ch.left = x+COLUMNS_MARGIN-((i==0)?X_SHIFT:0); + ch.top = r.top; + ch.right = ch.left + col->getWidth()-1-COLUMNS_MARGIN*2+((i==0)?X_SHIFT:0); + ch.bottom = ch.top + getColumnsHeight()-1; + col->customDrawHeader(c, &ch, &fontInfo); + x+=width/*+1*/; + } + c->moveTo(x, r.top); + c->lineTo(x, r.top+getColumnsHeight()); + c->popPen(); + +} + +ARGB32 ListWnd::getColumnSepColor() { + return columnSepColor; +} + +int ListWnd::getHeaderHeight() { + return (showColumnsHeaders && columnsList.getNumItems() > 0) ? getColumnsHeight() : 0; +} + +// Returns the current tree width in pixels +int ListWnd::getContentsWidth() { + return getColumnsWidth()+X_SHIFT; +} + +// Returns the current tree height in pixels +int ListWnd::getContentsHeight() { + return itemList.getNumItems()*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0))+Y_SHIFT*2; +} + +void ListWnd::setAutoSort(bool dosort) { + autosort = dosort; + itemList.setAutoSort(dosort); +} + +void ListWnd::setOwnerDraw(bool doownerdraw) { + ownerdraw = doownerdraw; +} + +int ListWnd::setFontSize(int size) +{ + LISTWND_PARENT::setFontSize(size); + if (size >= 0) textsize = size; + TextInfoCanvas c(this); + Wasabi::FontInfo fontInfo; + fontInfo.pointSize = getFontSize(); + setItemHeight(c.getTextHeight(&fontInfo)+2, false); + redraw = TRUE; + metrics_ok = FALSE; + invalidate(); + return 1; +} + +int ListWnd::getFontSize() { +#ifndef WASABINOMAINAPI + return textsize+api->metrics_getDelta(); +#else + //MULTIAPI-FIXME: not handling delta + return textsize; +#endif +} + +void ListWnd::onSetVisible(int show) { + LISTWND_PARENT::onSetVisible(show); + if (show) invalidate(); +} + +int ListWnd::fullyVisible(int pos) { + return (((lastComplete && pos <= lastItemVisible) || (!lastComplete && pos < lastItemVisible)) && ((firstComplete && pos >= firstItemVisible) || (!firstComplete && pos > firstItemVisible))); +} + +void ListWnd::ensureItemVisible(int pos) { + if (pos >= itemList.getNumItems()) pos = itemList.getNumItems()-1; + if (pos < 0) pos = 0; + if (fullyVisible(pos)) return; + + RECT c; + int y=pos*getItemHeight(); + getClientRect(&c); + int showing_height = c.bottom - c.top; + + if (getScrollY() < y) // scrolling up + y = (y - showing_height) + getItemHeight()*3; + else + y -= getItemHeight()*2; + + if (y < 0) y = 0; + else if (y + showing_height > getContentsHeight()) { + // just show bottom pane + y = getContentsHeight()-showing_height; + } + scrollToY(y); +} + +void ListWnd::scrollToItem(int pos) { + if (!isInited()) return; + scrollToY(Y_SHIFT+pos*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0))); +} + +int ListWnd::getNumColumns() { + return columnsList.getNumItems(); +} + +ListColumn *ListWnd::getColumn(int n) { + return columnsList.enumItem(n); +} + +int ListWnd::getColumnWidth(int c) { + ListColumn *col = columnsList[c]; + ASSERT(col != NULL); + return col->getWidth(); +} + +int ListWnd::selectAll(int cb) { + + int i; + + if (preventMultipleSelection) return 1; + + for (i=0;i<itemList.getNumItems();i++) { + selItemList.setSelected(i, TRUE, cb); + } + + if (cb) + notifySelChanged(); + return 1; +} + +int ListWnd::deselectAll(int cb) { + + int lif = getItemFocused(); + if (lif != -1) + invalidateItem(lif); + lastItemFocused = NULL; + + for (int i = 0; i < itemList.getNumItems(); i++) { + selItemList.setSelected(i, FALSE, cb); + } + + if (cb) + notifySelChanged(); + return 1; +} + +void ListWnd::notifySelChanged(int item, int sel) { + if (!getRedraw()) return; + if (item == -1) + notifyParent(ChildNotify::LISTWND_SELCHANGED); + else + notifyParent(ChildNotify::LISTWND_ITEMSELCHANGED, item, sel); +} + +int ListWnd::invertSelection(int cb) { + if (preventMultipleSelection) return 1; + for (int i = 0; i < itemList.getNumItems(); i++) { + toggleSelection(i, FALSE, cb); + } + return 1; +} + +int ListWnd::invalidateItem(int pos) { + RECT r; + if (!isInited()) return 0; + int rv = getItemRect(pos, &r); + r.top -= item_invalidate_border; + r.bottom += item_invalidate_border; + if (rv) + invalidateRect(&r); + return rv; +} + +int ListWnd::onLeftButtonDblClk(int x, int y) { + // check for column dblclick + int colhit; + if ((colhit = hitTestColumns(Wasabi::Std::makePoint(x, y))) >= 0) { + return onColumnDblClick(colhit, x, y); + } + + if (itemList.getNumItems() == 0) return 0; + POINT p={x,y}; + int i = hitTest(p); + if (i > -1) { + notifyParent(ChildNotify::LISTWND_DBLCLK, i, 0); + onDoubleClick(i); + return 1; + } + return 0; +} + +int ListWnd::onRightButtonDown(int x, int y) { + nodrag=TRUE; + return 1; +} + +int ListWnd::onRightButtonUp(int x, int y) { + nodrag=FALSE; + int i = hitTest(x,y); + if (i >= 0) { // did hit something + setItemFocused(i); // it always gets the focus + if (!getItemSelected(i)) { // reselect the item out of the cur selection + ListWnd::onLeftButtonDown(x,y); // don't call inherited! + ListWnd::onLeftButtonUp(x,y); // don't call inherited! + } + onRightClick(i); + } else { + if (wantAutoDeselect()) + deselectAll(); + } + onContextMenu(x,y); + + return 1; +} + +int ListWnd::onRightClick(int itemnum) { + return 0; +} + + +int ListWnd::onLeftButtonDown(int x, int y) { + if (colresize != -1) { + resizing_col = TRUE; + drawXorLine(colresizept.x); + } + + processbup = FALSE; + bdown = TRUE; + bdownx = x; + bdowny = y; + + if (!resizing_col) { + + POINT p={x,y}; + int i = hitTest(p); + if (i >= 0) { + if (Std::keyDown(VK_SHIFT)) { + if (getItemSelected(i)) + processbup=TRUE; + else + setSelectionEnd(i); + } else + if (Std::keyDown(VK_CONTROL)) { + if (getItemSelected(i)) + processbup=TRUE; + else + toggleSelection(i); + } else { + if (getItemSelected(i)) + processbup = TRUE; + else + setSelectionStart(i); + } + } else { + if (wantAutoDeselect()) + deselectAll(); + /*rectselecting=1; + selectStart.x = x; + selectStart.y = y; + selectLast.x = x; + selectLast.y = y; + drawRect(selectStart.x, selectStart.y, x, y); + beginCapture();*/ + } + } + + return 1; +} + +int ListWnd::onLeftButtonUp(int x, int y) { + + bdown = FALSE; + + if (resizing_col) { + resizing_col = FALSE; + drawXorLine(colresizept.x); + calcNewColWidth(colresize, colresizept.x); + recalcHeaders(); + return 1; + } + + // check for column label click + int colhit; + if ((colhit = hitTestColumnsLabel(Wasabi::Std::makePoint(x, y))) >= 0) { + ListColumn *lc = getColumn(colhit); + int ret = lc->onHeaderClick(); + if (!ret) ret = onColumnLabelClick(colhit, x, y); + return ret; + } + + POINT p={x,y}; + int i = hitTest(p); + + if (rectselecting || (processbup && !resizing_col) || hoverselect) { + if (i >= 0) { + if (Std::keyDown(VK_SHIFT)) { + if (getItemSelected(i)) + setSelectionStart(i); + else + setSelectionEnd(i); + } else { + if (Std::keyDown(VK_CONTROL) || !wantAutoDeselect()) { + toggleSelection(i); + selectionStart = i; + } else { + setSelectionStart(i); + } + } + } else { +/* if (rectselecting) { + drawRect(selectStart.x, selectStart.y, selectLast.x, selectLast.y); + endCapture(); + rectselecting=0; + selectRect(selectStart.x, selectStart.y, x, y); + } else*/ + if (wantAutoDeselect()) + deselectAll(); + } + } + + if (i >= 0) { + int cn = hitTestColumnClient(x); + int r = 0; + if (cn >= 0) { + ListColumn *lc = getColumn(cn); + ASSERT(lc != NULL); + r = lc->onColumnLeftClick(i); + } + if (!r) + { + // Add 1px clickable border to our icon + if (x < getIconWidth()+2) r = onIconLeftClick(i,x,y-(i*getItemHeight()) - getHeaderHeight()); + if (!r) onLeftClick(i); + } + } + + return 1; +} + +int ListWnd::onMouseMove(int x, int y) { + LISTWND_PARENT::onMouseMove(x,y); + + if (!bdown && (Std::keyDown(MK_RBUTTON) || Std::keyDown(MK_RBUTTON))) { + bdown = TRUE; + processbup = TRUE; + _enterCapture(); + } + + if (!rectselecting && bdown && !resizing_col && !nodrag && (ABS(x-bdownx) >= 4 || ABS(y-bdowny) >= 4)) { + processbup = FALSE; + bdown = FALSE; + int i = hitTest(x, y); + if (i != -1) { + onBeginDrag(i); + return 1; + } + } + +/* if (rectselecting) { + drawRect(selectStart.x, selectStart.y, selectLast.x, selectLast.y); + selectLast.x = x; + selectLast.y = y; + drawRect(selectStart.x, selectStart.y, selectLast.x, selectLast.y); + return 1; + }*/ + + POINT p={x,y}; + if (wantResizeCols()) { + if (!resizing_col) { + int c = hitTestColumns(p, &colresizeo); + if (c != -1) { + if (colresize != c) { + SetCursor(LoadCursor(NULL, IDC_SIZEWE)); // NONPORTABLE + if (!getCapture()) + beginCapture(); + colresize = c; + colresizept = p; + } + } else { + if (colresize != -1) { + SetCursor(LoadCursor(NULL, IDC_ARROW)); // NONPORTABLE + endCapture(); + colresize = -1; + } + } + } else { + if (p.x + getScrollX() < colresizeo + COLUMNS_MIN_WIDTH) { + p.x = colresizeo + COLUMNS_MIN_WIDTH - getScrollX(); + } + drawXorLine(colresizept.x); + colresizept = p; + drawXorLine(colresizept.x); + } + } + + if (hoverselect) { + int i = hitTest(x, y); + if (i >= 0 && !getItemSelected(i)) { + deselectAll(0); + setSelected(i, 1, 0); + wchar_t t[256]=L""; + getItemLabel(i, 0, t, 255); + foreach(tempselectnotifies) + sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t); + endfor; + } + } + return 1; +} + +// NONPORTABLE / Todo: implement necessary stuff in canvas +void ListWnd::drawXorLine(int x) { + HDC dc = GetDC(gethWnd()); + HBRUSH brush = CreateSolidBrush(0xFFFFFF); + HPEN pen = CreatePen(PS_SOLID,0,0xFFFFFF); + HBRUSH oldB = (HBRUSH)SelectObject(dc, brush); + HPEN oldP = (HPEN)SelectObject(dc, pen); + int mix = SetROP2(dc,R2_XORPEN); + + RECT r; + getClientRect(&r); + r.top -= getColumnsHeight(); + r.bottom = r.top + getColumnsHeight(); + + if (renderRatioActive()) { + multRatio(&x); + multRatio(&r); + } + + MoveToEx(dc, x, r.top, NULL); + LineTo(dc, x, r.bottom); + + SelectObject(dc, oldB); + SelectObject(dc, oldP); + SetROP2(dc, mix); + DeleteObject(pen); + DeleteObject(brush); + ReleaseDC(gethWnd(), dc); +} + +void ListWnd::calcNewColWidth(int c, int px) { + RECT r; + getClientRect(&r); + r.top -= getColumnsHeight(); + r.bottom = r.top + getColumnsHeight(); + px += getScrollX(); + int x = r.left+X_SHIFT; + for (int i=0;i<columnsList.getNumItems();i++) { + ListColumn *col = columnsList[i]; + if (col->getIndex() == c) { + int w = px - x; + col->setWidth(w); + setSlidersPosition(); + return; + } + x += col->getWidth(); + } + return; +} + +int ListWnd::hitTestColumns(POINT p, int *origin) { + RECT r; + if (!showColumnsHeaders) return -1; + int best=-1, besto = 0, bestd=9999; + getClientRect(&r); + r.top -= getColumnsHeight(); + r.bottom = r.top + getColumnsHeight(); + p.x += getScrollX(); + if (p.y > r.top && p.y < r.top + getColumnsHeight()) { + int x = r.left+X_SHIFT; + for (int i=0;i<columnsList.getNumItems();i++) { + ListColumn *col = columnsList[i]; + x += col->getWidth(); + if (p.x > x-COLUMNS_RESIZE_THRESHOLD && p.x < x+COLUMNS_RESIZE_THRESHOLD) { + int d = ABS(p.x-x); + if (d < bestd) { + bestd = d; + besto = x - col->getWidth(); + best = col->getIndex(); + } + } + } + } + + if (best != -1) + if (origin != NULL) *origin = besto; + return best; +} + +int ListWnd::hitTestColumnClient(int x) { + RECT cr = clientRect(); + int x1 = cr.left+X_SHIFT; + foreach(columnsList) + int x2 = x1 + columnsList.getfor()->getWidth(); + if (x >= x1 && x <= x2) return foreach_index; + x1 = x2; + endfor + return -1; +} + +void ListWnd::invalidateColumns() { + RECT r; + getClientRect(&r); + r.top -= getColumnsHeight(); + r.bottom = r.top + getColumnsHeight(); + invalidateRect(&r); +} + +int ListWnd::hitTestColumnsLabel(POINT p) { + RECT r; + if (!showColumnsHeaders) return -1; + getClientRect(&r); + r.top -= getColumnsHeight(); + r.bottom = r.top + getColumnsHeight(); + p.x += getScrollX(); + if (p.y > r.top && p.y < r.top + getColumnsHeight()) { + int x = X_SHIFT; + for (int i=0;i<columnsList.getNumItems();i++) { + ListColumn *col = columnsList[i]; + if (p.x >= x && p.x <= x+col->getWidth()) + return i; + x += col->getWidth(); + } + } + return -1; +} + +void ListWnd::setSelectionStart(int pos, int wantcb) { + if (wantAutoDeselect()) + deselectAll(wantcb); + if (!selItemList.isSelected(pos)) { + selItemList.setSelected(pos, TRUE, wantcb); + lastItemFocused = itemList[pos]; + lastItemFocusedPos = pos; + invalidateItem(pos); + repaint(); + } + + selectionStart = pos; + notifySelChanged(); +} + +void ListWnd::setSelectionEnd(int pos) { + + if (itemList.getNumItems() == 0) return; + if (selectionStart == -1) selectionStart = 0; + + if (wantAutoDeselect()) + deselectAll(); + + int inc = (selectionStart > pos) ? -1 : 1; + int i = selectionStart; + + while (1) { + if (!selItemList.isSelected(i)) { + selItemList.setSelected(i, TRUE); + lastItemFocused = itemList[i]; + lastItemFocusedPos = i; + invalidateItem(i); + } + if (i == pos) break; + i += inc; + } + notifySelChanged(); +} + +void ListWnd::setSelected(int pos, int selected, int cb) { + selItemList.setSelected(pos, selected, cb); +} + +void ListWnd::toggleSelection(int pos, int setfocus, int cb) { + if (!selItemList.isSelected(pos)) { + selItemList.setSelected(pos, TRUE, cb); + if (setfocus) { + if (selItemList.getNumSelected() > 1) { + for (int i=0;i<itemList.getNumItems();i++) + if (selItemList.isSelected(i)) + invalidateItem(i); + } + lastItemFocused = itemList[pos]; + lastItemFocusedPos = pos; + } + } else { + selItemList.setSelected(pos, FALSE, cb); + if (setfocus) { + lastItemFocused = NULL; + lastItemFocusedPos = -1; + } + } + + invalidateItem(pos); + if (cb) + notifySelChanged(); +} + +int ListWnd::onBeginDrag(int iItem) { + // nothing by default + lastItemFocused = NULL; + lastItemFocusedPos = -1; + invalidateItem(iItem); + return 0; +} + +int ListWnd::dragOver(int x, int y, ifc_window *sourceWnd) { + int rt = LISTWND_PARENT::dragOver(x, y, sourceWnd); + if (dragtimeron) return rt; + + POINT pos={x,y}; + screenToClient(&pos); + + int item = hitTest(pos); + if (item == LW_HT_BELOW || item == LW_HT_ABOVE) { + dragtimeron = 1; + dragskip = DRAGSKIP_START; + dragskipcount = DRAGSKIP_START-1; // start right away + setTimer(LISTWND_DRAG_TIMERID, LISTWND_DRAG_TIMERDELAY); + } + return rt; +} + +void ListWnd::onDragTimer() { + POINT pos; + Wasabi::Std::getMousePos(&pos); + screenToClient(&pos); + int item = hitTest(pos); + if (item == LW_HT_BELOW || item == LW_HT_ABOVE) { + dragskipcount++; + if (dragskipcount >= dragskip) { + switch (item) { + case LW_HT_BELOW: + scrollDown(); + break; + case LW_HT_ABOVE: + scrollUp(); + break; + } + dragskipcount = 0; + if (dragskip > 0) dragskip--; + } + } else { + killTimer(LISTWND_DRAG_TIMERID); + dragtimeron = 0; + } +} + +void ListWnd::timerCallback(int id) { + switch (id) { + case LISTWND_DRAG_TIMERID: + onDragTimer(); + return; + } + LISTWND_PARENT::timerCallback(id); +} + +void ListWnd::onSelectAll() { +} + +void ListWnd::onDelete() { + // do nothing by default +} + +void ListWnd::onDoubleClick(int itemnum) { + // do nothing by default +} + +void ListWnd::onLeftClick(int itemnum) { + // do nothing by default +} + +int ListWnd::onIconLeftClick (int itemnum, int x, int y) +{ + return 0; +} + +void ListWnd::onSecondLeftClick(int itemnum) { + // do nothing by default +} + +int ListWnd::scrollAbsolute(int x) { + scrollToX(x); + return getScrollX(); +} + +int ListWnd::scrollRelative(int x) { + scrollToX(getScrollX() + x); + return getScrollX(); +} + +int ListWnd::getItemFocused() { + if (lastItemFocused == NULL) return -1; + if (itemList[lastItemFocusedPos] == lastItemFocused) + return lastItemFocusedPos; + else { + lastItemFocusedPos = itemList.searchItem(lastItemFocused); + return lastItemFocusedPos; + } +} + +void ListWnd::setItemFocused(int pos, int ensure_visible) { + invalidateItem(lastItemFocusedPos); + lastItemFocused = itemList[pos]; + lastItemFocusedPos = -1; + if (lastItemFocused != NULL) lastItemFocusedPos = pos; + invalidateItem(lastItemFocusedPos); + if (ensure_visible) ensureItemVisible(pos); +} + +int ListWnd::getItemRect(int pos, RECT *r) { + MEMSET(r, 0, sizeof(RECT)); + if (pos < 0 || pos >= itemList.getNumItems()) return 0; + RECT cr={0,0,0,0}; + getClientRect(&cr); + r->left = -getScrollX() + X_SHIFT + cr.left; + r->right = cr.left + getColumnsWidth(); + if (showicons) r->right += getItemHeight(); + r->top = -getScrollY() + pos * (getItemHeight() + (wantColSepOnItems() ? COLSEPHEIGHT : 0)) + Y_SHIFT + cr.top; + r->bottom = r->top + getItemHeight(); + + // clip! + if (r->top > cr.bottom || r->bottom < cr.top) return 0; + + return 1; +} + +int ListWnd::locateData(LPARAM data) { // linear search + for (int i=0;i<itemList.getNumItems();i++) { + if (itemList[i]->data == data) + return i; + } + return -1; +} + +int ListWnd::getItemFocused(int pos) { + if (pos >= itemList.getNumItems()) return 0; + return getItemFocused() == pos; +} + +int ListWnd::getItemSelected(int pos) { + listItem *item = itemList[pos]; + return (item && selItemList.isSelected(pos)); +} + +int ListWnd::hitTest(POINT pos, int drag) { + RECT r; + getClientRect(&r); + if (pos.y < r.top) return LW_HT_ABOVE; + if (getScrollY() > 0 && drag && pos.y < r.top + LISTWND_DRAG_MARGIN) return LW_HT_ABOVE; + if (pos.y > r.bottom) return LW_HT_BELOW; + if (getContentsHeight() > (getScrollY() + r.bottom-r.top) && drag && pos.y > r.bottom - LISTWND_DRAG_MARGIN) return LW_HT_BELOW; + + if (pos.x > r.left + getColumnsWidth() - getScrollX()) return LW_HT_DONTKNOW; + if (pos.x < r.left + getScrollX()) return LW_HT_DONTKNOW; + + int i = (pos.y - r.top + getScrollY() - Y_SHIFT) / (getItemHeight() + (wantColSepOnItems() ? COLSEPHEIGHT : 0)); + if (i >= itemList.getNumItems()) return LW_HT_DONTKNOW; + return i; +} + +int ListWnd::hitTest(int x, int y, int drag) { + POINT pt={x, y}; + return hitTest(pt, drag); +} + +void ListWnd::selectRect(int x1, int y1, int x2, int y2) { + +} + +void ListWnd::drawRect(int x1, int y1, int x2, int y2) { + HDC dc = GetDC(gethWnd()); + RECT r={x1,y1,x2,y2}; + DrawFocusRect(dc, &r); + ReleaseDC(gethWnd(), dc); +} + +LPARAM ListWnd::getItemData(int pos) { + if (pos >= itemList.getNumItems()) return 0; + listItem *item = itemList[pos]; + if (item) return item->data; + return NULL; +} + +int ListWnd::getItemLabel(int pos, int subpos, wchar_t *txt, int textmax) +{ + const wchar_t *t = getSubitemText(pos, subpos); + if (t) + { + WCSCPYN(txt, t, textmax); + return 1; + } + return 0; +} + +void ListWnd::setItemLabel(int pos, const wchar_t *text) +{ + if (pos >= itemList.getNumItems()) return; + listItem *item = itemList[pos]; + if (!item) return; + item->label = text; + invalidateItem(pos); +} + +void ListWnd::resort() { + itemList.sort(TRUE); + invalidate(); +} + +int ListWnd::getSortDirection() { + return sortdir; +} + +int ListWnd::getSortColumn() { + return sortcol; +} + +void ListWnd::setSortDirection(int dir) { + sortdir=dir; +} + +void ListWnd::setSortColumn(int col) { + sortcol=col; +} + +int ListWnd::sortCompareItem(listItem *p1, listItem *p2) +{ + const wchar_t *l1=p1->label; + const wchar_t *l2=p2->label; + + int i; + + if(sortcol!=0) + { + if (p1->subitems != NULL) + { + for (i=0;i<p1->subitems->getNumItems();i++) + { + listSubitemStruct *subitem = p1->subitems->enumItem(i); + if (subitem->column == sortcol) + { + l1=subitem->label; + break; + } + } + } else { + l1 = L""; + } + if (p2->subitems != NULL) { + for (i=0;i<p2->subitems->getNumItems();i++) { + listSubitemStruct *subitem = p2->subitems->enumItem(i); + if (subitem->column == sortcol) { + l2=subitem->label; + break; + } + } + } else { + l2 = L""; + } + } + + if(!columnsList.enumItem(sortcol)->getNumeric()) { + if(!sortdir) return(WCSICMP(l1, l2)); + else return(WCSICMP(l2, l1)); + } else { + int a=WTOI(l1),b=WTOI(l2); + if(!sortdir) return a-b; + else return b-a; + } +} + + +int ListWnd::findItemByParam(LPARAM param) { + for (int i=0;i<itemList.getNumItems();i++) { + if (itemList[i]->data == param) + return i; + } + return -1; +} + +void ListWnd::setItemParam(int pos, LPARAM param) { + if (pos >= itemList.getNumItems()) return; + itemList[pos]->data = param; + invalidateItem(pos); +} + +int ListWnd::deleteByPos(int pos) { + listItem *item = itemList[pos]; + if (item == NULL) return 0; + +// selItemList.setSelected(pos, FALSE); // If you do not delete the item from the selItemList, you corrupt it for all item positions downstream of this one. + selItemList.deleteByPos(pos); + + itemList.removeByPos(pos); + + onItemDelete(item->data); + + deleteListItem(item); item=NULL; + + if (redraw) { + invalidate(); + setSlidersPosition(); + } + return 1; +} + +void ListWnd::deleteAllItems() { + bool sav = getRedraw(); + deselectAll(); + setRedraw(FALSE); + selItemList.deselectAll(); //force desel so as to avoid the MEMCPY + while (itemList.getNumItems()) { + deleteByPos(0); + } + setRedraw(sav); +// setSlidersPosition(); +} + +void ListWnd::setSubItem(int pos, int subpos, const wchar_t *txt) +{ + if (pos >= itemList.getNumItems()) return; + listItem *item = itemList[pos]; + if (!item) return; + if (!item->subitems) + item->subitems = new PtrList<listSubitemStruct>; + for (int i=0;i<item->subitems->getNumItems();i++) { + listSubitemStruct *subitem = item->subitems->enumItem(i); + if (subitem->column == subpos) { + if (subitem->label) { + FREE(subitem->label); + subitem->label = NULL; + } + if (txt) + subitem->label = WCSDUP(txt); + invalidateItem(pos); + return; + } + } + listSubitemStruct *subitem = (listSubitemStruct *) MALLOC(sizeof(listSubitemStruct)); + item->subitems->addItem(subitem); + subitem->label = WCSDUP(txt); + subitem->column = subpos; + invalidateItem(pos); +} + +SkinBitmap *ListWnd::getItemIcon(int pos) +{ + if (pos >= itemList.getNumItems()) return NULL; + listItem *item = itemList[pos]; + return item->icon; +} + +void ListWnd::setItemIcon(int pos, const wchar_t *bitmapid) { + if (pos >= itemList.getNumItems()) return; + listItem *item = itemList[pos]; + item->icon = bitmapid; + invalidateItem(pos); +} + +const wchar_t *ListWnd::getSubitemText(int pos, int subpos) { + if (pos >= itemList.getNumItems()) return NULL; + listItem *item = itemList[pos]; + if (!item) return NULL; + if (subpos == 0) { + return item->label; + } + if (!item->subitems) return NULL; + for (int i=0;i<item->subitems->getNumItems();i++) { + listSubitemStruct *subitem = item->subitems->enumItem(i); + if (subitem->column == subpos) { + return subitem->label; + } + } + return NULL; +} + +listItem *ListWnd::createListItem() { + listItem *item = listItem_freelist.getRecord(); + new(item) listItem(); + item->setList(this); + return item; +} + +void ListWnd::deleteListItem(listItem *item) { + if (item == NULL) return; + item->~listItem(); + listItem_freelist.freeRecord(item); +} + +ListColumn *ListWnd::enumListColumn(int pos) { + return columnsList[pos]; +} + +int ListWnd::getColumnPosByName(const wchar_t *name) +{ + for (int i = 0; i < columnsList.getNumItems(); i++) + { + const wchar_t *name2 = columnsList[i]->getName(); + if (name2 == NULL) continue; + if (!wcscmp(name, name2)) return i; + } + return -1; +} + +int ListWnd::delColumnByPos(int pos) { + if (pos < 0 || pos >= columnsList.getNumItems()) return 0; + delete columnsList[pos]; + columnsList.removeByPos(pos); + recalcHeaders(); + return 1; +} + +void ListWnd::recalcHeaders() { + if (getNumColumns() <= 0) return; + + int wid = 0, ndynamic = 0; + for (int i = 0; i < getNumColumns(); i++) { + ListColumn *col = enumListColumn(i); + if (!col->isDynamic()) wid += col->getWidth()+COLUMNS_MARGIN-1; + else ndynamic++; + } + if (ndynamic == 0) return; + + RECT r = clientRect(); + int wwidth = (r.right - r.left) - 2; + + int leftover = wwidth - wid; + if (leftover <= 1) return; + + leftover--; + leftover /= ndynamic; + + for (int i = 0; i < getNumColumns(); i++) { + ListColumn *col = enumListColumn(i); + if (col->isDynamic()) col->setWidth(leftover); + } + + //BU note: we could probably find a way to not invalidate everything + invalidate(); +} + +void ListWnd::itemSelection(int itemnum, int selected) { + notifySelChanged(itemnum, selected); + onItemSelection(itemnum, selected); +} + +void ListWnd::onItemSelection(int itemnum, int selected) { + if (selected) { + Accessible *a = getAccessibleObject(); + if (a != NULL) + a->onGetFocus(itemnum); + } +} + +int ListWnd::doAddItem(const wchar_t *label, LPARAM lParam, int pos) +{ + listItem *item = createListItem(); + item->label = label; + item->data = lParam; + + itemList.addItem(item, pos, ITEMLIST_INC); + lastAddedItem = item; + + if (redraw) + { + invalidate(); // todo: optimize to invalidate only if necessary + setSlidersPosition(); + } + + if (isInited()) calcBounds(); + int p = (pos == POS_LAST) ? itemList.getNumItems()-1 : pos; + if (p <= selectionStart) selectionStart++; + if (autosort) { + itemList.sort(); + p = itemList.searchItem(item); + } + return p; +} + +void ListWnd::setMinimumSize(int size) { + if (size > 0) itemList.setMinimumSize(size); +} + +int ListWnd::addItem(const wchar_t *label, LPARAM lParam) +{ + return doAddItem(label, lParam, POS_LAST); +} + +int ListWnd::insertItem(int pos, const wchar_t *label, LPARAM lParam) +{ + return doAddItem(label, lParam, pos); +} + +int ListWnd::getLastAddedItemPos() { + if (lastAddedItem == NULL) return -1; + return itemList.searchItem(lastAddedItem); +} + +int ListWnd::getColumnsHeight() { + return columnsHeight; +} + +int ListWnd::getColumnsWidth() { + int i, x=0; + for (i=0;i<columnsList.getNumItems();i++) x+= columnsList[i]->getWidth(); + return x+1; +} + +int ListWnd::insertColumn(ListColumn *col, int pos, int alignment) +{ + ASSERT(col != NULL); + ASSERT(pos >= -1); + if (pos < 0) col->setIndex(columnsList.getNumItems()); + else col->setIndex(pos); + col->setList(this); + col->setAlignment(alignment); + columnsList.addItem(col); + if (pos >= 0) { + columnsList.moveItem(columnsList.getNumItems()-1, pos); + } + if (redraw && isInited()) { + invalidate(); + setSlidersPosition(); + } + recalcHeaders(); + return columnsList.getNumItems(); +} + +void ListWnd::deleteAllColumns() { + columnsList.deleteAll(); + if (redraw && isInited()) { + invalidate(); + setSlidersPosition(); + } + recalcHeaders(); +} + +int ListWnd::addColumn(const wchar_t *name, int width, int numeric, int align) +{ + ListColumn *col = new ListColumn(); + col->setWidth(width); + col->setLabel(name); + col->setNumeric(numeric); + col->setAlignment(align); + return insertColumn(col, -1, align); +} + +bool ListWnd::setRedraw(bool _redraw) { + bool prev = redraw; + if (!redraw && _redraw) { + invalidate(); + setSlidersPosition(); + notifySelChanged(); + } + redraw = _redraw; + return prev; +} + +bool ListWnd::getRedraw() { + return redraw; +} + +int ListWnd::onChar(unsigned int c) { + //CT> Commented this out so shortcuts work in playlist editor + /*char b = TOUPPER(c); + if (b >= 'A' && b <= 'Z') { + jumpToNext(b); + return 1; + }*/ + + return LISTWND_PARENT::onChar(c); +} + +int ListWnd::onKeyDown(int keyCode) { + switch (keyCode) { + case VK_DOWN: + next(selectonupdown); + return 1; + case VK_UP: + previous(selectonupdown); + return 1; + case VK_PRIOR: + pageup(selectonupdown); + return 1; + case VK_NEXT: + pagedown(selectonupdown); + return 1; + case VK_HOME: + home(selectonupdown); + return 1; + case VK_END: + end(selectonupdown); + return 1; + case VK_DELETE: + onDelete(); + return 1; + case VK_RETURN: { + int i=getItemFocused(); + //setSelected(i, 0, 0); + //setSelected(i, 1, 1); + if(i!=-1) + onDoubleClick(i); + return 1; + } + } + return LISTWND_PARENT::onKeyDown(keyCode); +} + +void ListWnd::next(int wantcb) { + int from=getItemFocused(); +/* if (selItemList.getNumItems() > 0) + for (int i=0;i<itemList.getNumItems();i++) + if (getItemSelected(i)) { + from = i; + break; + }*/ + int to = from + 1; + if (to < itemList.getNumItems() && to >= 0) { + setSelectionStart(to, wantcb); + if (!fullyVisible(to)) { + RECT c; + getClientRect(&c); + scrollToY((Y_SHIFT*2+(to+1)*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0)))-(c.bottom-c.top)); + } + if (!wantcb) + { + wchar_t t[256]=L""; + getItemLabel(to, 0, t, 255); + foreach(tempselectnotifies) + sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t); + endfor; + } + } +} + +void ListWnd::selectCurrent() { + int from=getItemFocused(); + int to = from; + if (to < itemList.getNumItems() && to >= 0) { + setSelectionStart(to); + if (!fullyVisible(to)) { + RECT c; + getClientRect(&c); + scrollToY((Y_SHIFT*2+(to+1)*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0)))-(c.bottom-c.top)); + } + } +} + +void ListWnd::selectFirstEntry(int wantcb) { + setSelectionStart(0, wantcb); + ensureItemVisible(0); +} + +void ListWnd::previous(int wantcb) { + int from=0; +/* if (selItemList.getNumItems() > 0) + for (int i=0;i<itemList.getNumItems();i++) + if (getItemSelected(i)) { + from = i; + break; + }*/ + from = getItemFocused(); + int to = from - 1; + if (to < itemList.getNumItems() && to >= 0) { + setSelectionStart(to, wantcb); + ensureItemVisible(to); + if (!wantcb) { + wchar_t t[256]=L""; + getItemLabel(to, 0, t, 255); + foreach(tempselectnotifies) + sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t); + endfor; + } + } +} + +void ListWnd::pagedown(int wantcb) { + int from=-1,to; + if (selItemList.getNumSelected()) + for (int i=0;i<itemList.getNumItems();i++) + if (getItemSelected(i)) { + from = i; + break; + } + if(from==-1) to = 0; + else to = from + getLinesPerPage(); + to=MIN(to,itemList.getNumItems()-1); + if(to>=0) { + setSelectionStart(to, wantcb); + if (!fullyVisible(to)) { + RECT c; + getClientRect(&c); + scrollToY((Y_SHIFT*2+(to+1)*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0)))-(c.bottom-c.top)); + } + if (!wantcb) { + wchar_t t[256]=L""; + getItemLabel(to, 0, t, 255); + foreach(tempselectnotifies) + sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t); + endfor; + } + } +} + +void ListWnd::pageup(int wantcb) { + int from=-1,to; + if (selItemList.getNumSelected()) + for (int i=0;i<itemList.getNumItems();i++) + if (getItemSelected(i)) { + from = i; + break; + } + if(from==-1) to = 0; + else to = from - getLinesPerPage(); + to=MAX(to,0); + to=MIN(to,itemList.getNumItems()-1); + if(to>=0) { + setSelectionStart(to, wantcb); + ensureItemVisible(to); + if (!wantcb) { + wchar_t t[256]=L""; + getItemLabel(to, 0, t, 255); + foreach(tempselectnotifies) + sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t); + endfor; + } + } +} + +void ListWnd::home(int wantcb) { + if(!itemList.getNumItems()) return; + setSelectionStart(0, wantcb); + ensureItemVisible(0); + if (!wantcb) { + wchar_t t[256]=L""; + getItemLabel(0, 0, t, 255); + foreach(tempselectnotifies) + sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t); + endfor; + } +} + +void ListWnd::end(int wantcb) { + if(!itemList.getNumItems()) return; + int i=itemList.getNumItems()-1; + setSelectionStart(i, wantcb); + if (!fullyVisible(i)) { + RECT c; + getClientRect(&c); + scrollToY((Y_SHIFT*2+(i+1)*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0)))-(c.bottom-c.top)); + } + if (!wantcb) { + wchar_t t[256]=L""; + getItemLabel(i, 0, t, 255); + foreach(tempselectnotifies) + sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t); + endfor; + } +} + +void ListWnd::jumpToNext(wchar_t c) +{ + if (doJumpToNext(c, FALSE)) return; + doJumpToNext(c, TRUE); +} + +int ListWnd::doJumpToNext(wchar_t c, bool fromtop) +{ + int from = 0; + if (!fromtop && selItemList.getNumSelected()) + { + for (int i=0;i<itemList.getNumItems();i++) + if (getItemSelected(i)) + { + from = i+1; + break; + } + } + for (int j=from;j<itemList.getNumItems();j++) + { + listItem *item = itemList[j]; + if (item->label != NULL) { + wchar_t z = TOUPPERW(*(item->label)); + if (z == c) + { + setSelectionStart(j); + ensureItemVisible(j); + return 1; + } + } + } + return 0; +} + +void ListWnd::reset() { + columnsList.deleteAll(); + deleteAllItems(); +} + +void ListWnd::setShowColumnsHeaders(int show) { + int prev = showColumnsHeaders; + showColumnsHeaders = !!show; + if (prev != show) { + invalidate(); + } +} + +int ListWnd::onContextMenu (int x, int y) { + return notifyParent(ChildNotify::LISTWND_POPUPMENU, x, y); +} + +void ListWnd::scrollUp(int lines) { + scrollToY(MAX(0, getScrollY()-getItemHeight()*lines)); +} + +void ListWnd::scrollLeft(int lines) { + scrollToX(MAX(0, getScrollX()-getItemHeight()*lines)); +} + +void ListWnd::scrollDown(int lines) { + scrollToY(MIN(getMaxScrollY(), getScrollY()+getItemHeight()*lines)); +} + +void ListWnd::scrollRight(int lines) { + scrollToX(MIN(getMaxScrollX(), getScrollX()+getItemHeight()*lines)); +} + +int ListWnd::onMouseWheelUp(int clicked, int lines) { + lines *= Wasabi::Std::osparam_getScrollLines(); + if (!clicked) + scrollUp(lines); + else + scrollLeft(lines); + return 1; +} + +int ListWnd::onMouseWheelDown(int clicked, int lines) { + lines *= Wasabi::Std::osparam_getScrollLines(); + if (!clicked) + scrollDown(lines); + else + scrollRight(lines); + return 1; +} + +int ListWnd::onColumnLabelClick(int col, int x, int y) +{ + if(lastcolsort==col) { + setSortDirection(1); + lastcolsort=-1; + } else { + setSortDirection(0); + lastcolsort=col; + } + setSortColumn(col); + resort(); + return 1; +} + +ARGB32 ListWnd::getTextColor(LPARAM lParam) { + return textColor; +} + +ARGB32 ListWnd::getSelBgColor(LPARAM LParam) { + return color_item_selected; +} + +ARGB32 ListWnd::getSelFgColor(LPARAM LParam) { + ARGB32 r = color_item_selected_fg; + if (r == 0xFFFFFFFF) + return color_item_selected_fg; + return r; +} + +ARGB32 ListWnd::getBgColor() { + return bgcolor; +} + +ARGB32 ListWnd::getFocusColor(LPARAM LParam) { + return color_item_focused; +} + +ARGB32 ListWnd::getFocusRectColor(LPARAM lParam) { + return color_item_focusrect; +} + + +void ListWnd::moveItem(int from, int to) { + itemList.moveItem(from,to); + invalidate(); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ListColumn +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +ListColumn::ListColumn(const wchar_t *name, int isdynamic) +: NamedW(name), dynamic(isdynamic) +{ + align=COL_LEFTALIGN; + index = -1; + width = COLUMNS_DEFAULT_WIDTH; + list = NULL; + numeric = 0; +} + +void ListColumn::setLabel(const wchar_t *newlabel) { + setName(newlabel); +} + +const wchar_t *ListColumn::getLabel() { + return getName(); +} + +void ListColumn::setIndex(int newindex) { + index = newindex; +} + +int ListColumn::getIndex() { + return index; +} + +void ListColumn::setWidth(int _width) { + width = _width; + if (list && list->getRedraw()) { + list->invalidate(); + } +} + +int ListColumn::getWidth() { + return width; +} + +int ListColumn::customDrawHeader(Canvas *c, RECT *r, const Wasabi::FontInfo *fontInfo) +{ + int y = (r->bottom-r->top-c->getTextHeight(fontInfo)) / 2; + c->textOutEllipsed(r->left, y, r->right-r->left, c->getTextHeight(fontInfo), _(getName()), fontInfo); + return 1; +} + +void ListColumn::setDynamic(int isdynamic) +{ + int prev = dynamic; + dynamic = !!isdynamic; + if (prev != dynamic && dynamic && list != NULL) + list->recalcHeaders(); +} + +void ListColumn::setList(ListWnd *_list) { + list = _list; +} + +ListWnd *ListColumn::getList() { + return list; +} + +int ListWnd::wantAutoContextMenu() { + return 0; +} + +int ListWnd::onAcceleratorEvent(const wchar_t *name) { + if(!_wcsicmp(name, L"selectall")) { + selectAll(); + return 1; + } + return 0; +} + +int ListWnd::onAction(const wchar_t *action, const wchar_t *param, int x, int y, intptr_t p1, intptr_t p2, void *data, size_t datalen, ifc_window *source) { + int r = LISTWND_PARENT::onAction(action, param, x, y, p1, p2, data, datalen, source); + if (WCSCASEEQLSAFE(action, L"register_tempselectnotify")) { + tempselectnotifies.addItem(source); + } + else if (WCSCASEEQLSAFE(action, L"up")) { + previous((int)p1); + } + else if (WCSCASEEQLSAFE(action, L"down")) { + next((int)p1); + } + else if (WCSCASEEQLSAFE(action, L"home")) { + home((int)p1); + } + else if (WCSCASEEQLSAFE(action, L"end")) { + end((int)p1); + } + else if (WCSCASEEQLSAFE(action, L"pageup")) { + pageup((int)p1); + } + else if (WCSCASEEQLSAFE(action, L"pagedown")) { + pagedown((int)p1); + } + else if (WCSCASEEQLSAFE(action, L"select_current")) { + selectCurrent(); + } + else if (WCSCASEEQLSAFE(action, L"doubleclick")) { + int pos = getItemFocused(); + if (pos >= 0) onDoubleClick(pos); + return 1; + } + return r; +} + +void ListWnd::setShowIcons(int icons) +{ + showicons = icons; + invalidate(); +} + +int ListWnd::getShowIcons () +{ + return showicons; +} + +int ListWnd::getIconWidth () +{ + // The old behaviour used the itemheight as value, so we return this for backwards compatibility if iconWidth is negative + return (iconWidth < 0 ? itemHeight-2 : iconWidth); +} + +void ListWnd::setIconWidth (int width) +{ + iconWidth = width; + invalidate(); +} + +int ListWnd::getIconHeight () +{ + // The old behaviour used the itemheight as value, so we return this for backwards compatibility if iconWidth is negative + return (iconHeight < 0 ? itemHeight-2 : iconHeight); +} + +void ListWnd::setIconHeight (int height) +{ + iconHeight = height; + invalidate(); +} + +int ListWnd::getItemHeight () +{ + return (itemHeight < getIconHeight()+2) ? getIconHeight()+2 : itemHeight; +} + +void ListWnd::setItemHeight (int height, bool forceInvalidate /*true*/) +{ + itemHeight = height; + if (forceInvalidate) invalidate(); +}
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/listwnd.h b/Src/Wasabi/api/wnd/wndclass/listwnd.h new file mode 100644 index 00000000..018986ca --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/listwnd.h @@ -0,0 +1,459 @@ +#ifndef _LISTWND_H +#define _LISTWND_H + +#include <api/wnd/wndclass/scbkgwnd.h> +#include <bfc/common.h> + +#include <bfc/freelist.h> +#include "SelItemList.h" +#define POS_LAST -1 + +#define LISTWND_PARENT ScrlBkgWnd + +#define LW_HT_DONTKNOW (-1) +#define LW_HT_ABOVE (-10) +#define LW_HT_BELOW (-20) + +#define COL_LEFTALIGN 0 +#define COL_CENTERALIGN 1 +#define COL_RIGHTALIGN 2 + +class listItem; +class ListWnd; +class CompareListItem; + +class ListColumn : public NamedW +{ +friend class ListWnd; +public: + ListColumn(const wchar_t *name=NULL, int isdynamic=FALSE); + virtual ~ListColumn() { } + + int getWidth(); + void setWidth(int newwidth); + const wchar_t *getLabel(); + void setLabel(const wchar_t *newlabel); + virtual int customDrawHeader(Canvas *c, RECT *cr, const Wasabi::FontInfo *fontInfo); + virtual int onHeaderClick() { return 0; }//return 1 if you override + virtual int onColumnLeftClick(int pos) { return 0; }//return 1 if you override + int getNumeric() { return numeric; } + void setDynamic(int isdynamic); + int isDynamic() { return dynamic; } + void setAlignment(int _align) { align = _align; } + int getAlignment() { return align; } + +protected: + void setIndex(int i); + int getIndex(); + void setList(ListWnd *list); + ListWnd *getList(); + + void setNumeric(int n) { numeric=n; } + +private: + int width; + int index; + int numeric; + int dynamic; + ListWnd *list; + int align; +}; + +//class SelItemList; + + + +class ListWnd : public ScrlBkgWnd +{ +friend class ListColumn; +friend class SelItemList; +public: + ListWnd(); + + virtual ~ListWnd(); + virtual int onInit(); + virtual int onPostOnInit(); + virtual int onPaint(Canvas *canvas); + virtual int onResize(); + virtual int onLeftButtonDown(int x, int y); + virtual int onLeftButtonUp(int x, int y); + virtual int onRightButtonDown(int x, int y); + virtual int onRightButtonUp(int x, int y); + virtual int onMouseMove(int x, int y); + virtual int onLeftButtonDblClk(int x, int y); + virtual int onChar(unsigned int c); + virtual int onKeyDown(int keyCode); + virtual int onContextMenu (int x, int y); + virtual int wantAutoContextMenu(); + virtual int onMouseWheelUp(int click, int lines); + virtual int onMouseWheelDown(int click, int lines); + virtual int wantAutoDeselect() { return wantautodeselect; } + virtual void setWantAutoDeselect(int want) { wantautodeselect = want; } + + void onSetVisible(int show); + + void setAutoSort(bool dosort); + void setOwnerDraw(bool doownerdraw); + + virtual void timerCallback(int id); + + void next(int wantcb=1); + void selectCurrent(); + void selectFirstEntry(int wantcb=1); + void previous(int wantcb=1); + void pagedown(int wantcb=1); + void pageup(int wantcb=1); + void home(int wantcb=1); + void end(int wantcb=1); + void setItemCount(int c); + void reset(); + void setShowColumnsHeaders(int show); + int addColumn(const wchar_t *name, int width, int numeric=0, int align=COL_LEFTALIGN); // adds to end + ListColumn *getColumn(int n); + int getNumColumns(); + int getColumnWidth(int col); + bool setRedraw(bool redraw); // returns prev state + bool getRedraw(); + void setMinimumSize(int size); + virtual int addItem(const wchar_t *label, LPARAM lParam); + virtual int insertItem(int pos, const wchar_t *label, LPARAM lParam); + virtual int getLastAddedItemPos(); + virtual void setSubItem(int pos, int subpos, const wchar_t *txt); + virtual void deleteAllItems(); + virtual int deleteByPos(int pos); + int getNumItems(void); + + virtual int getItemLabel(int pos, int subpos, wchar_t *text, int textmax); + virtual void setItemLabel(int pos, const wchar_t *text); + virtual LPARAM getItemData(int pos); + virtual int getItemRect(int pos, RECT *r); + virtual int getItemSelected(int pos); // returns 1 if selected + virtual int getItemFocused(int pos); // returns 1 if focused + virtual int getItemFocused(); // returns focused item + void setItemFocused(int pos, int ensure_visible=TRUE); + void ensureItemVisible(int pos); + void invalidateColumns(); + virtual int scrollAbsolute(int x); + virtual int scrollRelative(int x); + virtual void scrollLeft(int lines=1); + virtual void scrollRight(int lines=1); + virtual void scrollUp(int lines=1); + virtual void scrollDown(int lines=1); + virtual const wchar_t *getSubitemText(int pos, int subpos); + + + int getFirstItemSelected(); + + + int getNextItemSelected(int lastpos); // next item AFTER given pos + + + virtual int selectAll(int cb=1); // force all items selected + + virtual int deselectAll(int cb=1); // force all items to be deselected + + virtual int invertSelection(int cb=1); // invert all selections + + virtual int hitTest(POINT pos, int drag=0); + + /** + Method + + @see + @ret + */ + virtual int hitTest(int x, int y, int drag=0); + + /** + Method + + @see + @ret + */ + virtual int invalidateItem(int pos); + virtual int locateData(LPARAM data); + + // -1 if we've never been drawn yet + + /** + Method + + @see + @ret + */ + int getFirstItemVisible() const { return firstItemVisible; } + + /** + Method + + @see + @ret + */ + int getLastItemVisible() const { return lastItemVisible; } + + virtual int setFontSize(int size); + + virtual int getFontSize(); + virtual void jumpToNext(wchar_t c); + int wantFocus() { return 1; } + void scrollToItem(int pos); + virtual void resort(); + int getSortDirection(); + + /** + Method + + @see + @ret + */ + int getSortColumn(); + + void setSortColumn(int col); + + void setSortDirection(int dir); + + int findItemByParam(LPARAM param); + + void setItemParam(int pos, LPARAM param); + + int getItemCount() { return getNumItems(); } + + void setSelectionStart(int pos, int wantcb=1); + + /** + Method + + @see + @ret + */ + virtual void setSelectionEnd(int pos); + + void setSelected(int pos, int selected, int cb=1); + void toggleSelection(int pos, int setfocus=TRUE, int cb=1); + virtual int getHeaderHeight(); + + // this sort function just provides string/numeric comparison + // if you need more types, just override and provide your own + + virtual int sortCompareItem(listItem *p1, listItem *p2); + + int getPreventMultipleSelection() { return preventMultipleSelection; } + int setPreventMultipleSelection(int val) { return preventMultipleSelection = val; } + void moveItem(int from, int to); + virtual int onAcceleratorEvent(const wchar_t *name); + + // override this to turn the LPARAM into a text + virtual const wchar_t *convertlParam(LPARAM lParam) { return NULL; } + virtual void convertlParamColumn(int col, int pos, LPARAM param, wchar_t *str, int maxlen) { }; + +protected: + /*static */void CreateXMLParameters(int master_handle); + + // return 1 if you override this + + virtual int ownerDraw(Canvas *canvas, int pos, RECT *r, LPARAM lParam, int selected, int focused) { return 0; }; + virtual void onPreItemDraw(Canvas *canvas, int pos, RECT *r, LPARAM lParam, int selected, int focused) { } + virtual void onPostItemDraw(Canvas *canvas, int pos, RECT *r, LPARAM lParam, int selected, int focused) { }; + virtual ARGB32 getTextColor(LPARAM lParam); + int getTextAntialias(LPARAM lParam) { return antialias; } + virtual int getTextBold(LPARAM lParam) { return 0; } + virtual int getTextItalic(LPARAM lParam) { return 0; } + virtual ARGB32 getSelBgColor(LPARAM lParam); + virtual ARGB32 getSelFgColor(LPARAM lParam); + virtual ARGB32 getBgColor(); + virtual ARGB32 getFocusColor(LPARAM lParam); + virtual ARGB32 getFocusRectColor(LPARAM lParam); + virtual int needFocusRect(LPARAM lParam) { return 0; } + virtual ARGB32 getColumnSepColor(); + virtual int wantColSepOnItems(); + virtual int getXShift(); + +public: + int insertColumn(ListColumn *col, int pos=-1, int alignment=COL_LEFTALIGN);// -1 is add to end +// void deleteColumn(int pos); + void deleteAllColumns(); + + void setHoverSelect(int a) { hoverselect = a; } + int getHoverSelect() { return hoverselect; } + + void setSelectOnUpDown(int i) { selectonupdown = i; } + int getSelectOnUpDown() { return selectonupdown; } + virtual int onAction(const wchar_t *action, const wchar_t *param=NULL, int x=-1, int y=-1, intptr_t p1=0, intptr_t p2=0, void *data=NULL, size_t datalen=0, ifc_window *source=NULL); + + /** + Method + Will only work with simple text lists, be forwarned!!! + + @see + @ret + */ + int getItemHeight(); + void setItemHeight(int height, bool forceInvalidate = true); + + int getIconWidth(); + void setIconWidth(int width); + int getIconHeight(); + void setIconHeight(int height); + +protected: + + virtual int getColumnsHeight(); + virtual int getColumnsWidth(); + virtual int getContentsWidth(); + virtual int getContentsHeight(); + + virtual void drawBackground(Canvas *canvas); + + void drawColumnHeaders(Canvas *c); + + void drawItems(Canvas *canvas); + + void updateScrollX(); + + void updateScrollY(); + int doJumpToNext(wchar_t c, bool fromTop); + int fullyVisible(int pos); + + virtual int onBeginDrag(int iItem); + + + virtual int dragOver(int x, int y, ifc_window *sourceWnd); + virtual void onSelectAll(); // hit Control-A + + virtual void onDelete(); // hit 'delete' + + virtual void onItemDelete(LPARAM lparam) {} + + virtual void onDoubleClick(int itemnum); // double-click on an item + // this is called with the selected item# + + virtual void onLeftClick(int itemnum); // left-click + // the second time you click on an already-focused item + + virtual void onSecondLeftClick(int itemnum); + // this is called once for the item under cursor on click + + virtual int onRightClick(int itemnum); // right-click on item + + virtual int onIconLeftClick(int itemnum, int x, int y); // Returns 1 if we should not invoke onLeftClick() + + // override this to be notified of item selections & deselections + + virtual void onItemSelection(int itemnum, int selected); + + virtual int onColumnDblClick(int col, int x, int y) { return 0; } + + virtual int onColumnLabelClick(int col, int x, int y); + + void selectRect(int x1, int y1, int x2, int y2); + + void drawRect(int x1, int y1, int x2, int y2); + + // interface to Freelist + + listItem *createListItem(); + void deleteListItem(listItem *item); + ListColumn *enumListColumn(int pos); + + int getColumnPosByName(const wchar_t *name); + + int delColumnByPos(int pos); +public: // Martin> dunno why these were protected... + void setShowIcons(int icons); + int getShowIcons(); // Maybe useful or not + SkinBitmap *getItemIcon(int item); + void setItemIcon(int pos, const wchar_t *bitmapid); + +protected: + int item_invalidate_border; + bool showColumnsHeaders; + void recalcHeaders(); + void itemSelection(int itemnum, int selected); + +private: + int doAddItem(const wchar_t *label, LPARAM lParam, int pos); + + + int hitTestColumns(POINT p, int *origin=NULL); + int hitTestColumnClient(int x); + int hitTestColumnsLabel(POINT p); + void drawXorLine(int x); + void calcNewColWidth(int col, int x); + void calcBounds(); + void onDragTimer(); + void notifySelChanged(int item=-1, int sel=-1); + virtual int wantResizeCols() { return 1; } + + int autosort, ownerdraw; + int textsize; + int itemHeight; + int iconWidth; // If it's still negative use itemHeight instead -- better user getIconWidth() + int iconHeight; + bool metrics_ok; + bool redraw; + int columnsHeight; + int dragtimeron; + + int antialias; + + PtrList<ListColumn> columnsList; + PtrListQuickSorted<listItem,CompareListItem> itemList; + + int firstItemVisible; + int lastItemVisible; + + listItem *lastItemFocused; + int lastItemFocusedPos; + + listItem *lastAddedItem; + SelItemList selItemList; + + int dragskip; + int dragskipcount; + int selectionStart; + int colresize; + POINT colresizept; + bool resizing_col; + int colresizeo; + + bool processbup; + bool bdown; + bool nodrag; + int bdownx, bdowny; + bool firstComplete, lastComplete; + + int rectselecting; + POINT selectStart; + POINT selectLast; + + int sortdir, sortcol, lastcolsort; + + int preventMultipleSelection; + + Freelist<listItem> listItem_freelist; + int wantautodeselect; + + int hoverselect; + int selectonupdown; + PtrList<ifc_window> tempselectnotifies; + StringW accessibleItemName; + int showicons; + +private: + /* XML Parameters */ + static XMLParamPair params[]; + int xuihandle; + bool hasUserBg; + + enum + { + LIST_ANTIALIAS = 0, + LIST_BACKGROUND, + LIST_TILE, + LIST_NOCOLHEADER, + }; + protected: + int setXuiParam(int xuihandle, int xmlattributeid, const wchar_t *xmlattributename, const wchar_t *value); +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/oswnd.cpp b/Src/Wasabi/api/wnd/wndclass/oswnd.cpp new file mode 100644 index 00000000..021327ab --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/oswnd.cpp @@ -0,0 +1,32 @@ +#include <precomp.h> +#include "oswnd.h" + +int OSWnd::onInit() +{ + OSWND_PARENT::onInit(); + onSetVisible(isVisible()); + return 1; +} + +void OSWnd::onSetVisible(int show) +{ +#ifdef WIN32 + ShowWindow(getOSHandle(), show ? SW_NORMAL : SW_HIDE); +#endif +} + +int OSWnd::onResize() +{ + OSWND_PARENT::onResize(); +#ifdef WIN32 + if (getOSHandle()) + { + RECT r; + getClientRect(&r); + SetWindowPos(getOSHandle(), NULL, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE); + } +#endif + return 1; +} + + diff --git a/Src/Wasabi/api/wnd/wndclass/oswnd.h b/Src/Wasabi/api/wnd/wndclass/oswnd.h new file mode 100644 index 00000000..7093dd6e --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/oswnd.h @@ -0,0 +1,19 @@ +#ifndef __OSWND_H +#define __OSWND_H + +#include <api/wnd/wndclass/guiobjwnd.h> + +#define OSWND_PARENT GuiObjectWnd + +class OSWnd : public OSWND_PARENT +{ +public: + virtual int onInit(); + virtual void onSetVisible(int show); + virtual int onResize(); + virtual int handleRatio() { return 0; } + + virtual HWND getOSHandle() = 0; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/oswndhost.cpp b/Src/Wasabi/api/wnd/wndclass/oswndhost.cpp new file mode 100644 index 00000000..ab1a9680 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/oswndhost.cpp @@ -0,0 +1,14 @@ +#include <precomp.h> +#include "oswndhost.h" + +#ifdef CBCLASS +#undef CBCLASS +#endif +#define CBCLASS OSWndHostI +START_DISPATCH; + VCB(OSWNDHOST_OSWNDHOST_HOST, oswndhost_host); + VCB(OSWNDHOST_OSWNDHOST_UNHOST, oswndhost_unhost); + VCB(OSWNDHOST_OSWNDHOST_SETREGIONOFFSETS, oswndhost_setRegionOffsets); +END_DISPATCH; + + diff --git a/Src/Wasabi/api/wnd/wndclass/oswndhost.h b/Src/Wasabi/api/wnd/wndclass/oswndhost.h new file mode 100644 index 00000000..8e7503e5 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/oswndhost.h @@ -0,0 +1,53 @@ +#ifndef __OSWNDHOST_H +#define __OSWNDHOST_H + +#include <bfc/dispatch.h> + +// {7050AACF-2731-4319-AF32-4ECE4CC8BDC4} +static const GUID osWndHostGuid = + { 0x7050aacf, 0x2731, 0x4319, { 0xaf, 0x32, 0x4e, 0xce, 0x4c, 0xc8, 0xbd, 0xc4 } }; + + +class OSWndHost : public Dispatchable +{ +public: + void oswndhost_host(HWND oswnd); + void oswndhost_unhost(); + void oswndhost_setRegionOffsets(RECT *r); + + DISPATCH_CODES + { + OSWNDHOST_OSWNDHOST_HOST = 0, + OSWNDHOST_OSWNDHOST_UNHOST = 5, + OSWNDHOST_OSWNDHOST_SETREGIONOFFSETS = 10, + }; +}; + +inline void OSWndHost::oswndhost_host(HWND oswnd) +{ + _voidcall(OSWNDHOST_OSWNDHOST_HOST, oswnd); +} + +inline void OSWndHost::oswndhost_unhost() +{ + _voidcall(OSWNDHOST_OSWNDHOST_UNHOST); +} + +inline void OSWndHost::oswndhost_setRegionOffsets(RECT *r) +{ + _voidcall(OSWNDHOST_OSWNDHOST_SETREGIONOFFSETS, r); +} + +class OSWndHostI : public OSWndHost +{ +public: + virtual void oswndhost_host(HWND oswnd) = 0; + virtual void oswndhost_unhost() = 0; + virtual void oswndhost_setRegionOffsets(RECT *r) = 0; + +protected: + RECVS_DISPATCH; +}; + + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/qpaintwnd.cpp b/Src/Wasabi/api/wnd/wndclass/qpaintwnd.cpp new file mode 100644 index 00000000..dfdccc18 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/qpaintwnd.cpp @@ -0,0 +1,357 @@ +#include <precomp.h> +#include "qpaintwnd.h" +#include <tataki/canvas/bltcanvas.h> +#include "../nu/threadpool/TimerHandle.hpp" + +#define TIMER_QUICKPAINT 0x650 + +// thread context, this is here so we can avoid the windows types in quickpaintwnd.h +class QuickPaintContext +{ +public: + QuickPaintContext(QuickPaintWnd *_wnd, int timeout) + { + killswitch = 0; + wnd = _wnd; + death = CreateEvent(NULL, FALSE, FALSE, NULL); + timer_ms = timeout; + WASABI_API_THREADPOOL->AddHandle(0, timer_handle, QPThreadPoolFunc, (void *)this, 0, 0/*api_threadpool::FLAG_LONG_EXECUTION*/); + timer_handle.Wait(timer_ms); + } + + ~QuickPaintContext() + { + CloseHandle(death); + timer_handle.Close(); + } + + void Kill() + { + InterlockedExchangePointer((volatile PVOID*)&wnd, 0); + killswitch=1; + WaitForSingleObject(death, INFINITE); + } + + QuickPaintWnd *wnd; + TimerHandle timer_handle; + HANDLE death; + volatile int killswitch; + int timer_ms; + static int QPThreadPoolFunc(HANDLE h, void *user_data, intptr_t t); +}; + +int QuickPaintContext::QPThreadPoolFunc(HANDLE h, void *user_data, intptr_t t) +{ + QuickPaintContext *context = (QuickPaintContext *)user_data; + + if (context->killswitch) + { + WASABI_API_THREADPOOL->RemoveHandle(0, h); + SetEvent(context->death); + } + else + { + DWORD start = GetTickCount(); + QuickPaintWnd *wnd = 0; + InterlockedExchangePointer((volatile PVOID*)&wnd, context->wnd); + if (wnd) + { + wnd->quickPaint(); + TimerHandle timer_handle(h); + DWORD end = GetTickCount(); + if (end-start > (DWORD)context->timer_ms) + timer_handle.Wait(1); + else + timer_handle.Wait(context->timer_ms - (end - start)); + } + } + + return 0; +} + + +// ----------------------------------------------------------------------- +QuickPaintWnd::QuickPaintWnd() +{ + invalidates_required = 0; + realtime = 1; + canvas_w = -1; + canvas_h = -1; + timerset = 0; + speed = 25; + enabled = 0; + render_canvas1 = NULL; + render_canvas2 = NULL; + paint_canvas = NULL; + thread_context = 0; + + while (1) + { + svc_skinFilter *obj = sfe.getNext(); + if (!obj) break; + filters.addItem(obj); + } + invalidated = 0; +} + +// ----------------------------------------------------------------------- +QuickPaintWnd::~QuickPaintWnd() +{ + foreach(filters) + sfe.release(filters.getfor()); + endfor; + + KillThread(); + + delete render_canvas1; + delete render_canvas2; +} + +// ----------------------------------------------------------------------- +void QuickPaintWnd::setRealtime(int rt) +{ + realtime = rt; +} + +// ----------------------------------------------------------------------- +int QuickPaintWnd::getRealtime() const +{ + return realtime; +} + +// ----------------------------------------------------------------------- +void QuickPaintWnd::setSpeed(int ms) +{ + speed = ms; + if (enabled && timerset) + { + if (thread_context) + thread_context->timer_ms = ms; + // let it change the timer value on the invalidate() timer + killTimer(TIMER_QUICKPAINT); + setTimer(TIMER_QUICKPAINT, getSpeed()); + } +} + +// ----------------------------------------------------------------------- +void QuickPaintWnd::startQuickPaint() +{ + enabled = 1; + if (!isInited()) return; + CreateRenderThread(); + timerset=1; +} + +// ----------------------------------------------------------------------- +void QuickPaintWnd::stopQuickPaint() +{ + enabled = 0; + if (!isInited()) return; + KillThread(); + timerset=0; +} + +// ----------------------------------------------------------------------- +int QuickPaintWnd::isQuickPainting() +{ + return enabled; +} + +// ----------------------------------------------------------------------- +int QuickPaintWnd::getSpeed() +{ + return speed; +} + +// ----------------------------------------------------------------------- +int QuickPaintWnd::onInit() +{ + QUICKPAINTWND_PARENT::onInit(); + if (enabled) + { + ASSERT(!thread_context); + CreateRenderThread(); + timerset = 1; + } + return 1; +} + +// ----------------------------------------------------------------------- +void QuickPaintWnd::timerCallback(int id) +{ + switch (id) + { + case TIMER_QUICKPAINT: + if (invalidates_required) + { + invalidated = 1; + + if (getRealtime() && isVisible() && !isMinimized()) + cascadeRepaint(); + else + invalidate(); + + InterlockedExchange(&invalidates_required, 0); + } + //quickPaint(); + break; + default: + QUICKPAINTWND_PARENT::timerCallback(id); + } +} + +void QuickPaintWnd::SetPaintingCanvas(BltCanvas *c) +{ + InterlockedExchangePointer((volatile PVOID*)&paint_canvas, c); +} + +BltCanvas *&QuickPaintWnd::GetDrawingConvas() +{ + if (paint_canvas == render_canvas2) + return render_canvas1; + else + return render_canvas2; +} + +// ----------------------------------------------------------------------- +int QuickPaintWnd::quickPaint() +{ + int repaint=0; + + int w, h; + getQuickPaintSize(&w, &h); + + if (wantEvenAlignment()) + { + if (w & 1) w++; + if (h & 1) h++; + } + + if (w == 0 && h == 0) return 0; + + BltCanvas *&render_canvas = GetDrawingConvas(); + int newone = 0; + + if (canvas_w != w || canvas_h != h) + { + delete render_canvas1; render_canvas1=0; + delete render_canvas2; render_canvas2=0; + } + + if (!render_canvas) + { + render_canvas = new BltCanvas(w, wantNegativeHeight() ? -h : h, getOsWindowHandle()); + canvas_w = w; + canvas_h = h; + newone = 1; + } + + repaint = onQuickPaint(render_canvas, canvas_w, canvas_h, newone); + + SetPaintingCanvas(render_canvas); + if (repaint) + InterlockedIncrement(&invalidates_required); + + return repaint; +} + +// ----------------------------------------------------------------------- +void QuickPaintWnd::getQuickPaintSize(int *w, int *h) +{ + RECT r; + getClientRect(&r); + if (w) *w = r.right - r.left; + if (h) *h = r.bottom - r.top; +} + +// ----------------------------------------------------------------------- +void QuickPaintWnd::getQuickPaintSource(RECT *r) +{ + ASSERT(r != NULL); + r->left = 0; + r->right = canvas_w; + r->top = 0; + r->bottom = canvas_h; +} + +// ----------------------------------------------------------------------- +void QuickPaintWnd::getQuickPaintDest(RECT *r) +{ + ASSERT(r != NULL); + getClientRect(r); +} + +// ----------------------------------------------------------------------- +void QuickPaintWnd::onSetVisible(int show) +{ + QUICKPAINTWND_PARENT::onSetVisible(show); + if (!show) + { + if (timerset) + { + KillThread(); + timerset = 0; + } + } + else + { + if (enabled && !timerset) + { + CreateRenderThread(); + + timerset = 1; + } + } +} + +// ----------------------------------------------------------------------- +int QuickPaintWnd::onPaint(Canvas *canvas) +{ + QUICKPAINTWND_PARENT::onPaint(canvas); + + if (!enabled) return 1; + + BltCanvas *render_canvas; + InterlockedExchangePointer((volatile PVOID*)&render_canvas, paint_canvas); + if (!render_canvas) return 1; + + RECT r; + getQuickPaintDest(&r); + RECT sr; + getQuickPaintSource(&sr); + + if (invalidated && wantFilters()) + { + foreach(filters) + filters.getfor()->filterBitmap((unsigned char *)render_canvas->getBits(), canvas_w, canvas_h, 32, NULL, getFiltersGroup()); + endfor; + invalidated = 0; + } + + render_canvas->/*getSkinBitmap()->*/stretchToRectAlpha(canvas, &sr, &r, getPaintingAlpha()); + InterlockedExchange(&invalidates_required, 0); + return 1; +} + +void QuickPaintWnd::KillThread() +{ + if (thread_context) + { + killTimer(TIMER_QUICKPAINT); + thread_context->Kill(); + delete thread_context; + thread_context = 0; + } +} + +void QuickPaintWnd::CreateRenderThread() +{ + int sp = getSpeed(); + if (!thread_context) + { + thread_context = new QuickPaintContext(this, sp); + } + else + thread_context->timer_ms = sp; + setTimer(TIMER_QUICKPAINT, sp); +}
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/qpaintwnd.h b/Src/Wasabi/api/wnd/wndclass/qpaintwnd.h new file mode 100644 index 00000000..b758d440 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/qpaintwnd.h @@ -0,0 +1,154 @@ +#ifndef __QPAINTWND_H +#define __QPAINTWND_H + +#include <api/wnd/wndclass/guiobjwnd.h> +#include <api/service/svcs/svc_skinfilter.h> + +#define QUICKPAINTWND_PARENT GuiObjectWnd + +/** + class QuickPaintWnd . + + @short + @author Nullsoft + @ver 1.0 + @see + @cat BFC +*/ +class QuickPaintContext; +class QuickPaintWnd : public QUICKPAINTWND_PARENT { + + public: + /** + QuickPaintWnd constructor . + + @see ~QuickPaintWnd() + */ + QuickPaintWnd(); + + /** + Destructor for QuickPaintWnd . + + @see QuickPaintWnd() + */ + virtual ~QuickPaintWnd(); + + /** + QuickPaintWnd method onInit . + + @ret 1 + */ + virtual int onInit(); + virtual int onPaint(Canvas *c); + + /** + QuickPaintWnd method timerCallback . + + @param id Identifies requested action + */ + virtual void timerCallback(int id); + virtual void onSetVisible(int show); + + /** + QuickPaintWnd method setRealtime . + + @see getRealtime() + @param rt + */ + virtual void setRealtime(int rt); + int getRealtime() const; + + /** + QuickPaintWnd method setSpeed sets the timer interval in milliseconds. + + @see getSpeed() + @param ms The timer interval in milliseconds. + */ + virtual void setSpeed(int ms); + + /** + QuickPaintWnd method getSpeed gets the timer interval in milliseconds. + + @see setSpeed() + @param ms The timer interval in milliseconds. + */ + virtual int getSpeed(); + + /** + QuickPaintWnd method startQuickPaint . + */ + virtual void startQuickPaint(); + + /** + QuickPaintWnd method stopQuickPaint . + */ + virtual void stopQuickPaint(); + + /** + QuickPaintWnd method isQuickPainting . + */ + virtual int isQuickPainting(); + + virtual int onQuickPaint(BltCanvas *c, int w, int h, int newone) { return 0; } // return 1 if your content has changed, or 0 to cancel update of your buffer to the window + virtual int wantEvenAlignment() { return 0; } // if you need even coordinates for your framebuffer, return 1 here + + /** + QuickPaintWnd method getQuickPaintSize gets the client area width and + height. + + @param w A pointer to the width to fill. + @param h A pointer to the height to fill. + */ + virtual void getQuickPaintSize(int *w, int *h); // by default returns client width/height + + /** + QuickPaintWnd method getQuickPaintSource . + + @see getQuickPaintSize() + @assert r exists. + @ret None + @except + @param r + */ + virtual void getQuickPaintSource(RECT *r); // by default returns the size of the quickpaint canvas + + /** + QuickPaintWnd method getQuickPaintDest . + + @see getQuickPaintSource() + @assert r exists. + @param r + */ + virtual void getQuickPaintDest(RECT *r); // by default returns the size of client area + virtual int wantNegativeHeight() { return 0; } + virtual int wantFilters() { return 0; } + virtual const wchar_t *getFiltersGroup() { return L"Vis/Eq"; } + + protected: +int invalidated; + private: + /** + QuickPaintWnd method quickPaint . + */ + friend class QuickPaintContext; + int quickPaint(); + void KillThread(); + void CreateRenderThread(); + int realtime; + volatile LONG invalidates_required; + BltCanvas *render_canvas1, *render_canvas2, *paint_canvas; + void SetPaintingCanvas(BltCanvas *c); + BltCanvas *&GetDrawingConvas(); + int canvas_w, canvas_h; + int speed; + int timerset; + int enabled; + + + PtrList<svc_skinFilter>filters; + SkinFilterEnum sfe; + QuickPaintContext *thread_context; +}; + + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/rootwndholder.cpp b/Src/Wasabi/api/wnd/wndclass/rootwndholder.cpp new file mode 100644 index 00000000..ad6000f3 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/rootwndholder.cpp @@ -0,0 +1,113 @@ +#include "precomp.h" +#include <api/wnd/api_wnd.h> + +#include "rootwndholder.h" +#include <api/wnd/notifmsg.h> +#include <tataki/canvas/canvas.h> + + +RootWndHolder::RootWndHolder() { + privptr = NULL; +} + +RootWndHolder::~RootWndHolder() { +} + +void RootWndHolder::rootwndholder_getRect(RECT *r) { + if (isInited()) + getClientRect(r); + else + MEMSET(r, 0, sizeof(RECT)); +} + +int RootWndHolder::onInit() { + ROOTWNDHOLDER_PARENT::onInit(); + ifc_window *w = rootwndholder_getRootWnd(); + if (w) { + checkInit(w); + setName(rootwndholder_getRootWnd()->getRootWndName()); + } + return 1; +} + +void RootWndHolder::checkInit(ifc_window *w) { + if (w && !w->isInited()) { + if (w->getParent() == NULL) + w->setParent(this); +// w->setStartHidden(getStartHidden()); + w->init(this); + } +} + +int RootWndHolder::onResize() { + int rv = ROOTWNDHOLDER_PARENT::onResize(); + if (!isInited()) return 1; + ifc_window *held = rootwndholder_getRootWnd(); + if (!held) return rv; + RECT r; + rootwndholder_getRect(&r); + if (renderRatioActive() && !held->handleRatio()) + multRatio(&r); + held->resize(r.left, r.top, r.right-r.left, r.bottom-r.top); + return rv; +} + +/*void RootWndHolder::onSetVisible(int v) { + ROOTWNDHOLDER_PARENT::onSetVisible(v); + if (!rootwndholder_getRootWnd()) return; + rootwndholder_getRootWnd()->setVisible(v); +}*/ + +int RootWndHolder::onActivate() { + int r = ROOTWNDHOLDER_PARENT::onActivate(); + if (rootwndholder_getRootWnd()) + rootwndholder_getRootWnd()->onActivate(); + return r; +} + +int RootWndHolder::onDeactivate() { + int r = ROOTWNDHOLDER_PARENT::onDeactivate(); + if (rootwndholder_getRootWnd()) + rootwndholder_getRootWnd()->onDeactivate(); + return r; +} + +int RootWndHolder::getPreferences(int what) { + if (rootwndholder_getRootWnd()) + return rootwndholder_getRootWnd()->getPreferences(what); + return ROOTWNDHOLDER_PARENT::getPreferences(what); +} + +ifc_window *RootWndHolder::rootwndholder_getRootWnd() { + return privptr; +} + +void RootWndHolder::rootwndholder_setRootWnd(ifc_window *w) { + if (privptr == w) return; + privptr = w; + checkInit(w); + if (isPostOnInit()) + onResize(); +} + +int RootWndHolder::onAction(const wchar_t *action, const wchar_t *param, int x, int y, intptr_t p1, intptr_t p2, void *data, size_t datalen, ifc_window *source) { + if (rootwndholder_getRootWnd()) + return rootwndholder_getRootWnd()->onAction(action, param, x, y, p1, p2, data, datalen, source); + return ROOTWNDHOLDER_PARENT::onAction(action, param, x, y, p1, p2, data, datalen, source); +} + +int RootWndHolder::childNotify(ifc_window *child, int msg, intptr_t param1, intptr_t param2) { + if (msg == ChildNotify::NAMECHANGED && child == rootwndholder_getRootWnd()) + setName(child->getRootWndName()); + return passNotifyUp(child, msg, (int)param1, (int)param2); +} + +int RootWndHolder::onPaint(Canvas *c) { + int rt = ROOTWNDHOLDER_PARENT::onPaint(c); + if (wantRenderBaseTexture()) { + RECT r; + rootwndholder_getRect(&r); + WASABI_API_WND->skin_renderBaseTexture(getBaseTextureWindow(), c, r, this); + } + return rt; +}
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/rootwndholder.h b/Src/Wasabi/api/wnd/wndclass/rootwndholder.h new file mode 100644 index 00000000..6c718ce5 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/rootwndholder.h @@ -0,0 +1,41 @@ +#ifndef __ROOTWNDHOLD_H +#define __ROOTWNDHOLD_H + +#include <api/wnd/virtualwnd.h> + +/** + A simple wnd that holds another window. Initializes it if needed, but DOES not delete it (and for a good reason, this is a ifc_window), + so your inheritor has to call whoever is needed to destroy the wnd +*/ + +#define ROOTWNDHOLDER_PARENT VirtualWnd + +class RootWndHolder : public ROOTWNDHOLDER_PARENT +{ + public: + RootWndHolder(); + virtual ~RootWndHolder(); + + // override this + virtual ifc_window *rootwndholder_getRootWnd(); + virtual void rootwndholder_getRect(RECT *r); + virtual void rootwndholder_setRootWnd(ifc_window *w); + + // BaseWnd + virtual int onInit(); + virtual int onResize(); +// virtual void onSetVisible(int v); + virtual int wantRenderBaseTexture() { return 0; } + virtual int onPaint(Canvas *c); + virtual int onActivate(); + virtual int onDeactivate(); + virtual int getPreferences(int what); + virtual int onAction(const wchar_t *action, const wchar_t *param=NULL, int x=-1, int y=-1, intptr_t p1=0, intptr_t p2=0, void *data=NULL, size_t datalen=0, ifc_window *source=NULL); + virtual int childNotify(ifc_window *child, int msg, intptr_t param1=0, intptr_t param2=0); + + private: + void checkInit(ifc_window *w); + ifc_window *privptr; +}; + +#endif
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/scbkgwnd.cpp b/Src/Wasabi/api/wnd/wndclass/scbkgwnd.cpp new file mode 100644 index 00000000..758c7660 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/scbkgwnd.cpp @@ -0,0 +1,869 @@ +#include <precomp.h> +#include "scbkgwnd.h" +#include <api/wnd/notifmsg.h> +#include <bfc/wasabi_std_wnd.h> +#include <api/wnd/PaintCanvas.h> + +#define SCROLLBAR_SEP 4 +#define TIMER_SMOOTHSCROLLY 8873 +#define TIMER_SMOOTHSCROLLX 8874 +#define SMOOTH_STEPS 5 +#define DEFAULT_BGCOLOR RGB(0,0,0) + +ScrlBkgWnd::ScrlBkgWnd() +{ + inDestroy = FALSE; + bmp = NULL; + bgColor = DEFAULT_BGCOLOR; + scrollX = 0; + scrollY = 0; + dbbuffer = 1; + needSetSliders = FALSE; + lineHeight = 16; + wantsep = 0; + wantTileBg = true; + lastratio = 1.0; + MEMSET(&smsqr, 0, sizeof(RECT)); + in_set_slider_position = 0; + + smoothScrollYInc = smoothScrollXInc = 0; + smoothScrollYCur = smoothScrollXCur = 0; + smoothScrollYTimerCount = smoothScrollXTimerCount = 0; + smoothYTimer = smoothXTimer = 0; +} + +ScrlBkgWnd::~ScrlBkgWnd() +{ + inDestroy = TRUE; +} + +int ScrlBkgWnd::onInit() +{ + + SCRLBKGWND_PARENT::onInit(); + + scrollY = 0; + scrollX = 0; + + hSep.setOrientation(SEP_HORIZONTAL); + + hScroll.setBitmaps(L"wasabi.scrollbar.horizontal.left", + L"wasabi.scrollbar.horizontal.left.pressed", + L"wasabi.scrollbar.horizontal.left.hover", + L"wasabi.scrollbar.horizontal.right", + L"wasabi.scrollbar.horizontal.right.pressed", + L"wasabi.scrollbar.horizontal.right.hover", + L"wasabi.scrollbar.horizontal.button", + L"wasabi.scrollbar.horizontal.button.pressed", + L"wasabi.scrollbar.horizontal.button.hover"); + + hScroll.setBackgroundBitmaps(L"wasabi.scrollbar.horizontal.background.left", + L"wasabi.scrollbar.horizontal.background.middle", + L"wasabi.scrollbar.horizontal.background.right"); + + vSep.setOrientation(SEP_VERTICAL); + + vScroll.setBitmaps(L"wasabi.scrollbar.vertical.left", + L"wasabi.scrollbar.vertical.left.pressed", + L"wasabi.scrollbar.vertical.left.hover", + L"wasabi.scrollbar.vertical.right", + L"wasabi.scrollbar.vertical.right.pressed", + L"wasabi.scrollbar.vertical.right.hover", + L"wasabi.scrollbar.vertical.button", + L"wasabi.scrollbar.vertical.button.pressed", + L"wasabi.scrollbar.vertical.button.hover"); + + vScroll.setBackgroundBitmaps(L"wasabi.scrollbar.vertical.background.top", + L"wasabi.scrollbar.vertical.background.middle", + L"wasabi.scrollbar.vertical.background.bottom"); + + // hScroll->setVertical(FALSE); + vScroll.setVertical(TRUE); + + hScroll.setStartHidden(TRUE); // prevent showing window at creation + vScroll.setStartHidden(TRUE); + hSep.setStartHidden(TRUE); + vSep.setStartHidden(TRUE); + + hScroll.setParent(this); + vScroll.setParent(this); + hSep.setParent(this); + vSep.setParent(this); + + hScroll.init(getOsModuleHandle(), getOsWindowHandle()); + vScroll.init(getOsModuleHandle(), getOsWindowHandle()); + hSep.init(getOsModuleHandle(), getOsWindowHandle()); + vSep.init(getOsModuleHandle(), getOsWindowHandle()); + + hScroll.setPosition(0); + vScroll.setPosition(0); + + setSlidersPosition(); // position sliders and show them if needed + + return 1; +} + +void ScrlBkgWnd::setBgBitmap(const wchar_t *b) +{ + bmp = b; + if (b) setBgColor(DEFAULT_BGCOLOR); +} + +void ScrlBkgWnd::setBgColor(ARGB32 rgb) +{ + bgColor = rgb; +} + +SkinBitmap *ScrlBkgWnd::getBgBitmap(void) +{ + return bmp; +} + +ARGB32 ScrlBkgWnd::getBgColor(void) +{ + return bgColor; +} + +// Scroll to a specified Y-pixels +void ScrlBkgWnd::scrollToY(int y, int signal) +{ + + WndCanvas *canvas = NULL; + RECT r; + int offset; + int dor2 = 0; + RECT r2 = {0, 0, 0, 0}; + int focused = gotFocus(); + + if (isVirtual() || renderRatioActive()) + { + scrollY = y; + invalidateRect(&clientRect()); + onScrollY(y); + return ; + } + + if (!Wasabi::Std::Wnd::isValidWnd(getOsWindowHandle())) return ; // no need to paint + + if (y > scrollY) + { // tree scrolling up, scroller going down. invalidating from the bottom. bitblting from bottom to top + int lines = y - scrollY; + offset = -lines; + getClientRect(&r); + canvas = new WndCanvas(); + canvas->attachToClient(this); + + RegionI reg; + makeWindowOverlayMask(®); + RegionI clip(&r); + reg.offset(0, offset); + clip.subtractRegion(®); + canvas->selectClipRgn(&clip); + + int b = hScroll.isVisible() ? hScroll.getHeight() : 0; + int c = vScroll.isVisible() ? vScroll.getWidth() : 0; + int a = focused && (!b); + if (r.bottom-r.top-lines > 0) + canvas->blit(r.left, r.top+lines, canvas, r.left, r.top, r.right-r.left- c, r.bottom-r.top-lines-a - b); + +// int a = focused && (!hScroll->isVisible()); + //if (r.bottom - r.top - lines > 0) +// canvas->blit(r.left, r.top + lines, canvas, r.left, r.top, r.right - r.left, r.bottom - r.top - lines - a); + + canvas->selectClipRgn(NULL); + if (!clip.isEmpty()) + invalidateRgn(&clip); + + + getClientRect(&r2); + r2.bottom = r2.top + 1; + dor2 = 1; + r.top = r.bottom - lines - 1; + } + if (y < scrollY) + { // tree scrolling down, scroller going up. invalidating from the top. bitblting from top to bottom + int lines = scrollY - y; + offset = lines; + getClientRect(&r); + canvas = new WndCanvas(); + canvas->attachToClient(this); + + RegionI reg; + makeWindowOverlayMask(®); + RegionI clip(&r); + reg.offset(0, offset); + clip.subtractRegion(®); + canvas->selectClipRgn(&clip); + + int c = vScroll.isVisible() ? vScroll.getWidth() : 0; + canvas->blit(r.left, r.top+focused, canvas, r.left, r.top+lines+focused, r.right-r.left-c, r.bottom-r.top-lines-focused); + //canvas->blit(r.left, r.top + focused, canvas, r.left, r.top + lines + focused, r.right - r.left, r.bottom - r.top - lines - focused); + + canvas->selectClipRgn(NULL); + if (!clip.isEmpty()) + invalidateRgn(&clip); + + getClientRect(&r2); + r2.top = r2.bottom - 1; + dor2 = 1; + r.bottom = r.top + lines + 1; + } + if (canvas) + { + delete canvas; + scrollY = y; + + // in case we have a virtualCanvas, we need to tell BaseWnd to call us to paint on it next time it's needed coz we blited directly to the screen + RECT cr; + getClientRect(&cr); + cr.top -= getHeaderHeight(); + RECT screenblit; + SubtractRect(&screenblit, &cr, &r); + + // invalidate what's needed + if (dor2 && focused) + cascadeRepaintRect(&r2, 0); + cascadeRepaintRect(&r); + + deferedInvalidateRect(&screenblit); + + //dbbuffer = 1; + //repaint(); + } + + if (signal) + updateVScroll(y); + + onScrollY(y); +} + +// Scroll to a specified X-pixel +void ScrlBkgWnd::scrollToX(int x, int signal) +{ + WndCanvas *canvas = NULL; + RECT r; + int offset; + int dor2 = 0; + RECT r2 = {0, 0, 0, 0}; + int focused = gotFocus(); + + if (isVirtual() || ABS(getRenderRatio() - 1.0) > 0.01) + { + scrollX = x; + getClientRect(&r); + invalidateRect(&r); + return ; + } + + if (x > scrollX) + { // tree scrolling left, scroller going right. invalidating from the right. bitblting from right to left + int lines = x - scrollX; + offset = -lines; + getClientRect(&r); + r.top -= getHeaderHeight(); + canvas = new WndCanvas(); + canvas->attachToClient(this); + + RegionI reg; + makeWindowOverlayMask(®); + RegionI clip(&r); + reg.offset(offset, 0); + clip.subtractRegion(®); + canvas->selectClipRgn(&clip); + + int c = vScroll.isVisible() ? vScroll.getWidth() : 0; + canvas->blit(r.left+lines, r.top, canvas, r.left, r.top, r.right-r.left-lines-focused-c, r.bottom-r.top); + //canvas->blit(r.left + lines, r.top, canvas, r.left, r.top, r.right - r.left - lines - focused, r.bottom - r.top); + + canvas->selectClipRgn(NULL); + if (!reg.isEmpty()) + invalidateRgn(®); + + getClientRect(&r2); + r2.right = r2.left + 1; + dor2 = 1; + r.left = r.right - lines - 1; + } + if (x < scrollX) + { // tree scrolling right, scroller going left. invalidating from the left. bitblting from left to right + int lines = scrollX - x; + offset = lines; + getClientRect(&r); + r.top -= getHeaderHeight(); + canvas = new WndCanvas(); + canvas->attachToClient(this); + + RegionI reg; + makeWindowOverlayMask(®); + RegionI clip(&r); + reg.offset(offset, 0); + clip.subtractRegion(®); + canvas->selectClipRgn(&clip); + + int a = focused && (!vScroll.isVisible()); + int c = hScroll.isVisible() ? hScroll.getHeight()-focused : 0; + canvas->blit(r.left+a, r.top, canvas, r.left+lines, r.top, r.right-r.left-lines-a, r.bottom-r.top-c); + + //int a = focused && (!vScroll->isVisible()); +// canvas->blit(r.left + a, r.top, canvas, r.left + lines, r.top, r.right - r.left - lines - a, r.bottom - r.top); + + canvas->selectClipRgn(NULL); + if (!reg.isEmpty()) + invalidateRgn(®); + + getClientRect(&r2); + r2.left = r2.right - 1; + dor2 = 1; + r.right = r.left + lines + 1; + } + if (canvas) + { + delete canvas; + scrollX = x; + + // in case we have a virtualCanvas, we need to tell BaseWnd to call us to paint on it next time it's needed coz we blited directly to the screen + RECT cr; + getClientRect(&cr); + cr.top -= getHeaderHeight(); + RECT screenblit; + SubtractRect(&screenblit, &cr, &r); + deferedInvalidateRect(&screenblit); + + if (dor2 && focused) + cascadeRepaintRect(&r2, 0); + // invalidate what's needed + cascadeRepaintRect(&r); + + //dbbuffer = 1; + //repaint(); + } + + if (signal) + updateHScroll(x); +} + +void ScrlBkgWnd::setSlidersPosition() +{ + if (in_set_slider_position) return ; + in_set_slider_position = 1; + _setSlidersPosition(); + in_set_slider_position = 0; +} + +void ScrlBkgWnd::_setSlidersPosition() +{ + + if (!isInited()) return ; + + RECT d; + getClientRect(&d); + if ((d.left >= d.right) || (d.top >= d.bottom)) + return ; + + RECT r; + if (inDestroy) return ; + if (!isVisible()) + { + needSetSliders = TRUE; + return ; + } + + needSetSliders = FALSE; + + if (needHScroll()) + { + SCRLBKGWND_PARENT::getClientRect(&r); + r.top = r.bottom - getScrollbarWidth(); + if (needVScroll()) + r.right -= getScrollbarWidth() + (wantsep ? SCROLLBAR_SEP : 0); + RECT z; hScroll.getClientRect(&z); + if (!Wasabi::Std::rectEqual(r, z)) + { // assumes ScrollBars are virtual + hScroll.resizeToRect(&r); + RECT s = r; + s.bottom = s.top; + s.top -= (wantsep ? SCROLLBAR_SEP : 0); + hSep.resizeToRect(&s); + } + if (!hScroll.isVisible()) + { + hScroll.setVisible(TRUE); + if (wantsep) hSep.setVisible(TRUE); + onHScrollToggle(1); + } + hScroll.setNPages(((int)(getContentsWidth() / (r.right - r.left))) + 1); + hScroll.setUpDownValue((int)(((float)lineHeight / (getContentsWidth() - (r.right - r.left)))*SCROLLBAR_FULL)); + hScroll.setPosition((int)((float)scrollX / getMaxScrollX() * SCROLLBAR_FULL)); + } + else + { + if (hScroll.isVisible()) + { + hScroll.setVisible(FALSE); + if (wantsep) hSep.setVisible(FALSE); + onHScrollToggle(0); + } + hScroll.setPosition(0); + scrollToX(0); + } + + if (needVScroll()) + { + SCRLBKGWND_PARENT::getClientRect(&r); + r.left = r.right - getScrollbarWidth(); + if (needHScroll()) + r.bottom -= getScrollbarWidth(); + RECT z; vScroll.getNonClientRect(&z); + if (!Wasabi::Std::rectEqual(r, z)) + { + vScroll.resizeToRect(&r); + RECT s = r; + s.right = s.left; + s.left -= (wantsep ? SCROLLBAR_SEP : 0); + vSep.resizeToRect(&s); + } + if (!vScroll.isVisible()) + { + vScroll.setVisible(TRUE); + if (wantsep) vSep.setVisible(TRUE); + onVScrollToggle(1); + } + vScroll.setNPages(((int)(getContentsHeight() / (r.bottom - r.top))) + 1); + vScroll.setUpDownValue((int)(((float)lineHeight / (getContentsHeight() - (r.bottom - r.top)))*SCROLLBAR_FULL)); + vScroll.setPosition((int)((float)scrollY / getMaxScrollY() * SCROLLBAR_FULL)); + } + else + { + if (vScroll.isVisible()) + { + vScroll.setVisible(FALSE); + if (wantsep) vSep.setVisible(FALSE); + onVScrollToggle(0); + } + vScroll.setPosition(0); + scrollToY(0); + } + + hSep.invalidate(); + vSep.invalidate(); + + if (needHScroll() && needVScroll()) + { + getNonClientRect(&smsqr); + smsqr.left = smsqr.right - getScrollbarWidth(); + smsqr.top = smsqr.bottom - getScrollbarWidth(); + invalidateRect(&smsqr); + } + else + ZERO(smsqr); +} + +void ScrlBkgWnd::onHScrollToggle(int set) +{} + +void ScrlBkgWnd::onVScrollToggle(int set) +{} + +int ScrlBkgWnd::onPaint(Canvas *canvas) +{ + RECT d; + getClientRect(&d); + if (d.right > d.left + 0xFFFF || d.bottom > d.top + 0xFFFF) return 1; + if ((d.left >= d.right) || (d.top >= d.bottom)) + { + return SCRLBKGWND_PARENT::onPaint(canvas); + } + + if (needSetSliders) setSlidersPosition(); + + // RECT z; + // GetUpdateRect(gethWnd(), &z, FALSE); + + PaintCanvas paintcanvas; + PaintBltCanvas paintbcanvas; + + if (canvas == NULL) + { + if (dbbuffer) + { + if (!paintbcanvas.beginPaintNC(this)) return 0; + canvas = &paintbcanvas; + } + else + { + if (!paintcanvas.beginPaint(this)) return 0; + canvas = &paintcanvas; + } + } + //dbbuffer=1; + SCRLBKGWND_PARENT::onPaint(canvas); + + RegionI *smsq = NULL; + + if (needHScroll() && needVScroll()) + { + renderBaseTexture(canvas, smsqr); + smsq = new RegionI(&smsqr); + } + + RECT r; + LabelWnd::getNonClientRect(&r); + RECT c = {r.left, r.top, r.right, r.top + getLabelHeight()}; // create label rect + + + RegionI *clip = new RegionI(); + if (canvas->getClipRgn(clip) == 0) + { + delete clip; + clip = new RegionI(&r); + if (smsq) clip->subtractRegion(smsq); + canvas->selectClipRgn(clip); + } + else + { + RegionI reg(&c); + clip->subtractRegion(®); + if (smsq) clip->subtractRegion(smsq); + canvas->selectClipRgn(clip); + } + delete smsq; + + drawBackground(canvas); + delete clip; + +if (getRenderRatio() != lastratio) { invalidate(); lastratio = getRenderRatio(); } // todo: make that an event + return 1; +} + +int ScrlBkgWnd::needDoubleBuffer() +{ + return dbbuffer; +} + +int ScrlBkgWnd::onEraseBkgnd(HDC dc) +{ + + /* DCCanvas canvas; + canvas.cloneDC(dc); + + drawBackground(&canvas);*/ + + return 1; +} + +// Draws tiled background +void ScrlBkgWnd::drawBackground(Canvas *canvas) +{ + RECT r(clientRect()); + RegionI reg(&r); + RegionI old; + canvas->getClipRgn(&old); + reg.andRegion(&old); + canvas->selectClipRgn(®); + if (bmp.getBitmap() && bgColor == DEFAULT_BGCOLOR) + { + r.top -= scrollY % bmp.getBitmap()->getHeight(); + r.left -= scrollX % bmp.getBitmap()->getWidth(); + if (wantTileBg) + bmp.getBitmap()->blitTile(canvas, &r); + else + bmp.getBitmap()->stretchToRect(canvas, &r); + } + else if (bgColor != DEFAULT_BGCOLOR) + { + canvas->fillRect(&r, bgColor); + } + canvas->selectClipRgn(&old); + +} + +bool ScrlBkgWnd::needHScroll() +{ + if (!wantHScroll()) return FALSE; + RECT r; + getNonClientRect(&r); + if (vScroll.isVisible()) + r.right -= getScrollbarWidth(); + return (getContentsWidth() > r.right - r.left); +} + +bool ScrlBkgWnd::needVScroll() +{ + if (!wantVScroll()) return FALSE; + RECT r; + getNonClientRect(&r); + r.top += getHeaderHeight(); + if (hScroll.isVisible()) + r.bottom -= getScrollbarWidth(); + return (getContentsHeight() > r.bottom - r.top); +} + +// Returns the current tree width in pixels +int ScrlBkgWnd::getContentsWidth() +{ + /*RECT r; + ScrlBkgWnd::getClientRect(&r); + return r.right-r.left;*/ + return 10000; +} + +// Returns the current tree height in pixels +int ScrlBkgWnd::getContentsHeight() +{ + /*RECT r; + ScrlBkgWnd::getClientRect(&r); + return r.bottom-r.top;*/ + return 10000; +} + +int ScrlBkgWnd::getMaxScrollY() +{ + RECT r; + getClientRect(&r); + return MAX<int>(0, getContentsHeight() - (r.bottom - r.top)); +} + +int ScrlBkgWnd::getMaxScrollX() +{ + RECT r; + getClientRect(&r); + return MAX<int>(0, getContentsWidth() - (r.right - r.left)); +} + +void ScrlBkgWnd::updateVScroll(int y) +{ + if (getMaxScrollY() == 0) { vScroll.setPosition(0); return ; } + int z = (int)((float)y / getMaxScrollY() * SCROLLBAR_FULL); + vScroll.setPosition(z); +} + +void ScrlBkgWnd::updateHScroll(int x) +{ + if (getMaxScrollX() == 0) { hScroll.setPosition(0); return ; } + int z = (int)((float)x / getMaxScrollX() * SCROLLBAR_FULL); + hScroll.setPosition(z); +} + +void ScrlBkgWnd::updateScrollY(bool smooth) +{ + if (getMaxScrollY() == 0) { scrollToY(0); return ; } + int y = (int)((float)(vScroll.getPosition()) / SCROLLBAR_FULL * getMaxScrollY()); + if (!smooth) + scrollToY(y /*& ~3*/); + else + smoothScrollToY(y); +} + +void ScrlBkgWnd::updateScrollX(bool smooth) +{ + if (getMaxScrollX() == 0) { scrollToX(0); return ; } + int x = (int)((float)(hScroll.getPosition()) / SCROLLBAR_FULL * getMaxScrollX()); + if (!smooth) + scrollToX(x /*& ~3*/); + else + smoothScrollToX(x); +} + +void ScrlBkgWnd::smoothScrollToX(int x) +{ + killSmoothXTimer(); + smoothScrollXInc = -(float)(scrollX - x) / SMOOTH_STEPS; + smoothScrollXCur = (float)scrollX; + smoothScrollXTimerCount = 0; + smoothXTimer = 1; + setTimer(TIMER_SMOOTHSCROLLX, 25); +} + +void ScrlBkgWnd::killSmoothYTimer() +{ + if (smoothYTimer) + { + killTimer(TIMER_SMOOTHSCROLLY); + smoothScrollYCur += smoothScrollYInc * (SMOOTH_STEPS - smoothScrollYTimerCount); + scrollToY((int)smoothScrollYCur); + smoothYTimer = 0; + updateVScroll(scrollY); + } +} + +void ScrlBkgWnd::killSmoothXTimer() +{ + if (smoothXTimer) + { + killTimer(TIMER_SMOOTHSCROLLX); + smoothScrollXCur += smoothScrollXInc * (SMOOTH_STEPS - smoothScrollXTimerCount); + scrollToX((int)smoothScrollXCur); + smoothXTimer = 0; + updateHScroll(scrollX); + } +} + +void ScrlBkgWnd::smoothScrollToY(int y) +{ + killSmoothYTimer(); + smoothScrollYInc = -(float)(scrollY - y) / SMOOTH_STEPS; + smoothScrollYCur = (float)scrollY; + smoothScrollYTimerCount = 0; + smoothYTimer = 1; + setTimer(TIMER_SMOOTHSCROLLY, 25); +} + +void ScrlBkgWnd::timerCallback(int id) +{ + switch (id) + { + case TIMER_SMOOTHSCROLLY: + smoothScrollYCur += smoothScrollYInc; + scrollToY((int)smoothScrollYCur, FALSE); + if (++smoothScrollYTimerCount == SMOOTH_STEPS) + killSmoothYTimer(); + return ; + case TIMER_SMOOTHSCROLLX: + smoothScrollXCur += smoothScrollXInc; + scrollToX((int)smoothScrollXCur, FALSE); + if (++smoothScrollXTimerCount == SMOOTH_STEPS) + killSmoothXTimer(); + return ; + } + SCRLBKGWND_PARENT::timerCallback(id); +} + +// Gets notification from sliders +int ScrlBkgWnd::childNotify(ifc_window *child, int msg, intptr_t param1, intptr_t param2) +{ + switch (msg) + { + case ChildNotify::SCROLLBAR_SETPOSITION: + if (child == &vScroll) + { + updateScrollY(!!param1); + return 1; + } + if (child == &hScroll) + { + updateScrollX(!!param1); + return 1; + } + break; + } + + return SCRLBKGWND_PARENT::childNotify(child, msg, param1, param2); +} + +int ScrlBkgWnd::onResize() +{ + int rt = SCRLBKGWND_PARENT::onResize(); + if (!isInited()) return rt; + invalidateRect(&smsqr); + setSlidersPosition(); + return 1; +} + +void ScrlBkgWnd::onSetVisible(int show) +{ + SCRLBKGWND_PARENT::onSetVisible(show); + if (show) + setSlidersPosition(); +} + +void ScrlBkgWnd::getClientRect(RECT *r) +{ + SCRLBKGWND_PARENT::getClientRect(r); + if (vScroll.isVisible(1)) + r->right -= getScrollbarWidth() + (wantsep ? SCROLLBAR_SEP : 0); + if (hScroll.isVisible(1)) + r->bottom -= getScrollbarWidth() + (wantsep ? SCROLLBAR_SEP : 0); + r->top += getHeaderHeight(); +} + +/*void ScrlBkgWnd::getNonClientRect(RECT *r) { + SCRLBKGWND_PARENT::getClientRect(r); // my non client rect is my parent's client rect + return; +}*/ + +int ScrlBkgWnd::getHeaderHeight() +{ + return 0; +} + +void ScrlBkgWnd::setLineHeight(int h) +{ + lineHeight = h; +} + +int ScrlBkgWnd::getLinesPerPage() +{ + RECT r; + getClientRect(&r); + int h = r.bottom - r.top; + return h / lineHeight; +} + +int ScrlBkgWnd::getScrollX() +{ + return scrollX; +} + +int ScrlBkgWnd::getScrollY() +{ + return scrollY; +} + +int ScrlBkgWnd::getScrollbarWidth() +{ + // TODO: maybe do if (hScroll.isVisible()) + return hScroll.getWidth(); + return vScroll.getWidth(); + return 0; +} + +/*void ScrlBkgWnd::clientToScreen(RECT *r) { + POINT p; + p.x = r->left; + p.y = r->top; + SCRLBKGWND_PARENT::clientToScreen((int *)&p.x, (int*)&p.y); + r->left = p.x; + r->top = p.y; + + p.x = r->right; + p.y = r->bottom; + SCRLBKGWND_PARENT::clientToScreen((int *)&p.x, (int*)&p.y); + r->right = p.x; + r->bottom = p.y; +} + +void ScrlBkgWnd::clientToScreen(int *x, int *y) { + SCRLBKGWND_PARENT::clientToScreen(x, y); +} + +void ScrlBkgWnd::clientToScreen(POINT *p) { + TREEWND_PARENT::clientToScreen((int *)&p->x, (int *)&p->y); +}*/ + +void ScrlBkgWnd::makeWindowOverlayMask(api_region *r) +{ + + return ; +#ifdef WIN32 + // With this routine empty, I'm just nuking the code from x-plat builds < KP + HDC dc = GetDC(getOsWindowHandle()); + + //if (getRandomRgn) + { + RECT cr; + getClientRect(&cr); + RECT wr; + getWindowRect(&wr); + + RegionI sr; + Wasabi::Std::Wnd::getRandomRegion(dc, sr.getOSHandle()); + sr.offset( -wr.left, -wr.top); + + r->setRect(&cr); + r->subtractRegion(&sr); + + } + + ReleaseDC(getOsWindowHandle(), dc); +#endif +} diff --git a/Src/Wasabi/api/wnd/wndclass/scbkgwnd.h b/Src/Wasabi/api/wnd/wndclass/scbkgwnd.h new file mode 100644 index 00000000..549e6f90 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/scbkgwnd.h @@ -0,0 +1,463 @@ +#ifndef __SCRLBKGWND_H +#define __SCRLBKGWND_H + +#include <tataki/canvas/canvas.h> +#include <tataki/bitmap/autobitmap.h> +#include <api/wnd/wndclass/labelwnd.h> +#include <api/wnd/wndclass/scrollbar.h> +#include <api/wnd/wndclass/sepwnd.h> + +#define SCRLBKGWND_PARENT LabelWnd + + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class ScrlBkgWnd : public SCRLBKGWND_PARENT { +protected: + + /** + Method + + @see + @ret + @param + */ + ScrlBkgWnd(); +public: + + /** + Method + + @see + @ret + @param + */ + virtual ~ScrlBkgWnd(); + + + /** + Method + + @see + @ret + @param + */ + virtual int onInit(); + + /** + Method + + @see + @ret + @param + */ + virtual int onPaint(Canvas *c); + + /** + Method + + @see + @ret + @param + */ + virtual void drawBackground(Canvas *canvas); + + /** + Method + + @see + @ret + @param + */ + virtual int onEraseBkgnd(HDC dc); + + /** + Method + + @see + @ret + @param + */ + virtual int childNotify(ifc_window *child, int msg, intptr_t param1, intptr_t param2); + + /** + Method + + @see + @ret + @param + */ + virtual int onResize(); + + /** + Method + + @see + @ret + @param + */ + virtual void getClientRect(RECT *r); +// virtual void getNonClientRect(RECT *r); + + /** + Method + + @see + @ret + @param + */ + virtual int getHeaderHeight(); + virtual void timerCallback (int id); + + + /** + Method + + @see + @ret + @param + */ + virtual void onHScrollToggle(int set); + + /** + Method + + @see + @ret + @param + */ + virtual void onVScrollToggle(int set); + + /** + Method + + @see + @ret + @param + */ + virtual void onSetVisible(int show); + + /** + Method + + @see + @ret + @param + */ + virtual int wantHScroll() { return 1; } + + /** + Method + + @see + @ret + @param + */ + virtual int wantVScroll() { return 1; } + + /** + Method + + @see + @ret + @param + */ + void makeWindowOverlayMask(api_region *r); + + + /** + Method + + @see + @ret + @param + */ + SkinBitmap *getBgBitmap(void); + + /** + Method + + @see + @ret + @param + */ + void setBgBitmap(const wchar_t *b); + + /** + Method + + @see + @ret + @param + */ + void setBgColor(ARGB32 rgb); + + /** + Method + + @see + @ret + @param + */ + virtual ARGB32 getBgColor(void); + + + /** + Method + + @see + @ret + @param + */ + virtual int getContentsWidth(); // not safe to call getclientrect! + + /** + Method + + @see + @ret + @param + */ + virtual int getContentsHeight(); // not safe to call getclientrect! + + + /** + Method + + @see + @ret + @param + */ + void setLineHeight(int h); + + /** + Method + + @see + @ret + @param + */ + int getLinesPerPage(); + + + /** + Method + + @see + @ret + @param + */ + int getScrollX(); + + /** + Method + + @see + @ret + @param + */ + int getScrollY(); + + /** + Method + + @see + @ret + @param + */ + int getScrollbarWidth(); + + /** + Method + + @see + @ret + @param + */ + virtual void scrollToY(int y, int signal=TRUE); + + /** + Method + + @see + @ret + @param + */ + virtual void scrollToX(int x, int signal=TRUE); + +protected: + + virtual void onScrollY(int y) { } + + /** + Method + + @see + @ret + @param + */ + void setSlidersPosition(); + + /** + Method + + @see + @ret + @param + */ + int needDoubleBuffer(); + + /** + Method + + @see + @ret + @param + */ + bool needHScroll(); + + /** + Method + + @see + @ret + @param + */ + bool needVScroll(); + + /** + Method + + @see + @ret + @param + */ + int getMaxScrollY(); + + /** + Method + + @see + @ret + @param + */ + int getMaxScrollX(); + + /** + Method + + @see + @ret + @param + */ + void updateScrollY(bool smooth=false); + + /** + Method + + @see + @ret + @param + */ + void updateScrollX(bool smooth=false); + + /** + Method + + @see + @ret + @param + */ + void smoothScrollToY(int y); + + /** + Method + + @see + @ret + @param + */ + void smoothScrollToX(int x); + + /** + Method + + @see + @ret + @param + */ + void updateVScroll(int y); + + /** + Method + + @see + @ret + @param + */ + void updateHScroll(int x); + + AutoSkinBitmap bmp; + + int dbbuffer; + bool inDestroy; + + ScrollBar hScroll; + ScrollBar vScroll; + SepWnd hSep; + SepWnd vSep; + + ARGB32 bgColor; + + int scrollX; + int scrollY; + + bool needSetSliders; + bool wantsep; + bool wantTileBg; + + int lineHeight; + + float smoothScrollYInc, smoothScrollXInc; + float smoothScrollYCur, smoothScrollXCur; + int smoothScrollYTimerCount, smoothScrollXTimerCount; + int smoothYTimer, smoothXTimer; + + /** + Method + + @see + @ret + @param + */ + void killSmoothYTimer(); + + /** + Method + + @see + @ret + @param + */ + void killSmoothXTimer(); + double lastratio; + RECT smsqr; + + /** + Method + + @see + @ret + @param + */ + void _setSlidersPosition(); + int in_set_slider_position; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/scrollbar.cpp b/Src/Wasabi/api/wnd/wndclass/scrollbar.cpp new file mode 100644 index 00000000..ce3e5b03 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/scrollbar.cpp @@ -0,0 +1,679 @@ +#include <precomp.h> + +#include <bfc/wasabi_std.h> + +#include "scrollbar.h" +#include <tataki/canvas/canvas.h> +#include <api/wnd/notifmsg.h> +#include <api/wnd/PaintCanvas.h> + +#define TIMER_ID 9871 +#define TIMER_ID2 9872 + +#define FIRST_DELAY 350 +#define NEXT_DELAY 75 + +ScrollBar::ScrollBar() { + leftrgn = NULL; + rightrgn = NULL; + buttonrgn = NULL; + + position = 0; + moving = 0; + lefting = 0; + righting = 0; + clicked = 0; + height = DEFAULT_HEIGHT; + buttonx = 0; + + shiftleft = 0; + shiftright = 0; + + curmouseposition = POS_NONE; + clickmouseposition = POS_NONE; + pageing = 0; + timer = timer2 = 0; + npages = 100; + pageway = PAGE_NONE; + updown = 256; + insetpos = 0; + clickbuttonx = 0; + vertical = 0; + firstdelay = 0; + lastx = lasty = 0; +} + +ScrollBar::~ScrollBar() { + deleteResources(); +} + +void ScrollBar::deleteResources() { + delete leftrgn; leftrgn = NULL; + delete buttonrgn; buttonrgn = NULL; + delete rightrgn; rightrgn = NULL; +} + +// this one is inherited +void ScrollBar::freeResources() { + SCROLLBAR_PARENT::freeResources(); + deleteResources(); +} + +void ScrollBar::reloadResources() { + SCROLLBAR_PARENT::reloadResources(); + loadBmps(); +} + + +int ScrollBar::onMouseMove (int x, int y) { + + SCROLLBAR_PARENT::onMouseMove(x, y); + lastx = x; + lasty = y; + + if (clicked && clickmouseposition == POS_BUTTON) { + + POINT pt={x,y}; + int x; + if (!vertical) + x = pt.x - clickpos.x; + else + x = pt.y - clickpos.y; + + RECT r; + getClientRect(&r); + int maxwidth; + if (!vertical) + maxwidth = (r.right-r.left)-(shiftright+shiftleft+bmpbutton.getWidth())+1; + else + maxwidth = (r.bottom-r.top)-(shiftright+shiftleft+bmpbutton.getHeight())+1; + buttonx = MIN(MAX(clickbuttonx + x, 0), maxwidth); + calcPosition(); + invalidate(); + + } else { + + int oldposition = curmouseposition; + curmouseposition = getMousePosition(); + if (oldposition != curmouseposition) invalidate(); + + if (curmouseposition != POS_NONE && !getCapture()) + beginCapture(); + + if (curmouseposition == POS_NONE && getCapture() && !clicked && !pageing) + endCapture(); + } + + + return 1; +} + +int ScrollBar::getWidth() { + if (!bmpbutton) return 0; + if (!vertical) + return bmpbutton.getHeight(); + else + return bmpbutton.getWidth(); + return 0; +} + +int ScrollBar::getMousePosition() { + int v = POS_NONE; + + POINT pt={lastx, lasty}; + + RECT c; + getClientRect(&c); + pt.x -= c.left; + pt.y -= c.top; + + api_region *l, *b, *r; + l = leftrgn->clone(); + b = buttonrgn->clone(); + if (!vertical) + b->offset(buttonx+shiftleft, 0); + else + b->offset(0, buttonx+shiftleft); + r = rightrgn->clone(); + if (!vertical) + r->offset(c.right-c.left-bmpleft.getWidth(), 0); + else + r->offset(0, c.bottom-c.top-bmpleft.getHeight()); + + if (b->ptInRegion(&pt)) + v = POS_BUTTON; + if (l->ptInRegion(&pt)) + v = POS_LEFT; + if (r->ptInRegion(&pt)) + v = POS_RIGHT; + + leftrgn->disposeClone(l); + buttonrgn->disposeClone(b); + rightrgn->disposeClone(r); + + return v; +} + +int ScrollBar::onLeftButtonDown(int x, int y) { + clickmouseposition = getMousePosition(); + if (!pageing && clickmouseposition != POS_NONE) { + clicked = 1; + if (clickmouseposition == POS_LEFT || clickmouseposition == POS_RIGHT) + handleUpDown(); + if (clickmouseposition) { + clickpos.x = lastx; + clickpos.y = lasty; + clickbuttonx = buttonx; + } + } else { + clicked = 0; + pageing = 1; + handlePageUpDown(); + } + invalidate(); + return 1; +} + +void ScrollBar::handleUpDown() { + setTimer(TIMER_ID2, FIRST_DELAY); + timer2 = 1; + firstdelay = 1; + + checkUpDown(); +} + +int ScrollBar::checkUpDown() { + if (!clicked) { + if (timer2) { + killTimer(TIMER_ID2); + timer2 = 0; + return 1; + } + } + + if (getMousePosition() == clickmouseposition) + upDown(clickmouseposition); + + return 1; + +} + +void ScrollBar::handlePageUpDown() { + + setTimer(TIMER_ID, FIRST_DELAY); + timer = 1; + firstdelay = 1; + + checkPageUpDown(); +} + +int ScrollBar::checkPageUpDown() { + + if (!pageing) { + if (timer) { + killTimer(TIMER_ID); + timer = 0; + pageway = PAGE_NONE; + return 1; + } + } + + POINT pt={lastx,lasty}; + RECT c; + getClientRect(&c); + pt.x -= c.left; + pt.y -= c.top; + + if (!vertical) { + int middlebutton = shiftleft + buttonx + bmpbutton.getWidth()/2; + api_region *r = buttonrgn->clone(); + r->offset(buttonx+shiftleft, 0); + if (pt.x > middlebutton && !r->ptInRegion(&pt) && pageway != PAGE_DOWN) + pageUp(); + if (pt.x < middlebutton && !r->ptInRegion(&pt) && pageway != PAGE_UP) + pageDown(); + buttonrgn->disposeClone(r); + } else { + int middlebutton = shiftleft + buttonx + bmpbutton.getHeight()/2; + api_region *r = buttonrgn->clone(); + r->offset(0, buttonx+shiftleft); + if (pt.y > middlebutton && !r->ptInRegion(&pt) && pageway != PAGE_DOWN) + pageUp(); + if (pt.y < middlebutton && !r->ptInRegion(&pt) && pageway != PAGE_UP) + pageDown(); + buttonrgn->disposeClone(r); + } + return 1; + +} + +int ScrollBar::onLeftButtonUp(int x, int y) { + clicked = 0; + clickmouseposition = POS_NONE; + curmouseposition = POS_NONE; + onMouseMove(x,y); + if (pageing) { + pageing = 0; + checkPageUpDown(); + } + onSetFinalPosition(); + invalidate(); + return 1; +} + +int ScrollBar::onRightButtonDown(int x, int y) { + return 1; +} + +int ScrollBar::onRightButtonUp(int x, int y) { + return 1; +} + +int ScrollBar::onMouseWheelUp(int clicked, int lines) { + return 1; +} + +int ScrollBar::onMouseWheelDown(int clicked, int lines) { + return 1; +} + +int ScrollBar::onPaint(Canvas *canvas) { + AutoSkinBitmap &thisleft = curmouseposition == POS_LEFT ? (clicked ? bmplpressed : bmplhilite) : bmpleft; + AutoSkinBitmap &thisbutton = curmouseposition == POS_BUTTON ? (clicked ? bmpbpressed : bmpbhilite) : bmpbutton; + AutoSkinBitmap &thisright = curmouseposition == POS_RIGHT ? (clicked ? bmprpressed : bmprhilite) : bmpright; + + if (curmouseposition != clickmouseposition && clicked) { + thisleft = bmpleft; + thisbutton = bmpbutton; + thisright = bmpright; + } + + RECT r; + PaintBltCanvas paintcanvas; + + if (canvas == NULL) { + if (!paintcanvas.beginPaint(this)) return 0; + canvas = &paintcanvas; + } + SCROLLBAR_PARENT::onPaint(canvas); + + getClientRect(&r); + + renderBaseTexture(canvas, r); + + if (!vertical) { + RECT c; + + c.left = r.left; + c.right = r.left; + c.top = r.top; + c.bottom = r.bottom; + if (bmpbackgroundleft.getBitmap()) { + c.right = c.left + bmpbackgroundleft.getWidth(); + bmpbackgroundleft.getBitmap()->stretchToRectAlpha(canvas, &c); + } + int l = c.right; + c.left = r.right; + c.right = r.right; + if (bmpbackgroundright.getBitmap()) { + c.left = r.right - bmpbackgroundright.getWidth(); + bmpbackgroundright.getBitmap()->stretchToRectAlpha(canvas, &c); + } + c.right = c.left; + c.left = l; + if (bmpbackgroundmiddle.getBitmap()) { + bmpbackgroundmiddle.getBitmap()->stretchToRectAlpha(canvas, &c); + } + + c.left = r.left + buttonx+shiftleft; + c.top = r.top + 0; + c.right = r.left + buttonx+thisbutton.getWidth()+shiftleft; + c.bottom = r.top + getWidth(); + + thisbutton.stretchToRectAlpha(canvas, &c); + + c.left = r.left; + c.top = r.top; + c.right = r.left + thisleft.getWidth(); + c.bottom = r.top + getWidth(); + + thisleft.stretchToRectAlpha(canvas, &c); + + c.left = r.right-thisright.getWidth(); + c.top = r.top; + c.right = r.right; + c.bottom = r.top+getWidth(); + + thisright.stretchToRectAlpha(canvas, &c); + } else { + RECT c; + + c.top = r.top; + c.bottom = r.top; + c.left = r.left; + c.right = r.right; + if (bmpbackgroundleft.getBitmap()) { + c.bottom = c.top + bmpbackgroundleft.getHeight(); + bmpbackgroundleft.getBitmap()->stretchToRectAlpha(canvas, &c); + } + int l = c.bottom; + c.top = r.bottom; + c.bottom = r.bottom; + if (bmpbackgroundright.getBitmap()) { + c.top = r.bottom - bmpbackgroundright.getHeight(); + bmpbackgroundright.getBitmap()->stretchToRectAlpha(canvas, &c); + } + c.bottom = c.top; + c.top = l; + if (bmpbackgroundmiddle.getBitmap()) { + bmpbackgroundmiddle.getBitmap()->stretchToRectAlpha(canvas, &c); + } + + c.left = r.right - thisleft.getWidth(); + c.top = r.top+buttonx + shiftleft; + c.right = r.right; + c.bottom = r.top+buttonx+thisbutton.getHeight() + shiftleft; + + thisbutton.stretchToRectAlpha(canvas, &c); + + c.left = r.right - thisleft.getWidth(); + c.top = r.top; + c.right = r.right; + c.bottom = r.top+thisleft.getHeight(); + + thisleft.stretchToRectAlpha(canvas, &c); + + c.left = r.right-thisright.getWidth(); + c.top = r.bottom-thisright.getHeight(); + c.right = r.right; + c.bottom = r.bottom; + + thisright.stretchToRectAlpha(canvas, &c); + } + + return 1; +} + +int ScrollBar::getHeight() { + return height; +} + +void ScrollBar::setHeight(int newheight) { + height = newheight; +} + +int ScrollBar::onResize() { + calcXPosition(); + invalidate(); + return 1; +} + +int ScrollBar::onInit() { + SCROLLBAR_PARENT::onInit(); + return 1; +} + +void ScrollBar::setBitmaps(wchar_t *left, wchar_t *lpressed, wchar_t *lhilite, + wchar_t *right, wchar_t *rpressed, wchar_t *rhilite, + wchar_t *button, wchar_t *bpressed, wchar_t *bhilite) { + + deleteResources(); + + bmpleft = left; + bmplpressed = lpressed; + bmplhilite = lhilite; + bmpright = right; + bmprpressed = rpressed; + bmprhilite = rhilite; + bmpbutton = button; + bmpbpressed = bpressed; + bmpbhilite = bhilite; + + loadBmps(); +} + +void ScrollBar::setBackgroundBitmaps(const wchar_t *left, const wchar_t *middle, const wchar_t *right) { + bmpbackgroundleft = left; + bmpbackgroundmiddle = middle; + bmpbackgroundright = right; +} + +void ScrollBar::loadBmps() { + + if (bmpleft.getBitmap()) leftrgn = new RegionI(bmpleft); + if (bmpbutton.getBitmap()) buttonrgn = new RegionI(bmpbutton); + if (bmpright.getBitmap()) rightrgn = new RegionI(bmpright); + + calcOverlapping(); + calcXPosition(); +} + +void ScrollBar::setPosition(int pos) { + setPrivatePosition(pos, FALSE); +} + +void ScrollBar::setPrivatePosition(int pos, bool signal, bool smooth) { + if (insetpos) return; // helps stupid people (like me) + insetpos = 1; + position = MIN(SCROLLBAR_FULL, pos); + position = MAX(0, position); + calcXPosition(); + if (signal) onSetPosition(smooth); + if (isInited() && isVisible()) + invalidate(); + insetpos = 0; +} + +int ScrollBar::getPosition() { + return position; +} + +int ScrollBar::onSetPosition(bool smooth) { + notifyParent(ChildNotify::SCROLLBAR_SETPOSITION, smooth); + return 1; +} + +int ScrollBar::onSetFinalPosition() { + notifyParent(ChildNotify::SCROLLBAR_SETFINALPOSITION); + return 1; +} + +void ScrollBar::calcOverlapping() { + + if (!vertical) { + + shiftleft = bmpleft.getWidth(); + if (leftrgn && buttonrgn) { + int i; + for (i=shiftleft;i>=0;i--) { + api_region *reg = buttonrgn->clone(); + reg->offset(i, 0); + if (leftrgn->doesIntersectRgn(reg)) { + i++; + buttonrgn->disposeClone(reg); + break; + } + buttonrgn->disposeClone(reg); + } + if (i >= 0) + shiftleft = i; + } + + shiftright = bmpright.getWidth(); + if (rightrgn && buttonrgn) { + int i; + for (i=0;i>=-shiftright;i--) { + api_region *reg = rightrgn->clone(); + reg->offset(i+bmpbutton.getWidth(), 0); + if (reg->doesIntersectRgn(buttonrgn)) { + i++; + rightrgn->disposeClone(reg); + break; + } + rightrgn->disposeClone(reg); + } + if (i >= -shiftright) + shiftright += i; + } + + } else { + + shiftleft = bmpleft.getHeight(); + if (leftrgn && buttonrgn) { + int i; + for (i=shiftleft;i>=0;i--) { + api_region *reg = buttonrgn->clone(); + reg->offset(0, i); + if (leftrgn->doesIntersectRgn(reg)) { + i++; + buttonrgn->disposeClone(reg); + break; + } + buttonrgn->disposeClone(reg); + } + if (i >= 0) + shiftleft = i; + } + + shiftright = bmpright.getHeight(); + if (rightrgn && buttonrgn) { + int i; + for (i=0;i>=-shiftright;i--) { + api_region *reg = rightrgn->clone(); + reg->offset(0, i+bmpbutton.getHeight()); + if (reg->doesIntersectRgn(buttonrgn)) { + i++; + rightrgn->disposeClone(reg); + break; + } + rightrgn->disposeClone(reg); + } + if (i >= -shiftright) + shiftright += i; + } + + } + +} + +void ScrollBar::calcXPosition() { + + if (!isInited()) return; + + RECT r; + getClientRect(&r); + + int maxwidth; + + if (!vertical) + maxwidth = (r.right-r.left)-(bmpbutton.getWidth()+shiftleft+shiftright)+1; + else + maxwidth = (r.bottom-r.top)-(bmpbutton.getHeight()+shiftleft+shiftright)+1; + int oldx = buttonx; + buttonx = (int)(((float)getPosition() / SCROLLBAR_FULL) * maxwidth); + if (buttonx != oldx) + invalidate(); +} + +void ScrollBar::calcPosition() { + + if (!isInited()) return; + + RECT r; + getClientRect(&r); + + int maxwidth; + + if (!vertical) + maxwidth = r.right-r.left-(bmpbutton.getWidth()+shiftleft+shiftright)+1; + else + maxwidth = r.bottom-r.top-(bmpbutton.getHeight()+shiftleft+shiftright)+1; + setPrivatePosition((int)((float)buttonx / maxwidth * SCROLLBAR_FULL)); + //invalidate(); +} + +void ScrollBar::timerCallback(int id) { + switch (id) { + case TIMER_ID: + if (firstdelay) { + killTimer(TIMER_ID); + setTimer(TIMER_ID, NEXT_DELAY); + timer = 1; + firstdelay = 0; + } + checkPageUpDown(); + break; + case TIMER_ID2: + if (firstdelay) { + killTimer(TIMER_ID2); + setTimer(TIMER_ID2, NEXT_DELAY); + timer2 = 1; + firstdelay = 0; + } + checkUpDown(); + break; + default: + SCROLLBAR_PARENT::timerCallback(id); + } +} + +// FG> smooth scrolling forced on, sorry, microsoft does it too so the user perceives IE scrolling as faster than it actually is +// eventho they tell you "The smooth-scrolling effect for list boxes should be disabled when this setting is FALSE. Your application must do this if it creates customized list boxes", they +// break their own rule so people don't bitch too much. ergo there is no reason we should not do that too. + +int ScrollBar::pageUp() { + + pageway = PAGE_UP; + + setPrivatePosition((int)MAX(0.f, (float)getPosition() + (float)SCROLLBAR_FULL / (npages-1)), TRUE, 1/*Std::osparam_getSmoothScroll()*/); + + return 1; +}; + +int ScrollBar::pageDown() { + + pageway = PAGE_DOWN; + + setPrivatePosition((int)MIN((float)SCROLLBAR_FULL, (float)getPosition() - (float)SCROLLBAR_FULL / (npages-1)), TRUE, 1/*Std::osparam_getSmoothScroll()*/); + + return 1; +}; + +void ScrollBar::setNPages(int n) { + //ASSERT(n >= 2); + if (n < 2) n = 2; + npages = n; +} + +void ScrollBar::gotoPage(int page) { + + page = MIN(page, npages-1); + page = MAX(page, 0); + + setPrivatePosition((int)((float)SCROLLBAR_FULL / (npages-1) * page), TRUE, FALSE); + +} + +void ScrollBar::setUpDownValue(int newupdown) { + updown = newupdown; +} + +int ScrollBar::upDown(int which) { + switch (which) { + case POS_LEFT: + setPrivatePosition(getPosition()-updown); + break; + case POS_RIGHT: + setPrivatePosition(getPosition()+updown); + break; + } + return 1; +} + +void ScrollBar::setVertical(bool isvertical) { + vertical = isvertical; + calcOverlapping(); + if (isInited()) + invalidate(); +} + diff --git a/Src/Wasabi/api/wnd/wndclass/scrollbar.h b/Src/Wasabi/api/wnd/wndclass/scrollbar.h new file mode 100644 index 00000000..98be5400 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/scrollbar.h @@ -0,0 +1,423 @@ +#ifndef __SCROLLBAR_H +#define __SCROLLBAR_H + +#include <api/wnd/virtualwnd.h> +#include <tataki/region/region.h> +#include <api/wnd/usermsg.h> +#include <tataki/bitmap/autobitmap.h> + +#define SCROLLBAR_FULL 65535 + +#define POS_NONE 0 +#define POS_LEFT 1 +#define POS_BUTTON 2 +#define POS_RIGHT 3 + +#define PAGE_NONE 0 +#define PAGE_DOWN 1 +#define PAGE_UP 2 + +#define DEFAULT_HEIGHT 16 + +#define SCROLLBAR_PARENT VirtualWnd + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class ScrollBar : public SCROLLBAR_PARENT { +public: + + /** + Method + + @see + @ret + @param + */ + ScrollBar(); + + /** + Method + + @see + @ret + @param + */ + virtual ~ScrollBar(); + + virtual int onMouseMove (int x, int y); + + /** + Method + + @see + @ret + @param + */ + virtual int onLeftButtonDown(int x, int y); + + /** + Method + + @see + @ret + @param + */ + virtual int onLeftButtonUp(int x, int y); + + /** + Method + + @see + @ret + @param + */ + virtual int onRightButtonDown(int x, int y); + + /** + Method + + @see + @ret + @param + */ + virtual int onRightButtonUp(int x, int y); + + /** + Method + + @see + @ret + @param + */ + virtual int onMouseWheelUp(int clicked, int lines); + + /** + Method + + @see + @ret + @param + */ + virtual int onMouseWheelDown(int clicked, int lines); + + /** + Method + + @see + @ret + @param + */ + virtual int onPaint(Canvas *canvas); + + /** + Method + + @see + @ret + @param + */ + virtual int onResize(); + + /** + Method + + @see + @ret + @param + */ + virtual int onInit(); + + /** + Method + + @see + @ret + @param + */ + virtual void timerCallback(int id); + + + /** + Method + + @see + @ret + @param + */ + virtual int wantDoubleClicks() { return 0; }; + + + virtual int onSetPosition(bool smooth=false); + + virtual int onSetFinalPosition(); + + void setBitmaps(wchar_t *left, wchar_t *lpressed, wchar_t *lhilite, + wchar_t *right, wchar_t *rpressed, wchar_t *rhilite, + wchar_t *button, wchar_t *bpressed, wchar_t *bhilite); + + void setBackgroundBitmaps(const wchar_t *left, const wchar_t *middle, const wchar_t *right); + + void setPosition(int pos); + + /** + Method + + @see + @ret + @param + */ + int getPosition(); + + /** + Method + + @see + @ret + @param + */ + int getHeight(); + + /** + Method + + @see + @ret + @param + */ + void setHeight(int newheight); + + /** + Method + + @see + @ret + @param + */ + void setNPages(int n); + + /** + Method + + @see + @ret + @param + */ + void gotoPage(int n); + + /** + Method + + @see + @ret + @param + */ + void setUpDownValue(int newupdown); + + /** + Method + + @see + @ret + @param + */ + void setVertical(bool isvertical); + + /** + Method + + @see + @ret + @param + */ + int getWidth(); + + + /** + Method + + @see + @ret + @param + */ + virtual void freeResources(); + + /** + Method + + @see + @ret + @param + */ + virtual void reloadResources(); + +private: + + /** + Method + + @see + @ret + @param + */ + void deleteResources(); + + /** + Method + + @see + @ret + @param + */ + int getMousePosition(); + + /** + Method + + @see + @ret + @param + */ + void calcOverlapping(); + + /** + Method + + @see + @ret + @param + */ + void calcXPosition(); + + /** + Method + + @see + @ret + @param + */ + void calcPosition(); + + /** + Method + + @see + @ret + @param + */ + void handlePageUpDown(); + + /** + Method + + @see + @ret + @param + */ + int checkPageUpDown(); + + /** + Method + + @see + @ret + @param + */ + void handleUpDown(); + + /** + Method + + @see + @ret + @param + */ + int checkUpDown(); + + /** + Method + + @see + @ret + @param + */ + int pageUp(); + + /** + Method + + @see + @ret + @param + */ + int pageDown(); + + /** + Method + + @see + @ret + @param + */ + int upDown(int which); + + /** + Method + + @see + @ret + @param + */ + void setPrivatePosition(int pos, bool signal=true, bool smooth=false); + + /** + Method + + @see + @ret + @param + */ + void loadBmps(); + + AutoSkinBitmap bmpleft, bmplpressed, bmplhilite, + bmpright, bmprpressed, bmprhilite, + bmpbutton, bmpbpressed, bmpbhilite, + bmpbackgroundleft, bmpbackgroundmiddle, bmpbackgroundright; + + RegionI *leftrgn, *rightrgn, *buttonrgn; + int position; + + int moving; + int lefting; + int righting; + int clicked; + + int buttonx; + + int curmouseposition; + int clickmouseposition; + int height; + + int shiftleft, shiftright; + POINT clickpos; + int clickbuttonx; + int pageing; + int firstdelay; + int timer; + int npages; + int pageway; + int updown; + int timer2; + int insetpos; + + int vertical; + int lastx, lasty; +}; + + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/sepwnd.cpp b/Src/Wasabi/api/wnd/wndclass/sepwnd.cpp new file mode 100644 index 00000000..da45b11b --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/sepwnd.cpp @@ -0,0 +1,63 @@ +#include <precomp.h> + +#include <tataki/bitmap/bitmap.h> +#include <api/wnd/basewnd.h> +#include "sepwnd.h" +#include <tataki/canvas/canvas.h> +#include <api/wnd/PaintCanvas.h> + +SepWnd::SepWnd() { + bitmap = NULL; + orientation = SEP_UNKNOWN; +} + +SepWnd::~SepWnd() { + if (bitmap) delete bitmap; +} + +int SepWnd::onPaint(Canvas *canvas) { + + PaintBltCanvas paintcanvas; + if (canvas == NULL) { + if (!paintcanvas.beginPaintNC(this)) return 0; + canvas = &paintcanvas; + } + + + if (!bitmap) { + switch (orientation) { + case SEP_VERTICAL: + bitmap = new SkinBitmap(L"studio.FrameVerticalDivider"); + break; + case SEP_HORIZONTAL: + bitmap = new SkinBitmap(L"studio.FrameHorizontalDivider"); + break; + case SEP_UNKNOWN: + return 1; + } + ASSERT(bitmap != NULL); + } + RECT r; + getClientRect(&r); + RenderBaseTexture(canvas, r); + if (bitmap) { + bitmap->stretchToRectAlpha(canvas, &r, 128); + } + return 1; +} + +int SepWnd::setOrientation(int which) { + orientation = which; + return 1; +} + +int SepWnd::onInit() { + SEPWND_PARENT::onInit(); + return 1; +} + +void SepWnd::freeResources() { + SEPWND_PARENT::freeResources(); + if (bitmap) delete bitmap; + bitmap = NULL; +}
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/sepwnd.h b/Src/Wasabi/api/wnd/wndclass/sepwnd.h new file mode 100644 index 00000000..60f55b72 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/sepwnd.h @@ -0,0 +1,27 @@ +#ifndef __SEPWND_H +#define __SEPWND_H + +#include <tataki/bitmap/bitmap.h> +#include <api/wnd/virtualwnd.h> + +#define SEP_UNKNOWN -1 +#define SEP_VERTICAL 0 +#define SEP_HORIZONTAL 1 + +#define SEPWND_PARENT VirtualWnd + +class SepWnd : public VirtualWnd { +public: + SepWnd(); + ~SepWnd(); + virtual int onPaint(Canvas *c); + virtual int setOrientation(int which); + virtual int onInit(); + virtual void freeResources(); +private: + SkinBitmap *bitmap; + int orientation; +}; + + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/slider.cpp b/Src/Wasabi/api/wnd/wndclass/slider.cpp new file mode 100644 index 00000000..51157d7f --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/slider.cpp @@ -0,0 +1,705 @@ +#include <precomp.h> +#include "slider.h" + +#include <tataki/canvas/canvas.h> +#include <api/wnd/notifmsg.h> +#include <api/wnd/PaintCanvas.h> + +#define DEFAULT_THUMBWIDTH 16 +#define DEFAULT_THUMBHEIGHT 16 + +SliderWnd::SliderWnd() +{ + seeking = 0; + enabled = 1; + hilite = 0; + pos = 0; + oldpos = -1; + thumbwidth = DEFAULT_THUMBWIDTH; + captured = 0; + xShift = 0; + yShift = 0; + + base_texture = NULL; + use_base_texture = 0; + no_default_background = 0; + + drawOnBorders = 0; + hotPosition = -1; + origPos = 0; + + vertical = 0; + + thumbCentered = 1; + thumbOffset = 0; + thumbStretched = 0; + hotposrange = -1; + setLimits(START, END); +} + +SliderWnd::~SliderWnd() +{} + + +int SliderWnd::onPaint(Canvas *canvas) +{ + if (canvas == NULL) + { + PaintBltCanvas paintcanvas; + if (!paintcanvas.beginPaint(this)) + return 0; + SliderWnd::onPaint(&paintcanvas); + } + + SLIDERWND_PARENT::onPaint(canvas); + + RECT r, origr; + getClientRect(&r); + origr = r; + + if (use_base_texture) + { + if (!base_texture) + { + renderBaseTexture(canvas, r); + } + else + { + RECT cr; + cr.left = xShift; + cr.top = yShift; + cr.right = cr.left + (r.right - r.left); + cr.bottom = cr.top + (r.bottom - r.top); + base_texture->blitToRect(canvas, &cr, &r); + } + } + + if (vertical) + { + RECT br; + br.left = r.left; + br.right = r.right; + + if (left.getBitmap()) + { + br.top = r.top; + br.bottom = left.getHeight(); + left.getBitmap()->stretchToRectAlpha(canvas, &br, getPaintingAlpha()); + } + + if (right.getBitmap()) + { + br.top = r.bottom - right.getHeight(); + br.bottom = r.bottom; + right.stretchToRectAlpha(canvas, &br, getPaintingAlpha()); + } + + if (middle.getBitmap()) + { + br.top = r.top + (left.getBitmap() ? left.getHeight() : 0); + br.bottom = r.bottom - (right.getBitmap() ? right.getHeight() : 0); + middle.getBitmap()->stretchToRectAlpha(canvas, &br, getPaintingAlpha()); + } + } + else + { + RECT br; + br.top = r.top; + br.bottom = r.bottom; + + if (left.getBitmap()) + { + br.left = r.left; + br.right = br.left + left.getWidth(); + left.getBitmap()->stretchToRectAlpha(canvas, &br, getPaintingAlpha()); + } + + if (right.getBitmap()) + { + br.left = r.right - right.getWidth(); + br.right = r.right; + right.getBitmap()->stretchToRectAlpha(canvas, &br, getPaintingAlpha()); + } + + if (middle.getBitmap()) + { + br.left = r.left + (left.getBitmap() ? left.getWidth() : 0); + br.right = r.right - (right.getBitmap() ? right.getWidth() : 0); + middle.getBitmap()->stretchToRectAlpha(canvas, &br, getPaintingAlpha()); + } + } + + if (vertical) + { + int w = (r.bottom - r.top) - thumbHeight(); + // ASSERT(w > 0); // if the control paints off the edge of the screen, this will needlessly assert. + if (w < 0) w = 0; + r.top += (pos * w) / length; + r.bottom = r.top + thumbHeight(); + if (!thumbStretched) + { + if (!thumbCentered) + { + r.left = origr.left + thumbOffset; + r.right = origr.left + thumbWidth() + thumbOffset; + } + else + { + int w = ((r.right - r.left) - thumbWidth()) / 2; + r.left = origr.left + w + thumbOffset; + r.right = origr.right - w + thumbOffset; + } + } + else + { + r.left = origr.left; + r.right = origr.right; + } + + } + else + { + // offset for left bitmap + if (!drawOnBorders) + { + if (left.getBitmap() != NULL) r.left += left.getWidth(); + if (right.getBitmap() != NULL) r.right -= right.getWidth(); + } + + int w = (r.right - r.left) - thumbWidth(); + if (w < 0) w = 0; + r.left += (pos * w) / length; + r.right = r.left + thumbWidth(); + if (r.right > origr.right) + { + r.left -= r.right - origr.right; + r.right = origr.right; + } + + if (!thumbStretched) + { + int thumbh = thumb.getBitmap() ? thumb.getHeight() : DEFAULT_THUMBWIDTH; + if (thumbCentered) + { + int h = ((r.bottom - r.top) - thumbh) / 2; + r.top = origr.top + h; + r.bottom = origr.bottom - h; + } + else + { + r.top = origr.top + thumbOffset; + r.bottom = origr.top + thumbh + thumbOffset; + } + } + else + { + r.top = origr.top; + r.bottom = origr.bottom; + } + } + + SkinBitmap *sb = getSeekStatus() ? (thumbdown.getBitmap() ? thumbdown.getBitmap() : thumb.getBitmap()) : ((hilite && thumbhilite.getBitmap()) ? thumbhilite.getBitmap() : thumb.getBitmap()); + + if (sb != NULL) + sb->stretchToRectAlpha(canvas, &r, getPaintingAlpha()); + else + canvas->fillRect(&r, RGB(255, 0, 0)); + + return 1; +} + +int SliderWnd::onInit() +{ + SLIDERWND_PARENT::onInit(); + if (!no_default_background) + { + if (vertical) + { + // Please note that these bitmaps here do not yet exist. + if (left.getBitmapName() == NULL) setLeftBmp(L"wasabi.slider.vertical.top"); + if (middle.getBitmapName() == NULL) setMiddleBmp(L"wasabi.slider.vertical.middle"); + if (right.getBitmapName() == NULL) setRightBmp(L"wasabi.slider.vertical.bottom"); + if (thumb.getBitmapName() == NULL) setThumbBmp(L"wasabi.slider.vertical.button"); + if (thumbdown.getBitmapName() == NULL) setThumbDownBmp(L"wasabi.slider.vertical.button.pressed"); + } + else + { + if (left.getBitmapName() == NULL) setLeftBmp(L"wasabi.slider.horizontal.left"); + if (middle.getBitmapName() == NULL) setMiddleBmp(L"wasabi.slider.horizontal.middle"); + if (right.getBitmapName() == NULL) setRightBmp(L"wasabi.slider.horizontal.right"); + if (thumb.getBitmapName() == NULL) setThumbBmp(L"wasabi.slider.horizontal.button"); + if (thumbdown.getBitmapName() == NULL) setThumbDownBmp(L"wasabi.slider.horizontal.button.pressed"); + } + } + + + return 1; +} + +int SliderWnd::onLeftButtonDown(int x, int y) +{ + SLIDERWND_PARENT::onLeftButtonDown(x, y); + if (!enabled) return 0; + seeking = 1; + + origPos = 0; + RECT r; + getClientRect(&r); + if (vertical) + { + int w = (r.bottom - r.top) - thumbHeight(); + if (w < 0) w = 0; + r.top += (pos * w) / length; + origPos = (y - r.top) - 1; + /*if(origPos<0 || origPos>thumbHeight())*/ origPos = (thumbHeight() / 2) - 2; + } + else + { + if (!drawOnBorders) + { + if (left.getBitmap() != NULL) r.left += left.getWidth(); + if (right.getBitmap() != NULL) r.right -= right.getWidth(); + } + int w = (r.right - r.left) - thumbWidth(); + if (w < 0) w = 0; + r.left += (pos * w) / length; + origPos = (x - r.left) - 1; + if (origPos < 0 || origPos > thumbWidth()) origPos = (thumbWidth() / 2) - 2; + } + + if (!captured) + { + captured = 1; + beginCapture(); + } + oldpos = pos; + onMouseMove(x, y); + return 1; +} + +//FG> +//removed cross-hierarchy deletion (crashs due to ancestor in common.dll trying to delete pointers in a different +//heap scope than the one in which they were allocated) +void SliderWnd::setBitmaps(const wchar_t *thumbbmp, const wchar_t *thumbdownbmp, const wchar_t *thumbhighbmp, const wchar_t *leftbmp, const wchar_t *middlebmp, const wchar_t *rightbmp) +{ + setThumbBmp(thumbbmp); + setThumbDownBmp(thumbdownbmp); + setThumbHiliteBmp(thumbhighbmp); + setLeftBmp(leftbmp); + setRightBmp(rightbmp); + setMiddleBmp(middlebmp); +} + +void SliderWnd::setLeftBmp(const wchar_t *name) +{ + left = name; + invalidate(); +} + +void SliderWnd::setMiddleBmp(const wchar_t *name) +{ + middle = name; + invalidate(); +} + +void SliderWnd::setRightBmp(const wchar_t *name) +{ + right = name; + invalidate(); +} + +void SliderWnd::setThumbBmp(const wchar_t *name) +{ + thumb = name; + invalidate(); +} + +void SliderWnd::setThumbDownBmp(const wchar_t *name) +{ + thumbdown = name; + invalidate(); +} + +void SliderWnd::setThumbHiliteBmp(const wchar_t *name) +{ + thumbhilite = name; + invalidate(); +} + +SkinBitmap *SliderWnd::getLeftBitmap() +{ + return left; +} + +SkinBitmap *SliderWnd::getRightBitmap() +{ + return right; +} + +SkinBitmap *SliderWnd::getMiddleBitmap() +{ + return middle; +} + +SkinBitmap *SliderWnd::getThumbBitmap() +{ + return thumb; +} + +SkinBitmap *SliderWnd::getThumbDownBitmap() +{ + return thumbdown; +} + +SkinBitmap *SliderWnd::getThumbHiliteBitmap() +{ + return thumbhilite; +} + +int SliderWnd::getWidth() +{ + if (vertical) + return (getThumbBitmap() ? getThumbBitmap()->getWidth() : 0); + else + { + return 64; + } +} + +int SliderWnd::getHeight() +{ + if (!vertical) + return (getThumbBitmap() ? getThumbBitmap()->getHeight() : 0); + else + { + return 64; + } +} + +void SliderWnd::setEnable(int en) +{ + if (enabled != en) invalidate(); + enabled = en; +} + +int SliderWnd::getEnable(void) +{ + return enabled; +} + +void SliderWnd::setPosition(int newpos, int wantcb) +{ + if (newpos < minlimit) newpos = minlimit; + else if (newpos > maxlimit) newpos = maxlimit; + + if (vertical) pos = maxlimit - newpos; + else /* horizontal */ pos = newpos - minlimit; + + if (wantcb) + onSetPosition(); + + invalidate(); +} + +int SliderWnd::onMouseMove(int x, int y) +{ + int p, w, mouseover; + + SLIDERWND_PARENT::onMouseMove(x, y); + + POINT po = {x, y}; + clientToScreen(&po); + mouseover = (WASABI_API_WND->rootWndFromPoint(&po) == this); + if (mouseover && !seeking && !captured) + { + beginCapture(); + captured = 1; + onEnterArea(); + } + int lasthilite = hilite; + hilite = enabled && mouseover; + if (hilite != lasthilite) + { + if (!mouseover && !seeking && captured) + { + endCapture(); + captured = 0; + onLeaveArea(); + invalidate(); + return 0; + } + invalidate(); + } + + if (!enabled) return 1; + + RECT r, origr; + getClientRect(&r); + x -= r.left; + y -= r.top; + + origr = r; + if (vertical) + { + w = (r.bottom - r.top) - thumbHeight(); + // p = (y - (r.top-origr.top)) - (thumbHeight()/2-2); + p = (y - (r.top - origr.top)) - origPos; + } + else + { + if (!drawOnBorders) + { + if (left != NULL) r.left += left.getWidth(); + if (right != NULL) r.right -= right.getWidth(); + } + w = (r.right - r.left) - thumbWidth(); + // p = (x - (r.left - origr.left)) - (thumbWidth()/2-2); + p = (x - (r.left - origr.left)) - origPos; + } + + if (seeking) + { + pos = (p * length) / w; + if (pos < 0) pos = 0; + else if (pos > length) pos = length; + + if (hotPosition != -1) + { + int a, c; + if (vertical) a = r.bottom - r.top; + else a = r.right - r.left; + c = getHotPosRange(); + if (c == -1) + { + int b = (int)(a * 0.075); + c = (b * length) / a; + } + + /** + EQBand: minlimit -127, maxlimit 127, hotpos 0 + PanBar: minlimit 0, maxlimit 225, hotpos 127 + + VSliders pos starts from top by 0 (winamp behaviour reversed!) + */ + + if (vertical) + { + //if (pos > (hotPosition - c) && pos < (hotPosition + c)) pos = hotPosition; + if ((maxlimit - pos) > (hotPosition - c) && (maxlimit - pos) < (hotPosition + c)) pos = hotPosition - minlimit; // Hehe, now it works ;) + } + else + { + if (pos > (hotPosition - c) && pos < (hotPosition + c)) pos = hotPosition; + //if ((pos - maxlimit)> (hotPosition - c) && (pos - maxlimit) < (hotPosition + c)) pos = hotPosition - minlimit; + } + } + + onSetPosition(); + invalidate(); + } + + return 1; +} + +void SliderWnd::onCancelCapture() +{ + SLIDERWND_PARENT::onCancelCapture(); + if (seeking && captured) + abort(); +} + +int SliderWnd::onLeftButtonUp(int x, int y) +{ + SLIDERWND_PARENT::onLeftButtonUp(x, y); + int wasseeking = seeking; + seeking = 0; + captured = 0; + oldpos = -1; + endCapture(); + if (wasseeking) + onSetFinalPosition(); + invalidate(); + return 1; +} + +int SliderWnd::onRightButtonDown(int x, int y) +{ + SLIDERWND_PARENT::onRightButtonDown(x, y); + if (seeking && captured) + { + abort(); + } + return 1; +} + +int SliderWnd::onChar(unsigned int c) +{ + SLIDERWND_PARENT::onChar(c); + if (seeking && captured && (c == 27)) + { + abort(); + } + return 1; +} + +int SliderWnd::onSetPosition() +{ + if (!isInited()) return 0; + notifyParent(ChildNotify::SLIDER_INTERIM_POSITION, getSliderPosition()); + return 0; +} + +int SliderWnd::onSetFinalPosition() +{ + if (!isInited()) return 0; + notifyParent(ChildNotify::SLIDER_FINAL_POSITION, getSliderPosition()); + return 0; +} + +int SliderWnd::getSliderPosition() +{ + if (vertical) return maxlimit -pos; + else return pos + minlimit; +} + +int SliderWnd::getSeekStatus() +{ + return seeking; +} + +int SliderWnd::thumbWidth() +{ + if (thumb.getBitmap() == NULL) return DEFAULT_THUMBWIDTH; + return thumb.getWidth(); +} + +int SliderWnd::thumbHeight() +{ + if (thumb.getBitmap() == NULL) return DEFAULT_THUMBHEIGHT; + return thumb.getHeight(); +} + +void SliderWnd::setUseBaseTexture(int useit) +{ + use_base_texture = useit; + invalidate(); +} + +void SliderWnd::setBaseTexture(SkinBitmap *bmp, int x, int y) +{ + base_texture = bmp; + use_base_texture = TRUE; + xShift = x; + yShift = y; + invalidate(); +} + +void SliderWnd::setNoDefaultBackground(int no) +{ + no_default_background = no; +} + +void SliderWnd::setDrawOnBorders(int draw) +{ + drawOnBorders = draw; +} + +void SliderWnd::onEnterArea() +{ + SLIDERWND_PARENT::onEnterArea(); +} + +void SliderWnd::onLeaveArea() +{ + SLIDERWND_PARENT::onLeaveArea(); +} + +void SliderWnd::setOrientation(int o) +{ + vertical = o; +} + +void SliderWnd::setHotPosition(int h) +{ + hotPosition = h; +} + +void SliderWnd::setThumbCentered(int c) +{ + thumbCentered = c; +} + +void SliderWnd::setThumbStretched(int c) +{ + thumbStretched = c; +} + + +void SliderWnd::setThumbOffset(int o) +{ + thumbOffset = o; +} + +void SliderWnd::abort() +{ + if (oldpos != -1) + { + seeking = 0; + captured = 0; + endCapture(); + pos = oldpos; + onSetPosition(); + invalidate(); + oldpos = -1; + } + return ; +} + +void SliderWnd::setLimits(int pminlimit, int pmaxlimit) +{ + minlimit = pminlimit; + maxlimit = pmaxlimit; + length = maxlimit - minlimit; +} + +int SliderWnd::onKeyDown(int vkcode) +{ + switch (vkcode) + { + case VK_LEFT: move_left(Std::keyModifier(STDKEY_CONTROL)); return 1; + case VK_RIGHT: move_right(Std::keyModifier(STDKEY_CONTROL)); return 1; + case VK_HOME: move_start(); return 1; + case VK_END: move_end(); return 1; + default: return SLIDERWND_PARENT::onKeyDown(vkcode); + } +} + +void SliderWnd::move_left(int bigstep) +{ + int pos = getSliderPosition(); + if (!bigstep) pos--; else pos -= (ABS(maxlimit - minlimit) / 10); + if (pos < minlimit) pos = minlimit; + setPosition(pos); +} + +void SliderWnd::move_right(int bigstep) +{ + int pos = getSliderPosition(); + if (!bigstep) + pos++; + else + pos += (ABS(maxlimit - minlimit) / 10); + if (pos > maxlimit) + pos = maxlimit; + setPosition(pos); +} + +void SliderWnd::move_start() +{ + setPosition(minlimit); +} + +void SliderWnd::move_end() +{ + setPosition(maxlimit); +} diff --git a/Src/Wasabi/api/wnd/wndclass/slider.h b/Src/Wasabi/api/wnd/wndclass/slider.h new file mode 100644 index 00000000..55c451ea --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/slider.h @@ -0,0 +1,451 @@ +#ifndef _SLIDER_H +#define _SLIDER_H + +#include <bfc/common.h> +#include <tataki/bitmap/autobitmap.h> +#include <api/wnd/wndclass/guiobjwnd.h> + +#define SLIDERWND_PARENT GuiObjectWnd +/** + Slider style control. + + @short Slider style control. + @author Nullsoft + @ver 1.0 +*/ +class SliderWnd : public SLIDERWND_PARENT +{ +public: + /** + Sets the defaults for the slider. Defaults to a horizontal + slider with the thumb in the center and is enabled. + */ + SliderWnd(); + + /** + Nothing is handled by the destructor. + */ + virtual ~SliderWnd(); + + /** + Event is triggered when the window requires a repaint. + Override this to implement your own behavior. + + Paints the slider on canvas according to current + state of the slider. + + @ret 0, Failed; 1, Success; + @param canvas The canvas on which to paint. + */ + virtual int onPaint(Canvas *canvas); + + /** + Event is triggered when the left mouse button is pressed while + the slider has focus. Override this to implement your + own behavior. + + @ret + @param x X coordinate of the mouse pointer. + @param y Y coordinate of the mouse pointer. + */ + virtual int onLeftButtonDown(int x, int y); + + /** + Event is triggered when the mouse has capture on the slider + and is being moved. Override this to implement your own + behavior. + + @ret 0, Failed; 1, Success; + @param x The X position of the mouse. + @param y The Y position of the mouse. + */ + virtual int onMouseMove(int x, int y); // only called when mouse captured + + /** + Event is triggered when the left mouse button is released. + Note that the mouse button must have been previously pressed + for this event to happen. Override this to implement your + own behavior. + + @ret 1, If you handle the event; 0, If you don't handle the event; + @param x The X position of the mouse. + @param y The Y position of the mouse. + */ + virtual int onLeftButtonUp(int x, int y); + + /** + Event is triggered when the right mouse button is pressed. + Override this to implement your own behavior. + */ + virtual int onRightButtonDown(int x, int y); + + /** + Event is triggered when a key is pressed and the slider + has focus. Override this to implement your own behavior. + + @ret 1, If you handle the event; 0, If you don't handle the event; + @param c The key that was pressed. + */ + virtual int onChar(unsigned int c); + + /** + Event is triggered when the mouse enters the region + of the slider. Override this to implement your + own behavior. + */ + virtual void onEnterArea(); + + /** + Event is triggered when the mouse leaves the region + of the slider. Override this to implement your + own behavior. + */ + virtual void onLeaveArea(); + + /** + Event is triggered then the slider is about to be initialized. + Override this event to implement your own behavior. + + By default this will render the slider according the it's current settings + and position of the thumb. + + @ret 1, Success; 0, Failure; + */ + virtual int onInit(); + + /** + Constants for positioning of the thumb. + */ + enum { + START = 0, + END = 65535, + FULL = END + }; + + /** + Set the sliders position. + + @param newpos The sliders new position. + @param wantcb !0, Generate a callback after the position has been set; 0, No callback; + */ + virtual void setPosition(int newpos, int wantcb=1); + + /** + Get the sliders current position. The range is from + START (0) to END (65535). + + @ret The sliders position (ranges from 0 to 65535). + */ + int getSliderPosition(); + + //void cancelSeek(); + + /** + Use a base texture when rendering the slider. + + @see setBaseTexture() + @param useit 0, Do not use; 1, Use base texture; + */ + void setUseBaseTexture(int useit); + + /** + Set the base texture of the slider. + + @see setUseBaseTexture() + @see SkinBitmap + @param bmp The bitmap to use as a texture. + @param x The X position of the base texture. + @param y The Y position of the base texture. + */ + void setBaseTexture(SkinBitmap *bmp, int x, int y); + + /** + Set the draw area to include the edge borders. + + @param draw 0, Do not include the edges; 1, Include the edges; + */ + void setDrawOnBorders(int draw); + + /** + Do not use the default background provided + by the current skin? + + If you set this to 1, you MUST specify your bitmaps. + + @param no 0, Use default background; 1, Do not use default; + */ + void setNoDefaultBackground(int no); + + /** + Set the bitmaps to be used to render the slider. + These include bitmaps for the left, middle, right of + the slider. For the thumb, we have bitmaps for the + normal, hilited and pushed thumb. + + The bitmaps are set using their xml id or "name". + The name should resemble something like this: + "studio.seekbar.left". + + @see setLeftBmp() + @see setMiddleBmp() + @see setRightBmp() + @see setThumbBmp() + @see setThumbDownBmp() + @see setThumbHiliteBmp() + @param thumbbmp The normal thumb bitmap name. + @param thumbdownbmp The thumb down bitmap name. + @param thumbhighbmp The hilited thumb bitmap name. + @param leftbmp The left bitmap of the slider name. + @param middlebmp The middle bitmap of the slider name. + @param rightbmp The right bitmap of the slider name. + */ + void setBitmaps(const wchar_t *thumbbmp, const wchar_t *thumbdownbmp, const wchar_t *thumbhighbmp, const wchar_t *leftbmp, const wchar_t *middlebmp, const wchar_t *rightbmp); + + /** + Set the left bitmap of the slider. + + @param name The left bitmap name. + */ + void setLeftBmp(const wchar_t *name); + + /** + Set the middle bitmap of the slider. + + @param name The middle bitmap name. + */ + void setMiddleBmp(const wchar_t *name); + + /** + Set the right bitmap of the slider. + + @param name The right bitmap name. + */ + void setRightBmp(const wchar_t *name); + + /** + Set the normal thumb bitmap of the slider. + + @param name The normal thumb bitmap name. + */ + void setThumbBmp(const wchar_t *name); + + /** + Set the thumb down bitmap of the slider. + + @param name The thumb down bitmap name. + */ + void setThumbDownBmp(const wchar_t *name); + + /** + Set the hilited thumb bitmap of the slider. + + @param name The hilited thumb bitmap name. + */ + void setThumbHiliteBmp(const wchar_t *name); + + /** + Get the height of the slider in pixels. + + @ret The height of the slider (in pixels). + */ + virtual int getHeight(); + + /** + Get the width of the slider in pixels. + + @ret The width of the slider (in pixels). + */ + virtual int getWidth(); + + /** + Get the left bitmap of the slider. + + @see SkinBitmap + @ret The left SkinBitmap. + */ + SkinBitmap *getLeftBitmap(); + + /** + Get the right bitmap of the slider. + + @see SkinBitmap + @ret The right SkinBitmap. + */ + SkinBitmap *getRightBitmap(); + + /** + Get the middle bitmap of the slider. + + @see SkinBitmap + @ret The middle SkinBitmap. + */ + SkinBitmap *getMiddleBitmap(); + + /** + Get the thumb bitmap of the slider. + + @see SkinBitmap + @ret The thumb SkinBitmap. + */ + SkinBitmap *getThumbBitmap(); + + /** + Get the thumb down bitmap of the slider. + + @see SkinBitmap + @ret The thumb down SkinBitmap. + */ + SkinBitmap *getThumbDownBitmap(); + + /** + Get the thumb hilite bitmap of the slider. + + @see SkinBitmap + @ret The thumb hilite SkinBitmap. + */ + SkinBitmap *getThumbHiliteBitmap(); + + /** + Set the sliders enable state. + + @param en 1, Enabled; 0, Disabled; + */ + + virtual void setEnable(int en); + + /** + Get the sliders enable state. + + @ret 1, Enabled; 0, Disabled; + */ + virtual int getEnable(void); + + /** + Set the orientation of the slider + (horizontal or vertical). + + @param o 0, Horizontal; 1, Vertical; + */ + virtual void setOrientation(int o); + + /** + This will set a "jump-to" position (like "center" for a balance slider). + The parameter is in thumb coordinates (0 to 65535). + + @param h The jump-to position (ranges from 0 to 65535, or START to END). + */ + virtual void setHotPosition(int h); + virtual int getHotPosRange() { return hotposrange; } + virtual void setHotPosRange(int range) { hotposrange = range; } + + /** + Set the thumb center flag. If on, this flag will + cause the thumb of the slider to be centered + automatically. + + @param c 1, Centered; 0, No centering; + */ + virtual void setThumbCentered(int c); + virtual void setThumbStretched(int c); + + /** + Set the thumb offset (from the left hand side). + This offset will be added to the zero position of the thumb. + Note, if you're using centering also, this will cause the slider + thumb to be passed the middle of the slider. + + @param o The offset of the thumb (in pixels). + */ + virtual void setThumbOffset(int o); + + /** + Set the minimum and maximum limit for the slider. + + @param minlimit The minimum value. + @param maxlimit The maximum value. + */ + virtual void setLimits(int minlimit, int maxlimit); + + virtual int getMaxLimit() { return maxlimit; } + virtual int getMinLimit() { return minlimit; } + virtual int getRange() { return maxlimit-minlimit; } + + virtual int onKeyDown(int vkcode); + + virtual void onCancelCapture(); +protected: + /** + Abort the current seek and end capture. + */ + void abort(); + + // override this to get position change notification + /** + Event is triggered when the mouse is moving the thumb + is being moved. Override this to implment your own behavior. + + @ret The thumb's position (ranges from 0 to 65535 or START to END). + */ + virtual int onSetPosition(); // called constantly as mouse moves + + /** + Event is triggered when the thumb is released and the final position + is about to be set. + + @ret The thumb's position (ranges from 0 to 65535 or START to END). + */ + virtual int onSetFinalPosition(); // called once after move done + + /** + Get the seeking status. + + @ret 1, User is seeking; 0, User is not seeking; + */ + int getSeekStatus(); // returns 1 if user is sliding tab + + int vertical; // set to 1 for up-n-down instead + + /** + Get the width of the thumb bitmap, in pixels. + + @ret The thumb's width (in pixels). + */ + int thumbWidth(); + + /** + Get the height of the thumb bitmap, in pixels. + + @ret The thumb's width (in pixels). + */ + int thumbHeight(); + // keyboard + void move_left(int bigstep); + void move_right(int bigstep); + void move_start(); + void move_end(); + + int minlimit, maxlimit, length; + +private: + int seeking; + int enabled; + int hilite; + int pos; + int oldpos; + int thumbwidth; + int captured; + int xShift, yShift; + SkinBitmap *base_texture; + int use_base_texture; + int no_default_background; + int drawOnBorders; + int hotPosition; + int origPos; + int thumbCentered, thumbOffset, thumbStretched; + int hotposrange; + + AutoSkinBitmap left, middle, right; + AutoSkinBitmap thumb, thumbdown, thumbhilite; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/status.cpp b/Src/Wasabi/api/wnd/wndclass/status.cpp new file mode 100644 index 00000000..9ef5af9a --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/status.cpp @@ -0,0 +1,242 @@ +#include <precomp.h> +#include "status.h" + +#include <tataki/color/skinclr.h> +#include <tataki/canvas/canvas.h> + +#include <api/wnd/wndclass/buttbar.h> +#include <api/wnd/wndclass/buttwnd.h> +#include <api/wndmgr/appcmds.h> +#include <bfc/parse/paramparser.h> + +#include <api/script/objects/c_script/c_text.h> + +#define STATUS_TIMER_DECAY 1 + +#define COMPLETED_WIDTH 96 + +static SkinColor textcolor(L"wasabi.statusbar.text"); + +class CmdButton : public ButtonWnd +{ +public: + CmdButton(const wchar_t *name, AppCmds *_cmd, int _id) : ButtonWnd(name), cmd(_cmd), id(_id) {} + + virtual void onLeftPush(int x, int y) { + cmd->appcmds_onCommand(id, &windowRect(), AppCmds::LEFT_CLICK); + } + + virtual void onRightPush(int x, int y) { + cmd->appcmds_onCommand(id, &windowRect(), AppCmds::RIGHT_CLICK); + } + + virtual int wantAutoContextMenu() { return 0; } + + AppCmds *cmd; + int id; +}; + +StatusBar::StatusBar() { + overtimer = 0; + max = 0; + completed = 0; + progress_width = 0; + bg.setContent(L"wasabi.statusbar"); + bbleft = bbright = NULL; +} + +StatusBar::~StatusBar() +{ + killTimer(STATUS_TIMER_DECAY); + delete bbleft; + delete bbright; +} + +int StatusBar::onInit() { + STATUSBAR_PARENT::onInit(); + + bg.init(this); + + #ifdef WASABI_COMPILE_WNDMGR + getGuiObject()->guiobject_registerStatusCB(this); // watched + #endif + + regenerate(); + + return 1; +} + +void StatusBar::timerCallback(int id) { + switch (id) { + case STATUS_TIMER_DECAY: { + killTimer(STATUS_TIMER_DECAY); + onSetStatusText(status_text, FALSE); // revert to main text + } + break; + default: + STATUSBAR_PARENT::timerCallback(id); + break; + } +} + +void StatusBar::pushCompleted(int _max) { + max = MAX(_max, 0); + completed = 0; + + GuiObject *outer = bg.findObject(L"wasabi.statusbar.progress.outline"); + outer->guiobject_setXmlParam(L"visible", L"0"); + ASSERT(outer != NULL); + ifc_window *outerw = outer->guiobject_getRootWnd(); + RECT cr; + outerw->getClientRect(&cr); + progress_width = cr.right - cr.left; + + outerw->setVisible(TRUE);//CUT + outer->guiobject_setTargetA(255); + outer->guiobject_setTargetSpeed(0.1f); + outer->guiobject_gotoTarget(); + + GuiObject *inner = bg.findObject(L"wasabi.statusbar.progress.inside"); + inner->guiobject_setTargetA(255); + inner->guiobject_setTargetSpeed(1.0f); + inner->guiobject_gotoTarget(); + inner->guiobject_setXmlParam(L"visible", L"0"); + + incCompleted(0); +} + +void StatusBar::incCompleted(int add) { + setCompleted(completed + add); +} + +void StatusBar::setCompleted(int _completed) { + completed = _completed; + GuiObject *inner = bg.findObject(L"wasabi.statusbar.progress.inside"); + ASSERT(inner != NULL); + if (!inner->guiobject_getRootWnd()->isVisible(1)) { + inner->guiobject_setXmlParam(L"visible", L"1"); + inner->guiobject_setTargetA(255); + inner->guiobject_setTargetSpeed(0.75); + inner->guiobject_gotoTarget(); + } + int pos = (int)(((float)completed / (float)max)*(float)progress_width); + inner->guiobject_setXmlParam(L"w", StringPrintfW(L"%d", pos)); +} + +void StatusBar::popCompleted() { + completed = 0; + max = 0; + GuiObject *inner = bg.findObject(L"wasabi.statusbar.progress.inside"); + inner->guiobject_setXmlParam(L"w", L"0"); + inner->guiobject_setTargetA(0); + inner->guiobject_setTargetSpeed(0.75); + inner->guiobject_gotoTarget(); + +//CUT later + inner->guiobject_setXmlParam(L"visible", L"0"); + GuiObject *outer = bg.findObject(L"wasabi.statusbar.progress.outline"); + outer->guiobject_setXmlParam(L"visible", L"0"); +} + +int StatusBar::onResize() { + STATUSBAR_PARENT::onResize(); + + RECT cr = clientRect(); + + bbleft->resize(cr.left, cr.top, bbleft->getWidth(), cr.bottom - cr.top); + + bbright->resize(cr.right-bbright->getWidth(), cr.top, bbright->getWidth(), cr.bottom - cr.top); + + cr.left += bbleft->getWidth(); + cr.right -= bbright->getWidth(); + + bg.resizeToRect(&cr); // put bg group in place + + invalidate(); + return 1; +} + +void StatusBar::onSetStatusText(const wchar_t *text, int overlay) +{ + killTimer(STATUS_TIMER_DECAY); + if (!overlay) + status_text = text; + else setTimer(STATUS_TIMER_DECAY, 4000); + ScriptObject *tx = bg.findScriptObject(L"wasabi.statusbar.text"); + if (tx == NULL) return; + C_Text(tx).setText(text ? text : L""); +} + +void StatusBar::onAddAppCmds(AppCmds *commands) { + if (appcmds.haveItem(commands)) appcmds.removeItem(commands); + appcmds.addItem(commands); + regenerate(); +} + +void StatusBar::onRemoveAppCmds(AppCmds *commands) { + if (appcmds.haveItem(commands)) { + appcmds.removeItem(commands); + regenerate(); + } +} + +void StatusBar::regenerate() { + if (!isInited()) return; + + delete bbleft; bbleft = new ButtBar; + delete bbright; bbright = new ButtBar; + bbleft->init(this); + bbright->init(this); + + ParamParser exclude(exclude_list, L";"); + ParamParser showonly(include_only, L";"); + + foreach(appcmds) + int n = appcmds.getfor()->appcmds_getNumCmds(); + for (int i = 0; i < n; i++) { + int side, id; + const wchar_t *name = appcmds.getfor()->appcmds_enumCmd(i, &side, &id); + if (name == NULL) break; + if (exclude.hasString(name)) continue; // exclusion list + if (showonly.getNumItems()) { + if (!showonly.hasString(name)) continue; // include-only list + } + CmdButton *cb = new CmdButton(name, appcmds.getfor(), id); +// cb->setXmlParam("wantfocus", "1"); + if (side == AppCmds::SIDE_LEFT) bbleft->addChild(cb); + else bbright->addChild(cb); + } + endfor + if (isPostOnInit()) + onResize(); +} + +void StatusBar::fakeButtonPush(const wchar_t *name) { + if (!fakeButtonPush(bbleft, name)) + fakeButtonPush(bbright, name); +} + +int StatusBar::fakeButtonPush(ButtBar *bb, const wchar_t *name) +{ + for (int i = 0; i < bb->getNumChildren(); i++) { + ButtonWnd *cmdb = bb->enumChild(i); + if (!WCSICMP(cmdb->getName(), name)) { + int x, y; + Wasabi::Std::getMousePos(&x, &y); + cmdb->screenToClient(&x, &y); + cmdb->onLeftPush(x, y); + return 1; + } + } + return 0; +} + +void StatusBar::setExclude(const wchar_t *val) { + exclude_list = val; + regenerate(); +} + +void StatusBar::setIncludeOnly(const wchar_t *val) { + include_only = val; + regenerate(); +} diff --git a/Src/Wasabi/api/wnd/wndclass/status.h b/Src/Wasabi/api/wnd/wndclass/status.h new file mode 100644 index 00000000..426c0723 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/status.h @@ -0,0 +1,178 @@ +#ifndef _STATUS_H +#define _STATUS_H + +#include <api/wnd/wndclass/guiobjwnd.h> +#include <bfc/string/StringW.h> +#include <api/wndmgr/guistatuscb.h> +#include <bfc/depend.h> + +class ButtBar; +class AppCmds; + +#define STATUSBAR_PARENT GuiObjectWnd + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class StatusBar : public STATUSBAR_PARENT, public GuiStatusCallbackI { +public: + + /** + Method + + @see + @ret + @param + */ + StatusBar(); + + /** + Method + + @see + @ret + @param + */ + virtual ~StatusBar(); + + + /** + Method + + @see + @ret + @param + */ + virtual int onInit(); + + // completeness indicator + + /** + Method + + @see + @ret + @param + */ + virtual void pushCompleted(int max); + + /** + Method + + @see + @ret + @param + */ + virtual void incCompleted(int add); + + /** + Method + + @see + @ret + @param + */ + virtual void setCompleted(int pos); + + /** + Method + + @see + @ret + @param + */ + virtual void popCompleted(); + + + /** + Method + + @see + @ret + @param + */ + virtual void timerCallback(int id); + + /** + Method + + @see + @ret + @param + */ + virtual int onResize(); + + virtual api_dependent *status_getDependencyPtr() { return this; } + + + /** + Method + + @see + @ret + @param + */ + virtual void onSetStatusText(const wchar_t *text, int overlay); + + /** + Method + + @see + @ret + @param + */ + virtual void onAddAppCmds(AppCmds *commands); + + /** + Method + + @see + @ret + @param + */ + virtual void onRemoveAppCmds(AppCmds *commands); + + /** + Method + + @see + @ret + @param + */ + void fakeButtonPush(const wchar_t *name); + +protected: + + int fakeButtonPush(ButtBar *bb, const wchar_t *name); + void setExclude(const wchar_t *val); + + void setIncludeOnly(const wchar_t *val); + StringW exclude_list, include_only; + +protected: + void regenerate(); + +private: + StringW contentgroupname; + + StringW status_text; + int overtimer; + + // completeness + int max; + int completed; + int progress_width; + + GuiObjectWnd bg; + + ButtBar *bbleft, *bbright; + + PtrList<AppCmds> appcmds; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/svcwndhold.cpp b/Src/Wasabi/api/wnd/wndclass/svcwndhold.cpp new file mode 100644 index 00000000..9620ce4d --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/svcwndhold.cpp @@ -0,0 +1,53 @@ +#include <precomp.h> + +#include "svcwndhold.h" + +#include <bfc/common.h> + +#include <api/service/svcs/svc_wndcreate.h> + +#include <api/wnd/wndclass/blankwnd.h> + +ServiceWndHolder::ServiceWndHolder(ifc_window *_child, svc_windowCreate *_svc) : + child(NULL), svc(NULL) + { + setChild(_child, _svc); +} + +ServiceWndHolder::~ServiceWndHolder() +{ + if (svc != NULL) + { + svc->destroyWindow(child); + if (!svc->refcount()) + WASABI_API_SVC->service_release(svc); + } else { + delete static_cast<BaseWnd*>(child); + } +} + +int ServiceWndHolder::setChild(ifc_window *_child, svc_windowCreate *_svc) +{ + if (child == _child && svc == _svc) return 0; + + if (child != NULL) { + if (svc != NULL) { + svc->destroyWindow(child); + if (!svc->refcount()) + WASABI_API_SVC->service_release(svc); + svc = NULL; + } else { + delete static_cast<BaseWnd*>(child); + } + child = NULL; + } + + child = _child; + svc = _svc; + + return 1; +} + +ifc_window *ServiceWndHolder::rootwndholder_getRootWnd() { + return child ? child : SERVICEWNDHOLDER_PARENT::rootwndholder_getRootWnd(); +} diff --git a/Src/Wasabi/api/wnd/wndclass/svcwndhold.h b/Src/Wasabi/api/wnd/wndclass/svcwndhold.h new file mode 100644 index 00000000..d671951e --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/svcwndhold.h @@ -0,0 +1,51 @@ +#ifndef _SVCWNDHOLD_H +#define _SVCWNDHOLD_H + +#include <api/wnd/wndclass/rootwndholder.h> +#include <bfc/common.h> + +class svc_windowCreate; + +// for some reason if this derives from virtualwnd typesheet won't show it +#define SERVICEWNDHOLDER_PARENT RootWndHolder + +/** + class ServiceWndHolder . + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class ServiceWndHolder : public SERVICEWNDHOLDER_PARENT { +public: + /** + ServiceWndHolder constructor . + + @param _child A pointer to the child we want to set. + @param _svc A pointer to the window creation service associated with the window we want to set as a child. + */ + ServiceWndHolder(ifc_window *child=NULL, svc_windowCreate *svc=NULL); + + /** + ServiceWndHolder destructor + */ + virtual ~ServiceWndHolder(); + + /** + ServiceWndHolder method setChild . + + @ret 1 + @param _child A pointer to the child we want to set. + @param _svc A pointer to the window creation service associated with the window we want to set as a child. + */ + int setChild(ifc_window *child, svc_windowCreate *svc); + + virtual ifc_window *rootwndholder_getRootWnd(); + +private: + ifc_window *child; + svc_windowCreate *svc; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/tabsheet.cpp b/Src/Wasabi/api/wnd/wndclass/tabsheet.cpp new file mode 100644 index 00000000..6323fcc2 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/tabsheet.cpp @@ -0,0 +1,544 @@ +#include "precomp.h" +#include "tabsheet.h" +#include "buttwnd.h" +#include "buttbar.h" +#include "tabsheetbar.h" + +#include <bfc/wasabi_std.h> +#include <api/wnd/notifmsg.h> +#include <api/script/objects/c_script/c_text.h> +#include <api/script/objects/c_script/c_group.h> +#include <api/wnd/PaintCanvas.h> + +TabSheet::TabSheet(int bbtype) { + leftscroll = rightscroll = NULL; + background = NULL; + tabrowmargin = 0; + active = NULL; + type = bbtype; + bb = NULL; + tsb = NULL; + content_margin_top = content_margin_right = content_margin_left = content_margin_bottom = 0; + contentwnd = NULL; + + if (bbtype == TABSHEET_GROUPS) { + tsb = new TabSheetBar(); + tsb->setParent(this); + } else { // schweitn rulz + bb = new ButtBar((bbtype == -1) ? ButtBar::NORMAL : bbtype); + bb->setParent(this); + if (bbtype == TABSHEET_NOTABS) + bb->setStartHidden(1); + } +} + +TabSheet::~TabSheet() { + delete bb; // kills tabs and child wnds + delete tsb; + delete leftscroll; + delete rightscroll; + delete background; + delete contentwnd; +} + +void TabSheet::setButtonType(int _type) { + type = _type; + if (contentwnd != NULL) { + if (type == ButtBar::STACK) + contentwnd->setContent(L"wasabi.tabsheet.content.noborder"); + else if (type != TABSHEET_NOTABS) + contentwnd->setContent(L"wasabi.tabsheet.content"); + else + contentwnd->setContent(NULL); + } + if (_type == TABSHEET_GROUPS && bb != NULL) { + PtrList<BaseWnd> l; + foreach(tabs) + l.addItem(tabs.getfor()->getBaseWnd()); + tabs.getfor()->setNoDeleteLinked(1); + endfor; + delete bb; bb = NULL; + tabs.removeAll(); + tsb = new TabSheetBar(); + tsb->setParent(this); + if (isInited()) + tsb->init(this); + foreach(l) + addChild(l.enumItem(foreach_index)); + endfor; + } else if (_type != TABSHEET_GROUPS && tsb != NULL) { + PtrList<BaseWnd> l; + foreach(tabs) + l.addItem(tabs.getfor()->getBaseWnd()); + tabs.getfor()->setNoDeleteLinked(1); + endfor; + delete tsb; tsb = NULL; + tabs.removeAll(); + bb = new ButtBar((type == -1) ? ButtBar::NORMAL : type); + bb->setParent(this); + if (type == TABSHEET_NOTABS) + bb->setStartHidden(1); + if (isInited()) + bb->init(this); + foreach(l) + addChild(l.enumItem(foreach_index)); + endfor; + } + if (bb != NULL) bb->setResizeMode(type); +} + +void TabSheet::killChildren() { + if (bb) { + delete bb; // kills tabs and child wnds + bb = new ButtBar((type == -1) ? ButtBar::NORMAL : type); + bb->setParent(this); + if (type == TABSHEET_NOTABS) + bb->setStartHidden(1); + bb->init(this); + } + if (tsb) { + delete tsb; // kills tabs and child wnds + tsb = new TabSheetBar; + tsb->setParent(this); + tsb->init(this); + } + + // mig: if you don't do this, you crash changing tabsheets at runtime. + tabs.removeAll(); + active = NULL; +} + +int TabSheet::onInit() { + TABSHEET_PARENT::onInit(); + + contentwnd = new GuiObjectWnd; + if (type == ButtBar::STACK) + contentwnd->setContent(L"wasabi.tabsheet.content.noborder"); + else if (type != TABSHEET_NOTABS) + contentwnd->setContent(L"wasabi.tabsheet.content"); + else + contentwnd->setContent(NULL); + contentwnd->setParent(this); + contentwnd->init(this); + rootwndholder_setRootWnd(contentwnd); + + if (leftscroll != NULL) { + leftscroll->init(this); + leftscroll->setParent(this); + } + if (rightscroll != NULL) { + rightscroll->init(this); + rightscroll->setParent(this); + } + + // init the windows + foreach(tabs) + if (foreach_index != 0) tabs.getfor()->getBaseWnd()->setStartHidden(TRUE); + tabs.getfor()->getBaseWnd()->init(this); + endfor + + if (bb) { + bb->setParent(this); + if (type == TABSHEET_NOTABS) + bb->setStartHidden(1); + bb->init(this); // inits the tabs + } + if (tsb) { + tsb->setParent(this); + tsb->init(this); // inits the tabs + } + + if (tabs.getNumItems() > 0) { + active = tabs[0]->getBaseWnd(); + //tabs[0]->setHilite(TRUE); // FG: FIX! + } + + if (isPostOnInit()) + onResize(); + + return 1; +} + +void TabSheet::getClientRect(RECT *r) { + TABSHEET_PARENT::getClientRect(r); + if (bb) { + if (type != TABSHEET_NOTABS) + r->top += bb->getHeight(); + } else + r->top += tsb->getHeight(); + + r->left += content_margin_left; + r->top += content_margin_top; + r->right -= content_margin_right; + r->bottom -= content_margin_bottom; +} + +#ifdef WASABI_COMPILE_IMGLDR +void TabSheet::setBackgroundBmp(const wchar_t *name) +{ + if (background) delete background; + background = NULL; + if (name && *name) + background = new SkinBitmap(name); +} +#endif + +SkinBitmap *TabSheet::getBackgroundBitmap() { + return background; +} + +int TabSheet::onPaint(Canvas *canvas) { + + PaintBltCanvas paintcanvas; + if (canvas == NULL) { + if (!paintcanvas.beginPaintNC(this)) return 0; + canvas = &paintcanvas; + } + TABSHEET_PARENT::onPaint(canvas); + + RECT r; + TABSHEET_PARENT::getClientRect(&r); + + if (bb) { + if (type != TABSHEET_NOTABS) + r.bottom = r.top + bb->getHeight(); + } + else if (tsb) + r.bottom = r.top + tsb->getHeight(); + + RECT br = r; + if (leftscroll) br.left += leftscroll->getWidth(); + if (rightscroll) br.right -= rightscroll->getWidth(); + + if (br.right <= br.left) return 1; + + if (background != NULL) { +#if 0 + int i, x = tilex; + for (i = 0; ; i++) { + tile->stretch(canvas, x, 0, tile->getWidth(), tabrowheight); + x += tile->getWidth(); + if (x >= r.right) break; + } +#else +#if 0 + if (background->getAlpha()) api->skin_renderBaseTexture(canvas, br); + background->stretchToRectAlpha(canvas, &br); +#else + background->stretchToRect(canvas, &br); +#endif +#endif + } else { +#if 0 + r.top = 0; + r.bottom = tabrowheight; + r.left = tilex; + r.right = tilex + tilew; + canvas->fillRect(&r, RGB(64, 64, 64)); +#else +// api->skin_renderBaseTexture(canvas, r); +#endif + } + + return 1; +} + +void TabSheet::setTabRowMargin(int newmargin) { + ASSERT(newmargin >= 0); + tabrowmargin = newmargin; + onResize(); +} + +int TabSheet::addChild(BaseWnd *newchild, const wchar_t *tip) { + + ASSERT(newchild != NULL); + + int first=0; + if (tabs.getNumItems() == 0) first = 1; + + if (isInited() && !newchild->isInited()) { + if (!first) newchild->setStartHidden(TRUE); + + ifc_window *holder = this; + if (contentwnd != NULL) { + if (contentwnd->getContentRootWnd() != NULL) { + GuiObject *o = contentwnd->getContent()->guiobject_findObject(L"content"); + if (o != NULL) + holder = o->guiobject_getRootWnd(); + } + } + newchild->setParent(holder); + newchild->init(holder); + } + + if (bb) + { + TabButton *tab = new TabButton(newchild, this, tip); + tabs.addItem(tab); + bb->addChild(tab); + } + else if (tsb) + { + GroupTabButton *tab = new GroupTabButton(newchild, this, tip); + tabs.addItem(tab); + tsb->addChild(tab); + } + if (isInited()) { + if (first) { + activateChild(newchild); + } + } + if (isPostOnInit()) onResize(); + return tabs.getNumItems()-1; +} + +void TabSheet::activateChild(BaseWnd *newactive) { + BaseWnd *prevactive = active; + if (newactive == NULL) newactive = active; + + if (prevactive == newactive) return; // not a switch + +#if 0 + RECT r = clientRect(); + + int w = r.right - r.left + 1; + int h = r.bottom - r.top + 1; +#endif + + int prevpos=-1, nextpos=-1; + + for (int i = 0; i < tabs.getNumItems(); i++) { + if (prevactive == tabs[i]->getBaseWnd()) prevpos = i; + if (newactive == tabs[i]->getBaseWnd()) nextpos = i; + } + + if (prevpos != -1) tabs[prevpos]->btn_setHilite(FALSE); + if (nextpos < tabs.getNumItems()) tabs[nextpos]->btn_setHilite(TRUE); + +#if 0 + // reveal tha new winder + if (newactive!= NULL) newactive->setVisible(TRUE); + + enable(FALSE); + if (prevactive!= NULL) prevactive->enable(FALSE); + if (newactive!= NULL) newactive->enable(FALSE); + +#define STEPS 6 + + // find which window is now active + for (int c = 0; c < STEPS; c++) { + int x; + if (prevpos > nextpos) x = (w * c) / STEPS; // right to left + else x = (w * (STEPS - c)) / STEPS; // left to right + int y = r.top; + if (prevpos > nextpos) { + if (newactive!= NULL) newactive->move(x - w, y); + if (prevactive!= NULL) prevactive->move(x, y); + } else { + if (newactive!= NULL) newactive->move(x, y); + if (prevactive!= NULL) prevactive->move(x - w, y); + } + if (newactive!= NULL) newactive->repaint(); + if (prevactive!= NULL) prevactive->repaint(); + Sleep(15); + } +#endif + + if (newactive!= NULL) newactive->setVisible(TRUE); + + if (prevactive!= NULL) prevactive->setVisible(FALSE); + +#if 0 + enable(TRUE); + if (prevactive!= NULL) prevactive->enable(TRUE); + if (newactive!= NULL) newactive->enable(TRUE); +#endif + + if (bb && newactive) + bb->setGroupLabel(newactive->getName()); + active = newactive; + onSetPage(nextpos); +} + +int TabSheet::onResize() { + TABSHEET_PARENT::onResize(); + + if (!isInited()) return 1; + + RECT r = clientRect(); + + // put buttbar at the top + if (bb) bb->resize(r.left, r.top-bb->getHeight(), r.right-r.left, bb->getHeight()+1); + if (tsb) tsb->resize(r.left, r.top-tsb->getHeight(), r.right-r.left, tsb->getHeight()+1); + + // resize content group if it's there + if (contentwnd) { + contentwnd->resize(&r); + // since its holder is not resizing its content, we need to do it ourselves + foreach(tabs) + BaseWnd *c = tabs.getfor()->getBaseWnd(); + if (c->getParent() != NULL && c->getParent() != this && c->getParent()->getParent() == contentwnd->getContentRootWnd()) { + RECT r; + c->getParent()->getClientRect(&r); + c->resize(&r); + } else { + // if we're holding it directly, resize it to our rect + c->resize(&r); + } + endfor + } + + invalidate(); + if (leftscroll) + leftscroll->invalidate(); + if (rightscroll) + rightscroll->invalidate(); + + return 1; +} + +BaseWnd *TabSheet::enumChild(int child) { + TabButtonBase *tb = tabs[child]; + if (tb == NULL) return NULL; + return tb->getBaseWnd(); +} + +int TabSheet::getNumChild() { + return tabs.getNumItems(); +} + +void TabSheet::setCurPage(int page) { + BaseWnd *e = enumChild(page); + if (e != NULL) activateChild(e); +} + +TabButtonBase *TabSheet::enumButton(int i) { + if (i < tabs.getNumItems()) + return tabs[i]; + else + return NULL; +} + +int TabSheet::childNotify(ifc_window *child, int msg, intptr_t param1, intptr_t param2) { + if (msg == ChildNotify::NAMECHANGED) + { + foreach(tabs) + ifc_window *w = tabs.getfor()->getBaseWnd(); + if (w == child || w == child->getParent()) { + const wchar_t *name = child->getRootWndName(); + tabs.getfor()->btn_setText(name && *name ? name : L"[?]"); + } + endfor; + } + if (msg == ChildNotify::GROUPRELOAD && child == contentwnd) { + foreach(tabs) + ifc_window *holder = this; + if (contentwnd->getContentRootWnd() != NULL) { + GuiObject *o = contentwnd->getContent()->guiobject_findObject(L"content"); + if (o != NULL) + holder = o->guiobject_getRootWnd(); + } + tabs.getfor()->getBaseWnd()->reparent(holder); + endfor; + } + return TABSHEET_PARENT::childNotify(child, msg, param1, param2); +} + + +void TabSheet::setContentMarginLeft(int cm) { + content_margin_left = cm; + if (isInited()) + onResize(); +} + +void TabSheet::setContentMarginTop(int cm) { + content_margin_top = cm; + if (isInited()) + onResize(); +} + +void TabSheet::setContentMarginRight(int cm) { + content_margin_right = cm; + if (isInited()) + onResize(); +} + +void TabSheet::setContentMarginBottom(int cm) { + content_margin_bottom = cm; + if (isInited()) + onResize(); +} + +int TabSheet::onAction(const wchar_t *action, const wchar_t *param, int x, int y, intptr_t p1, intptr_t p2, void *data, size_t datalen, ifc_window *source) { + if (!WCSICMP(action, L"Tabsheet:NextPage")) { nextPage(); return 1; } + if (!WCSICMP(action, L"Tabsheet:PreviousPage")) { previousPage(); return 1; } + return TABSHEET_PARENT::onAction(action, param, x, y, p1, p2, data, datalen, source); +} + +// TabButton + +TabButtonBase::TabButtonBase(BaseWnd *linkwnd, TabSheet *par, const wchar_t *tip) +: linked(linkwnd), parent(par) { + nodeletelinked = 0; + ASSERT(linked != NULL); +} + +TabButtonBase::~TabButtonBase() { + if (!nodeletelinked) delete linked; +} + +int TabButton::onInit() +{ + TABBUTTON_PARENT::onInit(); + setButtonText(linked->getNameSafe(L"[?]")); + return 1; +} + +void TabButton::onLeftPush(int x, int y) { + ASSERT(parent != NULL); + ASSERT(linked != NULL); + parent->activateChild(linked); +} + +void TabButton::btn_setHilite(int tf) { + setHilite(tf); +} + +void TabButton::btn_setText(const wchar_t *text) +{ + setButtonText(text); +} + +// GroupTabButton + +void GroupTabButton::grouptoggle_onLeftPush() { + GROUPTABBUTTON_PARENT::grouptoggle_onLeftPush(); + ASSERT(parent != NULL); + ASSERT(linked != NULL); + parent->activateChild(linked); +} + +void GroupTabButton::btn_setHilite(int tf) { + setStatus(tf ? STATUS_ON : STATUS_OFF); +} + +void GroupTabButton::btn_setText(const wchar_t *text) +{ + for (int i=0;i<getNumGroups();i++) { + GuiObject *grp = enumGroups(i)->getContent(); + if (grp != NULL) { + GuiObject *o = grp->guiobject_findObject(L"text"); + if (o != NULL) { + C_Text txt(o->guiobject_getScriptObject()); + txt.setText(text); + } + } + } +} + +int GroupTabButton::onInit() { + int rt = GROUPTABBUTTON_PARENT::onInit(); + btn_setText(linked->getNameSafe(L"[?]")); + return rt; +} + diff --git a/Src/Wasabi/api/wnd/wndclass/tabsheet.h b/Src/Wasabi/api/wnd/wndclass/tabsheet.h new file mode 100644 index 00000000..73426ec9 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/tabsheet.h @@ -0,0 +1,268 @@ +#ifndef _TABSHEET_H +#define _TABSHEET_H + +#include <api/skin/widgets/grouptgbutton.h> +#include <api/wnd/wndclass/buttwnd.h> +#include <bfc/common.h> +#include <api/wnd/wndclass/guiobjwnd.h> +#include <bfc/ptrlist.h> + +class ButtonWnd; +class SkinBitmap; +class TabButtonBase; +class ButtBar; +class TabSheetBar; + +#define TABSHEET_GROUPS -2 +#define TABSHEET_NOTABS -3 + +#define TABSHEET_PARENT GuiObjectWnd +/** + class TabSheet + + @short A TabSheet Control. + @author Nullsoft + @ver 1.0 + @see + @cat SDK +*/ +class TabSheet : public TABSHEET_PARENT +{ +public: + /** + TabSheet constructor + + @see ~TabSheet() + @param bbtype The type of button bar to use in the tabsheet. + */ + TabSheet(int bbtype=-1); + + /** + TabSheet destructor + @see TabSheet() + */ + virtual ~TabSheet(); + + /** + TabSheet method onInit + @ret 1 + */ + virtual int onInit(); + + /** + TabSheet method getClientRect + + @param r A pointer to the RECT that will be filled. + */ + virtual void getClientRect(RECT *); + + /** + TabSheet method onPaint + + @ret 1 + @param canvas The canvas upon which we'll paint ourself. + */ + virtual int onPaint(Canvas *canvas); + + /** + TabSheet method onResize + + @ret 1 + */ + virtual int onResize(); + + /** + TabSheet method setButtonType . + + @param type The button type. + */ + void setButtonType(int type); + + /** + TabSheet method setTabRowMargin + + @assert newmargin is non-negative + @param newmargin The new margin width in pixels. + */ + void setTabRowMargin(int pixels); + + /** + TabSheet method addChild + + @ret One less than the number of tabs + @param newchild A pointer to the new child window to add. + @param tip The tooltip for the button associated with this child. + */ + int addChild(BaseWnd *newchild, const wchar_t *tooltip=NULL); + + /** + TabSheet method activateChild + + @see addChild() + @see killChildren() + @ret None + @param newactive A pointer to the child window to render active. + */ + virtual void activateChild(BaseWnd *activechild); + + /** + TabSheet method killChildren . + */ + virtual void killChildren(); + + /** + TabSheet method childNotify . + + @ret Returns 1 when complete. + @param child The child that's being notified. + @param msg The message. + @param param1 Custom parameter 1. + @param param2 Custom parameter 2. + */ + virtual int childNotify(ifc_window *child, int msg, + intptr_t param1=0, intptr_t param2=0); + + void setContentMarginLeft(int cm); + void setContentMarginTop(int cm); + void setContentMarginRight(int cm); + void setContentMarginBottom(int cm); + + /** + TabSheet method enumChild + @ret The base window of the specified tab button, or NULL if there is none. + */ + BaseWnd *enumChild(int child); + + /** + TabSheet method getNumChild + @ret The number of tabs + */ + int getNumChild(); + + BaseWnd *getActiveChild() { return active; } + virtual void onSetPage(int n) { lastpage = n; } + int getCurPage() { return lastpage; } + void setCurPage(int page); + int getNumPages() { return tabs.getNumItems(); } + void nextPage() { int n = getCurPage()+1; if (n >= getNumPages()) n = 0; setCurPage(n); } + void previousPage() { int n = getCurPage()-1; if (n < 0) n = getNumPages()-1; setCurPage(n); } + int onAction(const wchar_t *action, const wchar_t *param, int x, int y, intptr_t p1, intptr_t p2, void *data, size_t datalen, ifc_window *source); + +protected: + /** TabSheet method enumButton + @ret The specified tab, or NULL if there is none. */ + TabButtonBase *enumButton(int button); + +public: + /** + TabSheet method setBackgroundBmp + + @param name The name of the bitmap to use. + */ +#ifdef WASABI_COMPILE_IMGLDR + void setBackgroundBmp(const wchar_t *name); //FG +#endif + SkinBitmap *getBackgroundBitmap(); //FG + +protected: + // you can set these in your constructor, they will be deleted for you + ButtonWnd *leftscroll, *rightscroll; + SkinBitmap *background; + +private: + int tabrowheight, tabrowwidth, tabrowmargin; + PtrList<TabButtonBase> tabs; + + ButtBar *bb; + TabSheetBar *tsb; + GuiObjectWnd *contentwnd; + + int tilex, tilew; + + BaseWnd *active; + int type; + int content_margin_left, content_margin_top, content_margin_right, content_margin_bottom; + int lastpage; +}; + +class TabButtonBase +{ + public: + TabButtonBase(BaseWnd *linkWnd, TabSheet *par, const wchar_t *tip=NULL); + virtual ~TabButtonBase(); + + BaseWnd *getBaseWnd() const { return linked; } + void setNoDeleteLinked(int i) { nodeletelinked = i; } + + virtual void btn_setHilite(int tf)=0; + virtual void btn_setText(const wchar_t *txt)=0; + + protected: + BaseWnd *linked; + TabSheet *parent; + int nodeletelinked; +}; + +#define TABBUTTON_PARENT ButtonWnd + +/** + Class TabButton + + @short + @author Nullsoft + @ver 1.0 + @see TabButtonBase + @cat SDK +*/ +class TabButton : public TABBUTTON_PARENT, public TabButtonBase { +public: + /** + TabButton constructor + + @assert The BaseWnd passed to this method must previously be linked. + @param linkwnd The window to associate with this button. + @param par A pointer to the parent tabsheet. + @param tip The tooltip for the window associated with this button. + */ + TabButton(BaseWnd *linkWnd, TabSheet *par, const wchar_t *tip=NULL) : TabButtonBase(linkWnd, par, tip) + { + if (tip != NULL) + setTip(tip); + } + + /** + TabButton method onInit + + @ret 1 + */ + virtual int onInit(); + + /** + TabButton method onLeftPush . + + @assert parent and linked both exist. + @param x The X position, of the mouse pointer, in the client screen. + @param y The Y position, of the mouse pointer, in the client screen. + */ + virtual void onLeftPush(int x, int y); + virtual void btn_setHilite(int tf); + virtual void btn_setText(const wchar_t *text); +}; + + +#define GROUPTABBUTTON_PARENT GroupToggleButton +class GroupTabButton : public GROUPTABBUTTON_PARENT, public TabButtonBase { +public: + GroupTabButton(BaseWnd *linkWnd, TabSheet *par, const wchar_t *tip=NULL) : TabButtonBase(linkWnd, par, tip) + { + setGroups(L"wasabi.tabsheet.button.selected.group", L"wasabi.tabsheet.button.unselected.group"); + } + virtual int wantFullClick() { return 0; } + virtual int wantAutoToggle() { return 0; } + virtual int onInit(); + virtual void grouptoggle_onLeftPush(); + virtual void btn_setHilite(int tf); + virtual void btn_setText(const wchar_t *text); +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/tabsheetbar.cpp b/Src/Wasabi/api/wnd/wndclass/tabsheetbar.cpp new file mode 100644 index 00000000..f31dbc73 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/tabsheetbar.cpp @@ -0,0 +1,130 @@ +#include "precomp.h" +#include "tabsheetbar.h" +#include "tabsheet.h" +#include "../notifmsg.h" + +TabSheetBar::TabSheetBar() { + margin = 0; + spacing = -4; + maxheightsofar = 0; + bottombar.setParent(this); +} + +TabSheetBar::~TabSheetBar() +{ + btns.deleteAll(); +} + +int TabSheetBar::onInit() { + int rt = TABSHEETBAR_PARENT::onInit(); + + bottombar.setContent(L"wasabi.tabsheet.nobutton.group"); + bottombar.init(this); + + foreach(btns) + GroupTabButton *gtb = btns.getfor(); + gtb->setParent(this); + if (!gtb->isInited()) { + gtb->init(this); + } + if (foreach_index == 0) + gtb->setStatus(STATUS_ON); + else + gtb->setStatus(STATUS_OFF); + int h = gtb->getPreferences(SUGGESTED_H); + if (h == AUTOWH) h = maxheightsofar; + maxheightsofar = MAX(maxheightsofar, h); + endfor; + + return rt; +} + +void TabSheetBar::addChild(GroupTabButton *child) { + ASSERT(!btns.haveItem(child)); + btns.addItem(child); + if (isInited()) { + child->setParent(this); + if (!child->isInited()) { + child->init(this); + } + int h = child->getPreferences(SUGGESTED_H); + if (h == AUTOWH) h = maxheightsofar; + maxheightsofar = MAX(maxheightsofar, h); + onResize(); + } + if (btns.getNumItems() == 1) + child->setStatus(STATUS_ON); + else + child->setStatus(STATUS_OFF); +} + +int TabSheetBar::getHeight() { + return maxheightsofar; +} + +int TabSheetBar::onResize() { + int rt = TABSHEETBAR_PARENT::onResize(); + GroupTabButton *selected=NULL; + foreach(btns) + if (btns.getfor()->getStatus() == STATUS_ON) { + selected = btns.getfor(); + break; + } + endfor; + if (selected == NULL) selected = btns.getFirst(); + + RECT r; + getClientRect(&r); + + int x = margin; + + foreach(btns) + GroupTabButton *gtb = btns.getfor(); + + int w = gtb->getPreferences(SUGGESTED_W); + if (w == AUTOWH) w = 66; + + RECT dr; + dr.left = r.left + x; + dr.top = r.top; + dr.right = dr.left + w; + dr.bottom = dr.top + getHeight(); + + gtb->resize(&dr); + + x += w + spacing; + endfor; + + x -= spacing; + RECT dr; + dr.left = r.left + x; + dr.top = r.top; + dr.right = r.right; + dr.bottom = r.top + getHeight(); + bottombar.resize(&dr); + + if (selected != NULL) + selected->bringToFront(); + + return rt; +} + +int TabSheetBar::childNotify(ifc_window *child, int msg, intptr_t param1/* =0 */, intptr_t param2/* =0 */) { + if (msg == ChildNotify::GROUPCLICKTGBUTTON_CLICKED && btns.haveItem(static_cast<GroupTabButton *>(child))) { + GroupToggleButton *but = NULL; + foreach(btns) + if (btns.getfor() != child) + btns.getfor()->setStatus(STATUS_OFF); + else + but = btns.getfor(); + endfor; + if (but != NULL) + but->setStatus(STATUS_ON); + else + (static_cast<GroupToggleButton *>(child))->setStatus(STATUS_ON); + onResize(); + } + if (msg == ChildNotify::AUTOWHCHANGED && btns.haveItem(static_cast<GroupTabButton *>(child)) && isPostOnInit()) + onResize(); + return TABSHEETBAR_PARENT::childNotify(child, msg, param1, param2); +}
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/tabsheetbar.h b/Src/Wasabi/api/wnd/wndclass/tabsheetbar.h new file mode 100644 index 00000000..3070e8eb --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/tabsheetbar.h @@ -0,0 +1,39 @@ +#ifndef __TABSHEETBAR_H +#define __TABSHEETBAR_H + +#include <api/wnd/wndclass/guiobjwnd.h> + +class GroupTabButton; + +#define TABSHEETBAR_PARENT GuiObjectWnd + + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class TabSheetBar : public TABSHEETBAR_PARENT +{ +public: + TabSheetBar(); + virtual ~TabSheetBar(); + virtual int onInit(); + virtual int onResize(); + virtual void addChild(GroupTabButton *child); + virtual int getHeight(); + virtual int childNotify(ifc_window *child, int msg, intptr_t param1 = 0, intptr_t param2 = 0); + void setMargin(int m) { margin = m; if (isInited()) onResize(); } + void setSpacing(int s) { spacing = s; if (isInited()) onResize(); } + +private: + int maxheightsofar; + PtrList<GroupTabButton> btns; + int margin, spacing; + GuiObjectWnd bottombar; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/textbar.cpp b/Src/Wasabi/api/wnd/wndclass/textbar.cpp new file mode 100644 index 00000000..ae695419 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/textbar.cpp @@ -0,0 +1,148 @@ +#include "precomp.h" + +#include "textbar.h" + +#include <bfc/ifc_canvas.h> +#include <bfc/string/string.h> +#include <bfc/skinclr.h> +#include <bfc/autobitmap.h> +#include <common/checkwnd.h> + +static SkinColor bgcolor("wasabi.textBar.background", "Text backgrounds"); +static SkinColor fgcolor("wasabi.textBar.text"); + +TextBar::TextBar() { + size = 16; + usebt = 0; + alignment = TEXTALIGN_LEFT; //set default alignment + checkwndtarget = NULL; + + textshadowed = 1; // display a shadow of the text in bgcolor. default: on + textoutlined = 0; // draw an outline of the text in bgcolor. default: off + drawbox = 0; // draw a box of bgcolor the size of the boundsrect. default: off + +// bgbitmap = "studio.textBar.background"; +} + +int TextBar::onLeftButtonDown(int x, int y) { + TEXTBAR_PARENT::onLeftButtonDown(x, y); + if (checkwndtarget) checkwndtarget->toggle(); + return 1; +} + +void TextBar::setUseBaseTexture(int u) { + usebt = u; + invalidate(); +} + +int TextBar::onPaint(Canvas *canvas) { + RECT r; + + PaintCanvas paintcanvas; + if (canvas == NULL) { + if (!paintcanvas.beginPaint(this)) return 0; + canvas = &paintcanvas; + } + TEXTBAR_PARENT::onPaint(canvas); + + getClientRect(&r); + + if (!usebt) { + if (drawbox) { + canvas->fillRect(&r, bgcolor); + } +/* + if (bgbitmap.getBitmap()->isInvalid()) + canvas->fillRect(&r, bgcolor); + else { + RECT br; + br.left = 0; + br.top = 0; + br.right = bgbitmap.getWidth(); + br.bottom = bgbitmap.getHeight(); + bgbitmap.getBitmap()->blitToRect(canvas, &br, &r, 255); + } +*/ + } else + renderBaseTexture(canvas, r); + + const char *name = getName(); + + if (name != NULL) { + canvas->setTextOpaque(FALSE); + canvas->pushTextSize(size); + int w, h; + canvas->getTextExtent(name, &w, &h); + int y = (r.bottom-r.top - h) / 2; +// int x = centered ? (r.right-r.left - w) / 2 : TEXTBAR_LEFTMARGIN; //teh old code + + int x = 0; + switch (alignment) { + default: + case TEXTALIGN_LEFT: x = TEXTBAR_LEFTMARGIN; break; + case TEXTALIGN_CENTER: x = (r.right-r.left - w) / 2; break; + case TEXTALIGN_RIGHT: x = (r.right-r.left - w); break; + } + + if (!drawbox && textoutlined) { + canvas->setTextColor(bgcolor); + canvas->textOut(r.left+x+1, r.top+y+1, getName()); + canvas->setTextColor(bgcolor); + canvas->textOut(r.left+x+1, r.top+y-1, getName()); + canvas->setTextColor(bgcolor); + canvas->textOut(r.left+x-1, r.top+y+1, getName()); + canvas->setTextColor(bgcolor); + canvas->textOut(r.left+x-1, r.top+y-1, getName()); + } else if (!drawbox && textshadowed) { + canvas->setTextColor(bgcolor); + canvas->textOut(r.left+x+1, r.top+y+1, getName()); + } + canvas->setTextColor(fgcolor); + canvas->textOut(r.left+x, r.top+y, getName()); + canvas->popTextSize(); + } + return 1; +} + +int TextBar::setTextSize(int newsize) { + if (newsize < 1 || newsize > 72) return 0; + size = newsize; + invalidate(); + return 1; +} + +int TextBar::setInt(int i) { + setName(StringPrintf(i)); + invalidate(); + return 1; +} + +void TextBar::onSetName() { + TEXTBAR_PARENT::onSetName(); + invalidate(); +} + +int TextBar::getTextWidth() { + if (!getName()) return 0; + BltCanvas *c = new BltCanvas(10, 10); + c->pushTextSize(size); + int r = c->getTextWidth(getName()); + c->popTextSize(); + delete c; + return r+4; +} + +int TextBar::getTextHeight() { + return size; +} + +void TextBar::setAlign(TextAlign align) { + if (alignment != align) { + alignment = align; + invalidate(); + } +} + +TextAlign TextBar::getAlign() { + return alignment; +} diff --git a/Src/Wasabi/api/wnd/wndclass/textbar.h b/Src/Wasabi/api/wnd/wndclass/textbar.h new file mode 100644 index 00000000..6c6cb97e --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/textbar.h @@ -0,0 +1,219 @@ +//PORTABLE +#ifndef _TEXTBAR_H +#define _TEXTBAR_H + +#include <bfc/virtualwnd.h> +#include <bfc/autobitmap.h> +#include <bfc/textalign.h> + +class CheckWnd; + +#define TEXTBAR_PARENT VirtualWnd +/** + TextBar uses the BaseWnd name field of the object as the text + to be displayed. + + @short TextBar control. + @author Nullsoft + @ver 1.0 + @see LabelWnd +*/ +class TextBar : public VirtualWnd { +public: + /** + Sets the default flags of the TextBar. Defaults to 16px fonts, + no background texture, left justified text, shadowed text in + the bgcolor, no outline, not box around the text. + */ + TextBar(); + + /** + Event is triggered when the window requires a repaint. + Override this to implement your own behavior. + + Paints the bitmap on canvas according to current + options (centering, tiling, stretching, title). + + @ret 0 for failure, 1 for success + @param canvas The canvas on which to paint. + */ + virtual int onPaint(Canvas *canvas); + + /** + Event is triggered when the name of the window is changed. + Override this to implement your own behavior. + + @see BaseWnd::setName() + */ + virtual void onSetName(); + + /** + Set the text to be displayed to an ascii representation of a numeric value. + + @ret 1. + @param i The numeric value to be displayed. + */ + int setInt(int i); + + /** + Set the size of the text for the textbar. + + @ret 1, success; 0, failure. + @param newsize The new text size, range is from 1 to 72 pixels. + */ + int setTextSize(int newsize); + + /** + Get the width of the text displayed, in pixels. + + @ret Width of the displayed text (in pixels). + */ + int getTextWidth(); + + /** + Get the height of the text displayed, in pixels. + + @ret Height of the displayed text. + */ + int getTextHeight(); + + /** + Use the base texture when rendering the TextBar? + If the base texture is used, it will be rendered as + the background of the textbar. + + @param u !0, Use base texture; 0, Do not use base texture; + */ + void setUseBaseTexture(int u); + + /** + Event is triggered when the left mouse button is pressed while + the textbar has focus. Override this to implement your + own behavior. + + @param x X coordinate of the mouse pointer. + @param y Y coordinate of the mouse pointer. + */ + virtual int onLeftButtonDown(int x, int y); + + /** + Center the text in the textbar? If not, + it will be left justified by default. + + @param center !0, Center text; 0, Do not center text; + */ +// void setCenter(int center); //old code + + /** + Get the center text flag. + + @see setCenter() + @ret TRUE, Text is being centered; FALSE, No centering (left justified); + */ +// bool getCentered(); + + /** + Sets the alignment of the text to left, center, or right aligned + (possibly more later on, not too sure yet) + */ + void setAlign(TextAlign alignment); + + /** + @ret returns the alignment of the text + */ + TextAlign getAlign(); + + + // The following three options have ascending overriding priority -- + + /** + Sets the shadowed text flag. If enabled, the text will be shadowed + with the "bgcolor" value. + + @see getTextShadowed() + @param settextshadowed !0, Shadow the text; 0, Do not shadow the text; + */ + void setTextShadowed(int settextshadowed) { + textshadowed = !!settextshadowed; + } + + /** + Get the shadowed text flag. If enabled, the text will be shadowed + with the "bgcolor" value. + + @see setTextShadowed() + @ret !0, Shadow the text; 0, Do not shadow the text; + */ + int getTextShadowed() { + return textshadowed; + } + + /** + Sets the outline text flag. If enabled, the text will be + outlined with the "bgcolor" value. + + @param settextoutlined !0, Outline the text; 0, Do not outline the text; + */ + void setTextOutlined(int settextoutlined) { + textoutlined = !!settextoutlined; + } + + /** + Get the outline text flag. If enabled, the text will be + outlined with the "bgcolor" value. + + @ret !0, Outline the text; 0, Do not outline the text; + */ + int getTextOutlined() { + return textoutlined; + } + + /** + Set the drawbox flag. If true, the drawbox flag will cause + a box to be drawn around the text in the textbar. + + @param setdrawbox !0, Drawbox around the text; 0, No drawbox; + */ + void setDrawBox(int setdrawbox) { + drawbox = !!setdrawbox; + } + + /** + Get the drawbox flag. If true, the drawbox flag will cause + a box to be drawn around the text in the textbar. + + @ret !0, Drawbox around the text; 0, No drawbox; + */ + int getDrawBox() { + return drawbox; + } + + /** + Associate a checkbox with the textbar. When a textbar is linked + to a checkbox, it will toggle the checkbox when it receives + left clicks. + + @param target A pointer to the CheckWnd to link. + */ + void setAutoToggleCheckWnd(CheckWnd *target) { + checkwndtarget = target; + } + + +private: + int size; + int usebt; + TextAlign alignment; //i changed this from centered, to a set text alignment thingie + + + int textshadowed; // display a shadow of the text in bgcolor. default: on + int textoutlined; // draw an outline of the text in bgcolor. default: off + int drawbox; // draw a box of bgcolor the size of the boundsrect. default: off + + AutoSkinBitmap bgbitmap; + CheckWnd *checkwndtarget; +}; + +const int TEXTBAR_LEFTMARGIN = 2; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/tooltip.cpp b/Src/Wasabi/api/wnd/wndclass/tooltip.cpp new file mode 100644 index 00000000..a619ab45 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/tooltip.cpp @@ -0,0 +1,47 @@ +//#include <precomp.h> +#include "./api.h" +#include <api/wnd/api_wnd.h> +#include "tooltip.h" + +#ifdef WASABI_COMPILE_CONFIG +#include <api/config/items/attrint.h> +#include <api/config/items/cfgitem.h> +#endif +#include <api/service/svc_enum.h> + +Tooltip::Tooltip(const wchar_t *txt) +{ + WASABI_API_WND->appdeactivation_push_disallow(NULL); + svc = NULL; + + if (!txt || !*txt) return ; + +#ifdef WASABI_COMPILE_CONFIG + // {9149C445-3C30-4e04-8433-5A518ED0FDDE} + const GUID uioptions_guid = + { 0x9149c445, 0x3c30, 0x4e04, { 0x84, 0x33, 0x5a, 0x51, 0x8e, 0xd0, 0xfd, 0xde } }; + if (!_intVal(WASABI_API_CONFIG->config_getCfgItemByGuid(uioptions_guid), L"Enable tooltips")) + { + // tooltips disabled + return ; + } +#endif + + waServiceFactory *svf = WASABI_API_SVC->service_enumService(WaSvc::TOOLTIPSRENDERER, 0); + svc = castService<svc_toolTipsRenderer>(svf); + + if (!svc) + { + // no tooltips available! + return ; + } + + svc->spawnTooltip(txt); +} + +Tooltip::~Tooltip() +{ + if (svc != NULL) + WASABI_API_SVC->service_release(svc); + WASABI_API_WND->appdeactivation_pop_disallow(NULL); +}
\ No newline at end of file diff --git a/Src/Wasabi/api/wnd/wndclass/tooltip.h b/Src/Wasabi/api/wnd/wndclass/tooltip.h new file mode 100644 index 00000000..18122a40 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/tooltip.h @@ -0,0 +1,18 @@ +#ifndef __TOOLTIP_H +#define __TOOLTIP_H + +#include <api/service/svcs/svc_tooltips.h> + +class Tooltip { + + public: + + Tooltip(const wchar_t *txt); + virtual ~Tooltip(); + + private: + + svc_toolTipsRenderer *svc; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/treewnd.cpp b/Src/Wasabi/api/wnd/wndclass/treewnd.cpp new file mode 100644 index 00000000..99b03c85 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/treewnd.cpp @@ -0,0 +1,1645 @@ +#include "precomp.h" + +#include "treewnd.h" + +#include <tataki/canvas/ifc_canvas.h> +#include <bfc/stack.h> +#include <api/wnd/wndclass/scrollbar.h> +#include <tataki/color/skinclr.h> +#include <api/wnd/notifmsg.h> +#include <api/wnd/accessible.h> +#include <api/wnd/PaintCanvas.h> + +#define DEF_TEXT_SIZE 14 +#define CHILD_INDENT itemHeight +#define X_SHIFT 2 +#define Y_SHIFT 2 +#define DRAG_THRESHOLD 4 + +#define TIMER_EDIT_DELAY 1000 +#define TIMER_EDIT_ID 1249 + +/////////////////////////////////////////////////////////////////////////////// +// TreeWnd +/////////////////////////////////////////////////////////////////////////////// + +static SkinColor textcolor(L"wasabi.tree.text"); +static SkinColor drophilitecolor(L"wasabi.tree.hiliteddrop"); +static SkinColor selectedcolor(L"wasabi.tree.selected"); + +int CompareTreeItem::compareItem(TreeItem *p1, TreeItem *p2) { + return p1->getTree()->compareItem(p1, p2); +} + +TreeWnd::TreeWnd() { + tabClosed = NULL; + tabOpen = NULL; + linkTopBottom = NULL; + linkTopRight = NULL; + linkTopRightBottom = NULL; + linkTabTopBottom = NULL; + linkTabTopRight = NULL; + linkTabTopRightBottom = NULL; + curSelected = NULL; + mousedown_item = NULL; + hitItem = NULL; + draggedItem = NULL; + tipitem = NULL; + edited = NULL; + editwnd = NULL; + metrics_ok = FALSE; + setSorted(TRUE); + setFontSize(DEF_TEXT_SIZE); + redraw = TRUE; + prevbdownitem = NULL; + autoedit=0; + autocollapse=1; + + tabClosed = L"wasabi.tree.tab.closed"; + tabOpen = L"wasabi.tree.tab.open"; + linkTopBottom = L"wasabi.tree.link.top.bottom"; + linkTopRight = L"wasabi.tree.link.top.right"; + linkTopRightBottom = L"wasabi.tree.link.top.rightBottom"; + linkTabTopBottom = L"wasabi.tree.link.tab.top.bottom"; + linkTabTopRight = L"wasabi.tree.link.tab.top.right"; + linkTabTopRightBottom = L"wasabi.tree.link.tab.top.rightBottom"; +} + +TreeWnd::~TreeWnd() { + // delete all root items + deleteAllItems(); + drawList.removeAll(); +} + +int TreeWnd::onInit() { + TREEWND_PARENT::onInit(); + + setBgBitmap(L"wasabi.tree.background"); + setLineHeight(itemHeight); + + return 1; +} + +void TreeWnd::setRedraw(bool r) { + int old = redraw; + redraw = r; + if (!old && redraw) + invalidate(); +} + +int TreeWnd::onPaint(Canvas *canvas) { + + PaintCanvas paintcanvas; + PaintBltCanvas paintbcanvas; + + if (canvas == NULL) { + if (needDoubleBuffer()) { + if (!paintbcanvas.beginPaintNC(this)) return 0; + canvas = &paintbcanvas; + } else { + if (!paintcanvas.beginPaint(this)) return 0; + canvas = &paintcanvas; + } + } + TREEWND_PARENT::onPaint(canvas); + +/* uncomment if you add columns or anything that should be not be drawn over by onPaint in which case you'll have to clip->subtract(your_region) + api_region *clip = new RegionI(); + canvas->getClipRgn(clip); */ + + /*RECT r; + getNonClientRect(&r); + + int y = -getScrollY()+Y_SHIFT+r.top; + int x = -getScrollX()+X_SHIFT;*/ + + Wasabi::FontInfo fontInfo; + fontInfo.color = textcolor; + fontInfo.opaque=false; + fontInfo.pointSize = getFontSize(); + + firstItemVisible = NULL; + lastItemVisible = NULL; + + ensureMetricsValid(); + + //drawSubItems(canvas, x, &y, items, r.top, r.bottom, 0); + drawItems(canvas, &fontInfo); + + canvas->selectClipRgn(NULL); // reset cliping region - NEEDED; + +// delete clip; uncomment if necessary + + return 1; +} + +void TreeWnd::drawItems(Canvas *canvas, const Wasabi::FontInfo *fontInfo) +{ + RECT r, c, ir; + RegionI *orig=NULL; + getClientRect(&r); + if (!canvas->getClipBox(&c)) { + getClientRect(&c); + orig = new RegionI(&c); + } else + orig = new RegionI(canvas); + + int first = ((c.top-r.top) + getScrollY() - Y_SHIFT) / itemHeight; + int last = ((c.bottom-r.top) + getScrollY() - Y_SHIFT) / itemHeight + 1; + POINT pt; + TreeItem *item; + bool hastab; + + for (int i=first;i<=last;i++) + { + + if (i >= drawList.getNumItems()) break; + + item = drawList[i]; + if (!item) continue; + item->getCurRect(&ir); + pt.x = r.left + X_SHIFT+item->getIndent()*itemHeight - getScrollX();//ir.left; + pt.y = ir.top; + + // if we need the +/- icon and any of the link lines, draw them + if (item->needTab()) { +// pt.x += itemHeight; + RECT _r={pt.x-itemHeight, pt.y, pt.x, pt.y+itemHeight}; + (item->isCollapsed() ? tabClosed : tabOpen).stretchToRectAlpha(canvas, &_r); + hastab=TRUE; + } else hastab = FALSE; + + int indent = item->getIndent(); + + for (int j=0;j<indent;j++) + { + RECT _r={pt.x-itemHeight*(j+1), pt.y, pt.x-itemHeight*j, pt.y+itemHeight}; + int l = getLinkLine(item, j); + if (l == (LINK_RIGHT | LINK_TOP)) { + ((hastab && j == 0) ? linkTabTopRight : linkTopRight).stretchToRectAlpha(canvas, &_r); + } + if (l == (LINK_RIGHT | LINK_TOP | LINK_BOTTOM)) { + ((hastab && j == 0) ? linkTabTopRightBottom : linkTopRightBottom).stretchToRectAlpha(canvas, &_r); + } + if (l == (LINK_BOTTOM | LINK_TOP)) { + ((hastab && j == 0) ? linkTabTopBottom : linkTopBottom).stretchToRectAlpha(canvas, &_r); + } + } + + item->customDraw(canvas, pt, itemHeight, (pt.x+getScrollX())-r.left-X_SHIFT, r, fontInfo); + } + + delete orig; +} + +TreeItem *TreeWnd::hitTest(int x, int y) { + POINT pt={x,y}; + return hitTest(pt); +} + +TreeItem *TreeWnd::hitTest(POINT pt) { + RECT r, ir; + getClientRect(&r); + + int first = (getScrollY() - Y_SHIFT) / itemHeight; + int last = ((r.bottom-r.top) + getScrollY() - Y_SHIFT) / itemHeight + 1; + + for (int i=first;i<=last;i++) { + + if (i >= drawList.getNumItems()) break; + + TreeItem *item = drawList.enumItem(i); + + if (item) { + item->getCurRect(&ir); + if (Wasabi::Std::pointInRect(ir, pt) && item->isHitTestable()) + return item; + } + } + + return NULL; +} + +void TreeWnd::getMetrics(int *numItemsShown, int *mWidth) { + *mWidth=0; + *numItemsShown=0; + drawList.removeAll(); + countSubItems(drawList, &items, X_SHIFT, numItemsShown, mWidth, 0); +} + +void TreeWnd::countSubItems(PtrList<TreeItem> &drawlist, TreeItemList *_list, int indent, int *count, int *maxwidth, int z) { + + TreeItemList &list = *_list; + + for (int i=0;i<list.getNumItems();i++) { + + TreeItem *nextitem = list[i]; + + int w = nextitem->getItemWidth(itemHeight, indent-X_SHIFT); + if (indent+w > *maxwidth) *maxwidth = w+indent; + + int j = indent-(nextitem->needTab() ? itemHeight : 0); + int k; + k = indent + w; + + nextitem->setCurRect(j, Y_SHIFT+(*count * itemHeight), k, Y_SHIFT+((*count+1) * itemHeight), z); + (*count)++; + + drawlist.addItem(nextitem); + + if (nextitem->isExpanded()) + countSubItems(drawlist, &nextitem->subitems, indent+CHILD_INDENT, count, maxwidth, z+1); + } +} + +void TreeWnd::timerCallback(int c) { + switch (c) { + case TIMER_EDIT_ID: + prevbdownitem = NULL; + killTimer(TIMER_EDIT_ID); + break; + default: + TREEWND_PARENT::timerCallback(c); + } +} + +int TreeWnd::onLeftButtonDown(int x, int y) { + + if (edited) + { + delete editwnd; editwnd = NULL; + endEditLabel(editbuffer); + } + + POINT pt={x,y}; + TreeItem *item = hitTest(pt); + + if (item) { + mousedown_item = item; + mousedown_anchor.x = pt.x; + mousedown_anchor.y = pt.y; + mousedown_dragdone = FALSE; + // only do expand/collapse if was already selected + setCurItem(item, autocollapse?(curSelected == item):0, FALSE); + beginCapture(); + } + + return 1; +} + +int TreeWnd::onLeftButtonUp(int x, int y) { + if (getCapture()) + endCapture(); + TREEWND_PARENT::onLeftButtonUp(x, y); + POINT pt={x,y}; + TreeItem *item = hitTest(pt); + if (autoedit && item == mousedown_item && item == prevbdownitem) + setCurItem(item, FALSE, TRUE); + else + if (autoedit) { + prevbdownitem = getCurItem(); + setTimer(TIMER_EDIT_ID, TIMER_EDIT_DELAY); + } + + mousedown_item = NULL; + return 1; +} + +int TreeWnd::onRightButtonUp(int x, int y){ + TREEWND_PARENT::onRightButtonUp(x, y); + POINT pos={x,y}; + + TreeItem *ti = hitTest(pos); + if (ti != NULL) { + selectItem(ti); + if (onPreItemContextMenu(ti, x, y) == 0) { + int ret = ti->onContextMenu(x, y); + onPostItemContextMenu(ti, x, y, ret); + return ret; + } + return 1; + } else { + return onContextMenu(x, y); + } +} + +int TreeWnd::onMouseMove(int x, int y) { + + TREEWND_PARENT::onMouseMove(x, y); + + POINT pt={x,y}; + + if (mousedown_item) { + if (!mousedown_dragdone && (ABS(pt.x - mousedown_anchor.x) > DRAG_THRESHOLD || ABS(pt.y - mousedown_anchor.y) > DRAG_THRESHOLD)) { + mousedown_dragdone = TRUE; + if (getCapture()) + endCapture(); + onBeginDrag(mousedown_item); + } + } + else + { + TreeItem *item = hitTest(pt); + if (item) { + if (tipitem != item) { + tipitem = item; + RECT r; + RECT c; + getClientRect(&c); + item->getCurRect(&r); + const wchar_t *tt = item->getTip(); + if (tt != NULL && *tt != '\0') + setLiveTip(tt); + else if (r.right > c.right || r.bottom > c.bottom || r.top < c.top || r.left < c.left) + setLiveTip(item->getLabel()); + else + setLiveTip(NULL); + } + } else { + setLiveTip(NULL); + } + } + + return 1; +} + +int TreeWnd::onLeftButtonDblClk(int x, int y) { + TreeItem *item = hitTest(x, y); + if (item == NULL) return 0; + return item->onLeftDoubleClick(); +} + +int TreeWnd::onRightButtonDblClk(int x, int y) { + TreeItem *item = hitTest(x, y); + if (item == NULL) return 0; + return item->onRightDoubleClick(); +} + +void TreeWnd::setLiveTip(const wchar_t *tip) +{ + if (!tip) + { + setTip(oldtip); + oldtip = L""; + return; + } + oldtip = TREEWND_PARENT::getTip(); + setTip(tip); +} + +int TreeWnd::onBeginDrag(TreeItem *treeitem) +{ + wchar_t title[WA_MAX_PATH]=L""; + // item calls addDragItem() + if (!treeitem->onBeginDrag(title)) return 0; + ASSERT(draggedItem == NULL); + draggedItem = treeitem; + if (*title != 0) setSuggestedDropTitle(title); + handleDrag(); + return 1; +} + +int TreeWnd::dragEnter(ifc_window *sourceWnd) { + // uh... we don't know yet, but we can accept drops in general + hitItem = NULL; + return 1; +} + +int TreeWnd::dragOver(int x, int y, ifc_window *sourceWnd) { + POINT pos={x,y}; + screenToClient(&pos); + TreeItem *prevItem; + + prevItem = hitItem; + hitItem = hitTest(pos); + + // no dropping on yourself! :) + if (hitItem == draggedItem) hitItem = NULL; + + // unselect previous item + if (prevItem != hitItem && prevItem != NULL) { + unhiliteDropItem(prevItem); + repaint(); // commit invalidation of unhilited item so no trouble with scrolling + prevItem->dragLeave(sourceWnd); + } + + + RECT r; + getClientRect(&r); + if (pos.y < r.top + 16) { + if (getScrollY() >= 0) { + scrollToY(MAX(0, getScrollY()-itemHeight)); + } + } else if (pos.y > r.bottom - 16) { + if (getScrollY() < getMaxScrollY()) { + scrollToY(MIN(getMaxScrollY(), getScrollY()+itemHeight)); + } + } + + if (hitItem != NULL) { + // hilight it + if (prevItem != hitItem) { + hiliteDropItem(hitItem); + repaint(); // commit invalidation of hilited so no trouble with scrolling + } + } + + if (hitItem == NULL) return defaultDragOver(x, y, sourceWnd); + + // ask the item if it can really accept such a drop + return hitItem->dragOver(sourceWnd); +} + +int TreeWnd::dragLeave(ifc_window *sourceWnd) { + if (hitItem != NULL) { + unhiliteDropItem(hitItem); + hitItem->dragLeave(sourceWnd); + } + hitItem = NULL; + return 1; +} + +int TreeWnd::dragDrop(ifc_window *sourceWnd, int x, int y) { + int res; + if (hitItem == NULL) return defaultDragDrop(sourceWnd, x, y); + // unhilite the dest + unhiliteDropItem(hitItem); + // the actual drop + res = hitItem->dragDrop(sourceWnd); + if (res) { + onItemRecvDrop(hitItem); + } + hitItem = NULL; + return res; +} + +int TreeWnd::dragComplete(int success) { + int ret; + ASSERT(draggedItem != NULL); + ret = draggedItem->dragComplete(success); + draggedItem = NULL; + return ret; +} + +void TreeItem::setTip(const wchar_t *tip) +{ + tooltip = tip; +} + +const wchar_t *TreeItem::getTip() +{ + return tooltip; +} + +void TreeWnd::hiliteDropItem(TreeItem *item) { + if (item) + item->setHilitedDrop(TRUE); +} + +void TreeWnd::hiliteItem(TreeItem *item) { + if (item) + item->setHilited(TRUE); +} + +void TreeWnd::selectItem(TreeItem *item) { + setCurItem(item, FALSE); +} + +void TreeWnd::selectItemDeferred(TreeItem *item) { + postDeferredCallback(DC_SETITEM, (intptr_t)item); +} + +void TreeWnd::delItemDeferred(TreeItem *item) { + postDeferredCallback(DC_DELITEM, (intptr_t)item); +} + +void TreeWnd::unhiliteItem(TreeItem *item) { + if (item) + item->setHilited(FALSE); +} + +void TreeWnd::unhiliteDropItem(TreeItem *item) { + if (item) + item->setHilitedDrop(FALSE); +} + +void TreeWnd::setCurItem(TreeItem *item, bool expandCollapse, bool editifselected) { + if (curSelected && curSelected != item) { + onDeselectItem(curSelected); + curSelected->setSelected(FALSE); + } + if (item) { + curSelected = item; + onSelectItem(curSelected); + item->setSelected(TRUE, expandCollapse, editifselected); + setSlidersPosition(); + } +} + +// Returns the current tree width in pixels +int TreeWnd::getContentsWidth() { + ensureMetricsValid(); + return maxWidth; +} + +// Returns the current tree height in pixels +int TreeWnd::getContentsHeight() { + ensureMetricsValid(); + return maxHeight; +} + +void TreeWnd::ensureMetricsValid() { + if (metrics_ok) return; + int n; + getMetrics(&n, &maxWidth); + maxWidth += X_SHIFT*2; + maxHeight = n*itemHeight+Y_SHIFT*2; + metrics_ok = TRUE; + setSlidersPosition(); +} + +// Gets notification from sliders +int TreeWnd::childNotify(ifc_window *child, int msg, intptr_t param1, intptr_t param2) { + switch (msg) { + case ChildNotify::EDITWND_ENTER_PRESSED: + if (child == editwnd && editwnd != NULL) { + endEditLabel(editbuffer); + return 1; + } + break; + case ChildNotify::EDITWND_CANCEL_PRESSED: + if (child == editwnd && editwnd != NULL) { + cancelEditLabel(); + return 1; + } + break; + case ChildNotify::EDITWND_DATA_MODIFIED: + if (child == editwnd && editwnd != NULL) { + editUpdate(); + return 1; + } + break; + } + + return TREEWND_PARENT::childNotify(child, msg, param1, param2); +} + +void TreeWnd::editUpdate() { + ASSERT(edited != NULL && editwnd != NULL); + if (!edited || !editwnd) return; + int w = editwnd->getTextLength()+16; + RECT i, r, e; + edited->getCurRect(&i); + getClientRect(&r); + editwnd->getClientRect(&e); + e.left += i.left; + e.right += i.left; + e.top += i.top; + e.bottom += i.top; + e.right = i.left+w; + e.right = MIN<int>(r.right - X_SHIFT, e.right); + editwnd->resize(&e); + editwnd->invalidate(); +} + +TreeItem *TreeWnd::addTreeItem(TreeItem *item, TreeItem *par, int _sorted, int haschildtab) { + + ASSERT(item != NULL); + ASSERTPR(item->getTree() == NULL, "can't transplant TreeItems"); + ASSERTPR(item->getLabel() != NULL, "tree items must have a label to be inserted"); + + item->setSorted(_sorted); + item->setChildTab(haschildtab ? TAB_AUTO : TAB_NO/*&& par != NULL*/); + item->setTree(this); + item->linkTo(par); + + if (par == NULL) + items.addItem(item); + + all_items.addItem(item); + + metrics_ok = FALSE; + + if (redraw) + invalidate(); + + item->onTreeAdd(); + + return item; +} + +int TreeWnd::removeTreeItem(TreeItem *item) { + ASSERT(item != NULL); + ASSERT(item->getTree() == this); + if (item->isSelected()) item->setSelected(FALSE); + if (curSelected == item) curSelected = NULL; +//CUT item->deleteSubitems(); + TreeItem *par = item->getParent(); + if (!par) { // is root item ? + ASSERT(items.haveItem(item)); + items.removeItem(item); + } else { + if (!par->removeSubitem(item)) + return 0; + } + all_items.removeItem(item); + metrics_ok = FALSE; + drawList.removeItem(item); + if (redraw) + invalidate(); + + item->setTree(NULL); + item->onTreeRemove(); + + if (par != NULL) par->onChildItemRemove(item); + + return 1; +} + +void TreeWnd::moveTreeItem(TreeItem *item, TreeItem *newparent) { + ASSERT(item != NULL); + ASSERTPR(item->getTree() == this, "can't move between trees (fucks up Freelist)"); + removeTreeItem(item); + addTreeItem(item, newparent, item->subitems.getAutoSort(), item->childTab); +} + +void TreeWnd::deleteAllItems() { + bool save_redraw = redraw; + setRedraw(FALSE); + + TreeItem *item; + while ((item = enumRootItem(0)) != NULL) + delete item; + + setRedraw(save_redraw); +} + +void TreeWnd::setSorted(bool dosort) { + items.setAutoSort(dosort); +} + +bool TreeWnd::getSorted() { + return items.getAutoSort(); +} + +void TreeWnd::sortTreeItems() { + items.sort(TRUE); + metrics_ok = FALSE; + if (redraw) + invalidate(); +} + +TreeItem *TreeWnd::getSibling(TreeItem *item) { + for (int i=0;i<items.getNumItems();i++) { + if (items[i] == item) { + if (i == items.getNumItems()-1) return NULL; + return items[i+1]; + } + } + return NULL; +} + +void TreeWnd::setAutoCollapse(bool doautocollase) { + autocollapse=doautocollase; +} + +int TreeWnd::onContextMenu(int x, int y) { + POINT pos={x,y}; + screenToClient(&pos); + TreeItem *ti = hitTest(pos); + if (ti != NULL) { + selectItem(ti); + return ti->onContextMenu(x, y); + } + return 0; +} + +int TreeWnd::onDeferredCallback(intptr_t param1, intptr_t param2) { + switch (param1) { + case DC_SETITEM: + setCurItem((TreeItem *)param2, FALSE); + return 1; + case DC_DELITEM: + delete (TreeItem *)param2; + return 1; + case DC_EXPAND: + expandItem((TreeItem *)param2); + return 1; + case DC_COLLAPSE: + collapseItem((TreeItem *)param2); + return 1; + } + return 0; +} + +int TreeWnd::getNumRootItems() { + return items.getNumItems(); +} + +TreeItem *TreeWnd::enumRootItem(int which) { + return items[which]; +} + +void TreeWnd::invalidateMetrics() { + metrics_ok = FALSE; +} + +int TreeWnd::getLinkLine(TreeItem *item, int level) { + + ASSERT(item != NULL); + + int l = 0; + int r = 0; + + if (item->parent == NULL) + return 0; + + TreeItem *cur=item; + + while (cur->getParent() && l < level) { + cur = cur->getParent(); + l++; + } + + if (cur->getSibling()) r |= LINK_BOTTOM | LINK_TOP; + if (level == 0) r |= LINK_RIGHT; + if (level == 0 && cur->getParent()) r |= LINK_TOP; + + return r; + +} + +int TreeWnd::onMouseWheelDown(int clicked, int lines) { + if (!clicked) + scrollToY(MIN(getMaxScrollY(), getScrollY()+itemHeight)); + else + scrollToX(MIN(getMaxScrollX(), getScrollX()+itemHeight)); + return 1; +} + +int TreeWnd::onMouseWheelUp(int clicked, int lines) { + if (!clicked) + scrollToY(MAX(0, getScrollY()-itemHeight)); + else + scrollToX(MAX(0, getScrollX()-itemHeight)); + return 1; +} + +int TreeWnd::expandItem(TreeItem *item) { + ASSERT(item != NULL); + + return item->expand(); +} + +void TreeWnd::expandItemDeferred(TreeItem *item) { + postDeferredCallback(DC_EXPAND, (intptr_t)item); +} + +int TreeWnd::collapseItem(TreeItem *item) { + ASSERT(item != NULL); + + return item->collapse(); +} + +void TreeWnd::collapseItemDeferred(TreeItem *item) { + postDeferredCallback(DC_COLLAPSE, (intptr_t)item); +} + +TreeItem *TreeWnd::getCurItem() { + return curSelected; +} + +int TreeWnd::getItemRect(TreeItem *item, RECT *r) { + ASSERT(item != NULL); + + return item->getCurRect(r); +} + +void TreeWnd::editItemLabel(TreeItem *item) { + + if (edited) { + edited->setEdition(FALSE); + edited->invalidate(); + } + + + ASSERT(item != NULL); + if (item == NULL) return; + + if (item->onBeginLabelEdit()) return; + item->setEdition(TRUE); + edited = item; + + editwnd = new EditWnd(); + editwnd->setModal(TRUE); + editwnd->setAutoSelect(TRUE); + editwnd->setStartHidden(TRUE); + editwnd->init(getOsModuleHandle(), getOsWindowHandle()); + editwnd->setParent(this); + RECT r; + edited->getCurRect(&r); + RECT cr; + getClientRect(&cr); + r.right = cr.right; + if (r.bottom - r.top < 24) r.bottom = r.top + 24; + editwnd->resize(&r); + wcsncpy(editbuffer, edited->getLabel(), 256); + editwnd->setBuffer(editbuffer, 255); + editUpdate(); + editwnd->setVisible(TRUE); +} + +void TreeWnd::endEditLabel(const wchar_t *newlabel) +{ + editwnd = NULL; // editwnd self destructs + if (edited->onEndLabelEdit(newlabel)) + edited->setLabel(newlabel); + edited->setEdition(FALSE); + edited->invalidate(); + onLabelChange(edited); + edited = NULL; + invalidateMetrics(); + setSlidersPosition(); +} + +void TreeWnd::cancelEditLabel(int destroyit) { + ASSERT(edited != NULL); + if (!edited) return; + + if (destroyit) + delete editwnd; + + editwnd = NULL; // editwnd self destructs (update> except if destroyit for cancelling from treewnd) + edited->setEdition(FALSE); + edited->invalidate(); + edited = NULL; +} + +void TreeWnd::setAutoEdit(int ae) { + autoedit = ae; +} + +int TreeWnd::getAutoEdit() { + return autoedit; +} + +TreeItem *TreeWnd::getByLabel(TreeItem *item, const wchar_t *name) +{ + TreeItem *ti; + // handle root-level searching + if (item == NULL) { + int n = getNumRootItems(); + for (int i = 0; i < n; i++) { + ti = enumRootItem(i); + if (!wcscmp(name, ti->getLabel())) return ti; + ti = getByLabel(ti, name); + if (ti) return ti; + } + return NULL; + } + + // check the given item + if (!wcscmp(name, item->getLabel())) return item; + + // depth first search + ti = item->getChild(); + if (ti != NULL) { + ti = getByLabel(ti, name); + if (ti != NULL) return ti; + } + + // recursively check siblings + ti = item->getSibling(); + if (ti != NULL) ti = getByLabel(ti, name); + + return ti; +} + +int TreeWnd::onGetFocus() { + int r = TREEWND_PARENT::onGetFocus(); + +#if 0 +DebugString("yay got focus"); + TreeItem *ti = getCurItem(); + if (ti != NULL) { + ti->setSelected(FALSE); + selectItemDeferred(ti); + } +#endif + + return r; +} + +int TreeWnd::onKillFocus() { + + TREEWND_PARENT::onKillFocus(); + mousedown_item=NULL; +/* if (edited) + cancelEditLabel();*/ +#if 0 +DebugString("no mo focus"); +#endif + + return 1; +} + +int TreeWnd::onChar(unsigned int c) +{ + int r = 0; + + if (c == 27) { + if (edited) + cancelEditLabel(1); + } + + if (curSelected != NULL && (r = curSelected->onChar(c)) != 0) return r; + + wchar_t b = TOUPPERW(c); + if (b >= 'A' && b <= 'Z') + { + jumpToNext(b); + r = 1; + } + + return r ? r : TREEWND_PARENT::onChar(c); +} + +int TreeWnd::getNumVisibleChildItems(TreeItem *c) { + int nb=0; + for(int i=0;i<c->getNumChildren();i++) { + TreeItem *t=c->getNthChild(i); + if(t->hasSubItems() && t->isExpanded()) + nb+=getNumVisibleChildItems(t); + nb++; + } + return nb; +} + +int TreeWnd::getNumVisibleItems() { + int nb=0; + for(int i=0;i<items.getNumItems();i++) { + TreeItem *t=items.enumItem(i); + if(t->hasSubItems() && t->isExpanded()) + nb+=getNumVisibleChildItems(t); + nb++; + } + return nb; +} + +TreeItem *TreeWnd::enumVisibleChildItems(TreeItem *c, int n) { + int nb=0; + for(int i=0;i<c->getNumChildren();i++) { + TreeItem *t=c->getNthChild(i); + if(nb==n) return t; + if(t->hasSubItems() && t->isExpanded()) { + TreeItem *t2=enumVisibleChildItems(t, n-nb-1); + if(t2) return t2; + nb+=getNumVisibleChildItems(t); + } + nb++; + } + return NULL; +} + +TreeItem *TreeWnd::enumVisibleItems(int n) { + int nb=0; + for(int i=0;i<items.getNumItems();i++) { + TreeItem *t=items.enumItem(i); + if(nb==n) return t; + if(t->hasSubItems() && t->isExpanded()) { + TreeItem *t2=enumVisibleChildItems(t, n-nb-1); + if(t2) return t2; + nb+=getNumVisibleChildItems(t); + } + nb++; + } + return NULL; +} + +int TreeWnd::findChildItem(TreeItem *c, TreeItem *i, int *nb) { + for(int j=0;j<c->getNumChildren();j++) { + TreeItem *t=c->getNthChild(j); (*nb)++; + if (t == i) return *nb; + if(t->hasSubItems() && t->isExpanded()) { + int n = findChildItem(t, i, nb); + if (n != -1) return *nb; + } + } + return -1; +} + +int TreeWnd::findItem(TreeItem *i) { + int nb=-1; + for(int j=0;j<items.getNumItems();j++) { + TreeItem *t=items.enumItem(j); nb++; + if (t == i) return nb; + if(t->hasSubItems() && t->isExpanded()) { + int n = findChildItem(t, i, &nb); + if (n != -1) return nb; + } + } + return -1; +} + + +TreeItem *TreeWnd::enumAllItems(int n) { + return all_items[n]; +} + +int TreeWnd::onKeyDown(int keycode) +{ + switch(keycode) + { + case 113: { + TreeItem *item = getCurItem(); + if (item) + item->editLabel(); + return 1; + } + case STDKEY_UP: { + TreeItem *t=getCurItem(); + int l=getNumVisibleItems(); + if (t == NULL) { + if (l > 0) setCurItem(enumVisibleItems(getNumVisibleItems()-1), FALSE, FALSE); + } else { + for(int i=0;i<l;i++) + if(enumVisibleItems(i)==t) { + if(i-1>=0) { + TreeItem *t2=enumVisibleItems(i-1); + if(t2) setCurItem(t2,FALSE,FALSE); + } + } + } + return 1; + } + case STDKEY_DOWN: { + TreeItem *t=getCurItem(); + int l=getNumVisibleItems(); + if (t == NULL) { + if (l > 0) setCurItem(enumVisibleItems(0), FALSE, FALSE); + } else { + for(int i=0;i<l;i++) + if(enumVisibleItems(i)==t) { + TreeItem *t2=enumVisibleItems(i+1); + if(t2) setCurItem(t2,FALSE,FALSE); + } + } + return 1; + } + case VK_PRIOR: { + TreeItem *t=getCurItem(); + int l=getNumVisibleItems(); + for(int i=0;i<l;i++) + if(enumVisibleItems(i)==t) { + int a=MAX(i-5,0); + TreeItem *t2=enumVisibleItems(a); + if(t2) setCurItem(t2,FALSE,FALSE); + } + return 1; + } + case VK_NEXT: { + TreeItem *t=getCurItem(); + int l=getNumVisibleItems(); + for(int i=0;i<l;i++) + if(enumVisibleItems(i)==t) { + int a=MIN(i+5,l-1); + TreeItem *t2=enumVisibleItems(a); + if(t2) setCurItem(t2,FALSE,FALSE); + } + return 1; + } + case STDKEY_HOME: { + TreeItem *t=enumVisibleItems(0); + if(t) setCurItem(t,FALSE,FALSE); + return 1; + } + case STDKEY_END: { + TreeItem *t=enumVisibleItems(getNumVisibleItems()-1); + if(t) setCurItem(t,FALSE,FALSE); + return 1; + } + case STDKEY_LEFT: { + TreeItem *t=getCurItem(); + if(t) t->collapse(); + return 1; + } + case STDKEY_RIGHT: { + TreeItem *t=getCurItem(); + if(t) t->expand(); + return 1; + } + } + + return TREEWND_PARENT::onKeyDown(keycode); +} + +void TreeWnd::jumpToNext(wchar_t c) { + firstFound=FALSE; + if (jumpToNextSubItems(&items, c)) return; + firstFound=TRUE; + jumpToNextSubItems(&items, c); +} + +int TreeWnd::jumpToNextSubItems(TreeItemList *list, wchar_t c) { + + for (int i=0;i<list->getNumItems();i++) { + + TreeItem *nextitem = list->enumItem(i); + const wchar_t *l = nextitem->getLabel(); + wchar_t b = l ? TOUPPERW(*l) : 0; + if (b == c && firstFound) + { + selectItem(nextitem); + nextitem->ensureVisible(); + return 1; + } + + if (nextitem->isSelected()) firstFound = TRUE; + + if (nextitem->isExpanded()) + if (jumpToNextSubItems(&nextitem->subitems, c)) return 1; + } + return 0; +} + +void TreeWnd::ensureItemVisible(TreeItem *item) { + ASSERT(item != NULL); + + // walk the parent tree to make sure item is visible + for (TreeItem *cur = item->getParent(); cur; cur = cur->getParent()) { + if (cur->isCollapsed()) cur->expand(); + } + + RECT r; + RECT c; + item->getCurRect(&r); + getClientRect(&c); + if (r.top < c.top || r.bottom > c.bottom) { + if (r.top + (c.bottom - c.top) <= getContentsHeight()) + scrollToY(r.top); + else { + scrollToY(getContentsHeight()-(c.bottom-c.top)); + } + } +} + +void TreeWnd::setHilitedColor(const wchar_t *colorname) { + // we have to store it in a String because SkinColor does not make a copy + hilitedColorName = colorname; + hilitedColor = hilitedColorName; +} + +ARGB32 TreeWnd::getHilitedColor() { + return hilitedColor; +} + +int TreeWnd::compareItem(TreeItem *p1, TreeItem *p2) +{ + int r = wcscmp(p1->getLabel(), p2->getLabel()); + if (r == 0) return CMP3(p1, p2); + return r; +} + +int TreeWnd::setFontSize(int newsize) +{ + TREEWND_PARENT::setFontSize(newsize); + if (newsize >= 0) textsize = newsize; + TextInfoCanvas c(this); + Wasabi::FontInfo fontInfo; + fontInfo.pointSize = getFontSize(); + itemHeight = c.getTextHeight(&fontInfo); + redraw = 1; + metrics_ok = 0; + invalidate(); + return 1; +} + +int TreeWnd::getFontSize() { +#ifndef WASABINOMAINAPI + return textsize + api->metrics_getDelta(); +#else + //MULTIAPI-FIXME: not handling delta + return textsize; +#endif +} + + +void TreeWnd::onSelectItem(TreeItem *i) { + Accessible *a = getAccessibleObject(); + if (a != NULL) + a->onGetFocus(findItem(i)); +} + +void TreeWnd::onDeselectItem(TreeItem *i) { +} + + +//////////////////////////////////////////////////////////////////////////////////// +// TreeItem +//////////////////////////////////////////////////////////////////////////////////// + +TreeItem::TreeItem(const wchar_t *label) { + parent=NULL; + MEMZERO(&curRect, sizeof(RECT)); + childTab = TAB_AUTO; + tree = NULL; + expandStatus = STATUS_COLLAPSED; + icon = NULL; + _z = 0; + + if (label != NULL) + setLabel(label); + + selected = FALSE; + hilitedDrop = FALSE; + hilited = FALSE; + being_edited = FALSE; + + setSorted(TRUE); +} + +TreeItem::~TreeItem() { + // the subitem will call parent tree which will remove item from our list + deleteSubitems(); + + // remove from parent tree + if (tree) tree->removeTreeItem(this); + + delete icon; +} + +void TreeItem::deleteSubitems() { + while (subitems.getNumItems() > 0) { + delete subitems.enumItem(0); + } +} + +void TreeItem::setSorted(int issorted) { + subitems.setAutoSort(!!issorted); +} + +void TreeItem::setChildTab(int haschildtab) { + childTab = haschildtab; +} + +void TreeItem::linkTo(TreeItem *par) { + parent = par; + + if (par == NULL) return; + + par->addSubItem(this); +} + +void TreeItem::addSubItem(TreeItem *item) { + subitems.addItem(item); +} + +int TreeItem::removeSubitem(TreeItem *item) { + if (subitems.searchItem(item) == -1) return 0; + subitems.removeItem(item); + if (tree->redraw) + tree->invalidate(); + return 1; +} + +TreeItem *TreeItem::getChild() { + return subitems.getFirst(); +} + +TreeItem *TreeItem::getChildSibling(TreeItem *item) { // locate item in children and return its sibling + for (int i=0;i<subitems.getNumItems();i++) { + if (subitems.enumItem(i) == item) { + if (i == subitems.getNumItems()-1) return NULL; + return subitems.enumItem(i+1); + } + } +return NULL; +} + +TreeItem *TreeItem::getSibling() { // returns next item + if (!parent) + return tree->getSibling(this); + else + return parent->getChildSibling(this); +} + +void TreeItem::setTree(TreeWnd *newtree) { + tree = newtree; + // recursively reset tree for children, if any + for (int i = 0; ; i++) { + TreeItem *item = getNthChild(i); + if (item == NULL) break; + item->setTree(tree); + } +} + +void TreeItem::ensureVisible() +{ + if (tree) tree->ensureItemVisible(this); +} + +const wchar_t *TreeItem::getLabel() +{ + return label; +} + +void TreeItem::setLabel(const wchar_t *newlabel) +{ + label = newlabel; + if (newlabel) { + if (tree) + tree->invalidateMetrics(); + invalidate(); + } +} + +int TreeItem::customDraw(Canvas *canvas, const POINT &pt, int txtHeight, int indentation, const RECT &clientRect, const Wasabi::FontInfo *fontInfo) +{ + if (being_edited) return 0; + + SkinBitmap *icon = getIcon(); + + int cw = clientRect.right - clientRect.left; + + int iconw = MIN(icon ? icon->getWidth() : 0, cw); + + if (isSelected() || isHilitedDrop()) + { + RECT r; + r.left = pt.x; + r.top = pt.y; + //r.right = r.left + canvas->getTextWidth(label)+2+(icon ? txtHeight : 0); + r.right = r.left + canvas->getTextWidth(label, fontInfo)+2+iconw; + r.bottom = r.top + txtHeight; + canvas->fillRect(&r, isHilitedDrop() ? drophilitecolor : selectedcolor); + } + + if (isHilited()) + { + RECT r; + r.left = pt.x; + r.top = pt.y; + //r.right = r.left + canvas->getTextWidth(label)+2+(icon ? txtHeight : 0); + r.right = r.left + canvas->getTextWidth(label, fontInfo)+2+iconw; + r.bottom = r.top + txtHeight; + canvas->drawRect(&r, 1, tree->getHilitedColor()); + } + + POINT d=pt; + + if (icon) { + RECT i; + i.left = pt.x+1; + i.right = i.left + iconw; +// i.top = pt.y+1; + int lh = MIN(icon->getHeight(), txtHeight); + i.top = pt.y + (txtHeight - lh) / 2; +// i.bottom = i.top + txtHeight-2; + i.bottom = i.top + lh; + icon->stretchToRectAlpha(canvas, &i); + //d.x += txtHeight; + d.x += icon->getWidth(); + } + + canvas->textOut(d.x+1, d.y, label, fontInfo); + + return canvas->getTextWidth(label, fontInfo)+2+iconw; +} + +int TreeItem::getItemWidth(int txtHeight, int indentation) { + SkinBitmap *icon = getIcon(); + if (!label) return (icon ? txtHeight : 0); + TextInfoCanvas c(tree); + Wasabi::FontInfo fontInfo; + fontInfo.pointSize = getTree()->getFontSize(); + int width = c.getTextWidth(label, &fontInfo)+2; + width += (icon ? txtHeight : 0); + return width; +} + +int TreeItem::getNumChildren() { + return subitems.getNumItems(); +} + +TreeItem *TreeItem::getNthChild(int nth) { + if (nth >= subitems.getNumItems()) return NULL; + return subitems.enumItem(nth); +} + +void TreeItem::setCurRect(int x1, int y1, int x2, int y2, int z) { + curRect.left = x1; + curRect.right = x2; + curRect.top = y1; + curRect.bottom = y2; + _z = z; +} + +int TreeItem::getIndent() { + return _z; +} + +bool TreeItem::hasSubItems() { + return subitems.getNumItems()>0; +} + +bool TreeItem::needTab() { + return (childTab == TAB_YES || (childTab == TAB_AUTO && hasSubItems())); +} + +bool TreeItem::isExpanded() { + if (!hasSubItems()) return FALSE; + return expandStatus == STATUS_EXPANDED; +} + +bool TreeItem::isCollapsed() { + if (!hasSubItems()) return TRUE; + return expandStatus == STATUS_COLLAPSED; +} + +int TreeItem::getCurRect(RECT *r) { + r->left = curRect.left-tree->getScrollX(); + r->top = curRect.top-tree->getScrollY(); + r->right = curRect.right-tree->getScrollX(); + r->bottom = curRect.bottom-tree->getScrollY(); + RECT c; + tree->getClientRect(&c); + r->top += c.top; + r->bottom += c.top; + r->left += c.left; + r->right += c.left; + return 1; +} + +TreeWnd *TreeItem::getTree() const { + return tree; +} + +void TreeItem::setSelected(bool isSelected, bool expandCollapse, bool editifselected) { + bool wasselected = selected; + selected = !!isSelected; + + if (selected != wasselected) { + invalidate(); + tree->repaint(); + if (selected) { + onSelect(); + ASSERT(tree != NULL); + tree->onItemSelected(this); + } else { + onDeselect(); + ASSERT(tree != NULL); + tree->onItemDeselected(this); + } + } else { + if (selected && editifselected) { + editLabel(); + } + } + + if (expandCollapse) { + if (isCollapsed()) + expand(); + else + collapse(); + } +} + +void TreeItem::invalidate() { + if (tree) { + RECT r; + getCurRect(&r); + tree->invalidateRect(&r); + } +} + +bool TreeItem::isSelected() { + return selected; +} + +int TreeItem::collapse() { + int old = expandStatus; + + if (hasSubItems()) { + if (expandStatus == STATUS_COLLAPSED) + return 0; + expandStatus = STATUS_COLLAPSED; + RECT c; + tree->getClientRect(&c); + RECT r; + getCurRect(&r); + r.bottom = c.bottom; + r.left = c.left; + r.right = c.right; + if (tree) { + tree->invalidateRect(&r); + tree->invalidateMetrics(); + } + } + + onCollapse(); + + return old != expandStatus; +} + +int TreeItem::expand() { + int old = expandStatus; + + if (hasSubItems()) { + if (expandStatus == STATUS_EXPANDED) + return 0; + expandStatus = STATUS_EXPANDED; + RECT c; + tree->getClientRect(&c); + RECT r; + getCurRect(&r); + r.bottom = c.bottom; + r.left = c.left; + r.right = c.right; + if (tree) { + tree->invalidateRect(&r); + tree->invalidateMetrics(); + } + } + + onExpand(); + + return old != expandStatus; +} + +int TreeItem::onContextMenu(int x, int y) { + return 0; +} + +void TreeItem::setHilitedDrop(bool ishilitedDrop) { + bool washilighted = hilitedDrop; + hilitedDrop = !!ishilitedDrop; + + if (washilighted != hilitedDrop) + invalidate(); +} + +void TreeItem::setHilited(bool ishilited) { + bool washilighted = hilited; + hilited = !!ishilited; + + if (washilighted != hilited) + invalidate(); +} + +TreeItem *TreeItem::getParent() { + return parent; +} + +bool TreeItem::isHilitedDrop() { + return hilitedDrop; +} + +bool TreeItem::isHilited() { + return hilited; +} + +void TreeItem::setIcon(SkinBitmap *newicon) { + if (icon) { + delete icon; + icon = NULL; + } + icon = newicon; + invalidate(); +} + +SkinBitmap *TreeItem::getIcon() { + return icon; +} + +void TreeItem::sortItems() { + subitems.sort(); +} + +void TreeItem::editLabel() { + if (!tree) return; + tree->editItemLabel(this); +} + +int TreeItem::onBeginLabelEdit() { + return 1; // disable editing by default +} + +int TreeItem::onEndLabelEdit(const wchar_t *newlabel) +{ + return 1; // accept new label by default +} + +void TreeItem::setEdition(bool isedited) { + being_edited = !!isedited; + invalidate(); +} + +bool TreeItem::getEdition() { + return being_edited; +} + +bool TreeItem::isSorted() +{ + return subitems.getAutoSort(); +} + diff --git a/Src/Wasabi/api/wnd/wndclass/treewnd.h b/Src/Wasabi/api/wnd/wndclass/treewnd.h new file mode 100644 index 00000000..454b9349 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/treewnd.h @@ -0,0 +1,624 @@ +#ifndef _TREEWND_H +#define _TREEWND_H + +// BU: lots of changes +// - all items must be deletable, and will be deleted on destructor +// - root items list not allocated w/ new +// - items set sorting within their PtrListSorted instead of manually calling it +// - setting an item to auto-sort does *not* make subitems autosort too + +#include <api/wnd/wndclass/scbkgwnd.h> +#include <bfc/ptrlist.h> +#include <api/wnd/wndclass/editwnd.h> +#include <bfc/common.h> +#include <tataki/color/skinclr.h> + +#define TREEWND_PARENT ScrlBkgWnd + +#define STATUS_EXPANDED 0 +#define STATUS_COLLAPSED 1 + +#define HITTEST_BEFORE 0 +#define HITTEST_IN 1 +#define HITTEST_AFTER 2 + +#define LINK_RIGHT 1 +#define LINK_TOP 2 +#define LINK_BOTTOM 4 + +#define TAB_NO FALSE +#define TAB_YES TRUE +#define TAB_AUTO 2 + +#define WM_SETITEMDEFERRED WM_USER+6546 + +#define DC_SETITEM 10 +#define DC_DELITEM 20 +#define DC_EXPAND 30 +#define DC_COLLAPSE 40 + +// Forward references + +class TreeItemList; +class TreeItem; +class TreeWnd; + +class FontSize; + +// classes & structs +class CompareTreeItem { +public: + static int compareItem(TreeItem *p1, TreeItem *p2); +}; + +class TreeItemList : public PtrListQuickSorted<TreeItem, CompareTreeItem> { }; + +class TreeItem +{ +friend class TreeWnd; +public: + TreeItem(const wchar_t *label=NULL); + virtual ~TreeItem(); + + virtual SkinBitmap *getIcon(); + virtual void setIcon(SkinBitmap *newicon); + + virtual void onTreeAdd() {} + virtual void onTreeRemove() {} + virtual void onChildItemRemove(TreeItem *item) {} + // override this to keep from being selected + virtual int isHitTestable() { return 1; } + virtual void onSelect() {} + virtual void onDeselect() {} + virtual int onLeftDoubleClick() { return 0; } + virtual int onRightDoubleClick() { return 0; } + virtual int onContextMenu(int x, int y); + virtual int onChar(UINT key) { return 0; } // return 1 if you eat the key + + // these are called after the expand/collapse happens + virtual void onExpand() {} + virtual void onCollapse() {} + + virtual int onBeginLabelEdit(); + virtual int onEndLabelEdit(const wchar_t *newlabel); + + virtual void setLabel(const wchar_t *label); + virtual const wchar_t *getLabel(); + + void setTip(const wchar_t *tip); + const wchar_t *getTip(); + + // override to draw by yourself. Return the width of what you've drawn + virtual int customDraw(Canvas *canvas, const POINT &pt, int defaultTxtHeight, int indentation, const RECT &clientRect, const Wasabi::FontInfo *fontInfo); + + // return 0 to refuse being dragged + // else return 1 and install the droptype and dropitem + // also, write suggested title into suggestedTitle if any + virtual int onBeginDrag(wchar_t *suggestedTitle) { return 0; } + + virtual int dragOver(ifc_window *sourceWnd) { return 0; } + virtual int dragLeave(ifc_window *sourceWnd) { return 0; } + virtual int dragDrop(ifc_window *sourceWnd) { return 0; } + + virtual int dragComplete(int success) { return 0; } + + void ensureVisible(); + + TreeItem *getNthChild(int nth); // enumerates children (zero based) + TreeItem *getChild(); + TreeItem *getChildSibling(TreeItem *item); + TreeItem *getSibling(); + TreeItem *getParent(); + + void editLabel(); + + int getNumChildren(); + bool hasSubItems(); + + void setSorted(int issorted); + void setChildTab(int haschildtab); + bool isSorted(); + + bool isCollapsed(); + bool isExpanded(); + + void invalidate(); + bool isSelected(); + bool isHilited(); + void setHilited(bool ishilited); + + int collapse(); + int expand(); + + int getCurRect(RECT *r); + + void setCurrent(bool tf); + + TreeWnd *getTree() const; + +protected: + + bool isHilitedDrop(); + void setHilitedDrop(bool ishilitedDrop); + + void linkTo(TreeItem *linkto); +// void childDeleted(TreeItem *child); + void setTree(TreeWnd *newtree); + void addSubItem(TreeItem *item); + void setCurRect(int x1, int y1, int x2, int y2, int z); + int getIndent(); + + bool needTab(); + void sortItems(); // sorts the children of this item + void setEdition(bool isedited); + bool getEdition(); + +private: + void setSelected(bool isselected, bool expandCollapse=false, bool editifselected=false); + // this really calls delete on the subitems + void deleteSubitems(); + + int removeSubitem(TreeItem *item); + + int getItemWidth(int txtHeight, int indentation); + + StringW label; + class TreeItem *parent; + TreeItemList subitems; // children + RECT curRect; + int childTab; + TreeWnd *tree; + int expandStatus; + SkinBitmap *icon; + int _z; + StringW tooltip; // if empty, falls back to livetip + + bool selected:1; + bool hilitedDrop:1; + bool hilited:1; + bool being_edited:1; +}; + + +/** + + + @short Tree-like view with leaf items. + @ver 1.0 + @author Nullsoft + @see TreeItem +*/ +class TreeWnd : public TREEWND_PARENT { + +friend class TreeItem; + +public: + + /** + Sets up the default values for the TreeWnd. These defaults are + auto collapse enabled and sets the TreeWnd bitmaps to the default Wasabi + values. + */ + TreeWnd(); + + /** + Deletes all the root items (including subitems). + */ + virtual ~TreeWnd(); + + /** + Event is triggered when the button is about to be initialized. + Override this event to implement your own behavior. + + @ret 1 + */ + virtual int onInit(); + + /** + Paints the bitmap on canvas according + to current options (centering, tiling, stretching, title). + + @ret 0 for failure, 1 for success + @param canvas The canvas on which to paint. + */ + virtual int onPaint(Canvas *canvas); + + /** + Notify a child window via a generic message system. + + @see addChild() + @ret + @param child A pointer to the child window which will receive the notify. + @param msg The message you want to send to the child. + @param p1 A user parameter. + @param p2 A user parameter. + */ + virtual int childNotify(ifc_window *child, int msg, intptr_t param1=0, intptr_t param2=0); + + /** + Event triggered when the left mouse + button is pressed over the TreeWnd. + + Override this to implement your own behavior. + + Default behavior is to stop editing a TreeItem label + (if editing was occuring). Also will cause a collapse + or expansion of the subitems if an item was previously + selected. + + @ret 1, If you handle the event. + @param x The X coordinate of the mouse. + @param y The Y coordinate of the mouse. + */ + virtual int onLeftButtonDown(int x, int y); + + /** + Event is triggered when the left mouse button + is released from a previously pressed state. + + Override this to implement your own behavior. + + @ret 1, If you handle the event. + @param x The X coordinate of the mouse. + @param y The Y coordinate of the mouse. + */ + virtual int onLeftButtonUp(int x, int y); + + /** + Event is triggered when the right mouse button + is released from a previously pressed state. + + Override this to implement your own behavior. + + @ret 1, If you handle the event. + @param x The X coordinate of the mouse. + @param y The Y coordinate of the mouse. + */ + virtual int onRightButtonUp(int x, int y); + + /** + Event is triggered when the mouse is moved + over the TreeWnd. + + Override this to implement your own behavior. + + Default is to handle drops (drag and drop). + + @ret 1, If you handle the event. + @param x The X coordinate of the mouse. + @param y The Y coordinate of the mouse. + */ + virtual int onMouseMove(int x, int y); + + /** + Do we want the context command menu to pop-up + on right clicks? + + Default is no. + + @see ContextCmdI + @ret 0, AutoContextMenu off; 1, AutoContextMenu on; + */ + virtual int wantAutoContextMenu() { return 0; } + + /** + Event is triggered when the left mouse button + is double clicked and the cursor is over the + TreeWnd. + + Default is to check if the doubleclick + happened over an item, if it did, it calls + the item's handler of this event. + + @ret 1, if you handle the event. + @param x The X coordinate of the mouse. + @param y The Y coordinate of the mouse. + */ + virtual int onLeftButtonDblClk(int x, int y); + + /** + Event is triggered when the right mouse button + is double clicked and the cursor is over the + TreeWnd. + + Default is to check if the doubleclick + happened over an item, if it did, it calls + the item's handler of this event. + + @ret 1, If you handle the event. + @param x The X coordinate of the mouse. + @param y The y coordinate of the mouse. + */ + virtual int onRightButtonDblClk(int x, int y); + + /** + Event is triggered when the mouse wheel + is rolled up. + + Override this to implement your own behavior. + + Default is to scroll vertically as required. + When the wheel is clicked and rolled, the + TreeWnd is scrolled horizontally. + + @ret 1, If you handle the event. + @param clicked The pushed state of the mouse wheel. + @param lines The number of lines to scroll (or columns if horizontally scrolling). + */ + virtual int onMouseWheelUp(int clicked, int lines); + + /** + Event is triggered when the mouse wheel + is rolled down. + + Override this to implement your own behavior. + + Default is to scroll vertically as required. + When the wheel is clicked and rolled, the + TreeWnd is scrolled horizontally. + + @ret 1, If you handle the event. + @param clicked The pushed state of the mouse wheel. + @param lines The number of lines to scroll (or columns if horizontally scrolling). + */ + virtual int onMouseWheelDown(int clicked, int lines); + + /** + */ + virtual void timerCallback(int c); + + /** + Event is triggered when the right click occurs over + the TreeWnd, but not on a TreeItem. + + Override this to implement your own behavior. + + @ret 1, If you handle the event. + @param x The X coordinate of the mouse. + @param y The Y coordinate of the mouse. + */ + virtual int onContextMenu(int x, int y); + + // override and return 1 to abort calling context menu on item + virtual int onPreItemContextMenu(TreeItem *item, int x, int y) { return 0; } + // override to catch when item context menu complete + virtual void onPostItemContextMenu(TreeItem *item, int x, int y, int retval) { } + + /** + Event is triggered when a scheduled deferred callback + occurs. + + Override this to implement your own behavior. + + @ret 1, If you handle this event; 0, If you do not handle this event; + @param param1 Generic user paramater 1. + @param param2 Generic user paramater 2. + */ + virtual int onDeferredCallback(intptr_t param1, intptr_t param2); + + /** + Event is triggered when a key is pressed + and the TreeWnd has focus. + + Override this to implement your own behavior. + + @ret 1, If you handle the event. + @param c The key that was pressed. + */ + virtual int onChar(unsigned int c); + + /** + Event is triggered when a key is pressed + and the TreeWnd has focus. + + This method handles extended keys. + + @ret 1, If you handle the event. + */ + virtual int onKeyDown(int keycode); + + /** + + */ + virtual void jumpToNext(wchar_t c); + + /** + Verifies if the item received is in the + viewable area of the TreeWnd. If not, it + will make it visible by scrolling to the + appropriate position. + + @param item A pointer to the item to verify. + */ + void ensureItemVisible(TreeItem *item); + + // don't need to override this: just calls thru to the treeitem + virtual int onBeginDrag(TreeItem *treeitem); + + virtual int dragEnter(ifc_window *sourceWnd); + virtual int dragOver(int x, int y, ifc_window *sourceWnd); + virtual int dragLeave(ifc_window *sourceWnd); + virtual int dragDrop(ifc_window *sourceWnd, int x, int y); + + virtual int dragComplete(int success); + + int wantFocus() { return 1; } + + // override this if you want to control the item sort order + virtual int compareItem(TreeItem *p1, TreeItem *p2); + +protected: + // these will be called if the pointer is not over a treeitem + virtual int defaultDragOver(int x, int y, ifc_window *sourceWnd) { return 0; } + virtual int defaultDragDrop(ifc_window *sourceWnd, int x, int y) { return 0; } + + // called with item that received a drop + virtual void onItemRecvDrop(TreeItem *item) {} + + virtual void onLabelChange(TreeItem *item) {} + + virtual void onItemSelected(TreeItem *item) {} + virtual void onItemDeselected(TreeItem *item) {} + + virtual int onGetFocus(); + virtual int onKillFocus(); + +public: + + virtual int getContentsWidth(); + virtual int getContentsHeight(); + + void setRedraw(bool r); + + TreeItem *addTreeItem(TreeItem *item, TreeItem *par=NULL, int sorted=TRUE, int haschildtab=FALSE); + + // just removes a TreeItem from the tree, doesn't delete it... this is for + // ~TreeItem to call only + int removeTreeItem(TreeItem *item); + + void moveTreeItem(TreeItem *item, TreeItem *newparent); + + void deleteAllItems(); + + int expandItem(TreeItem *item); + void expandItemDeferred(TreeItem *item); + int collapseItem(TreeItem *item); + void collapseItemDeferred(TreeItem *item); + + void selectItem(TreeItem *item); // selects. + void selectItemDeferred(TreeItem *item);// selects. posted. + void delItemDeferred(TreeItem *item); + void hiliteItem(TreeItem *item); + void unhiliteItem(TreeItem *item); + void setHilitedColor(const wchar_t *colorname); + ARGB32 getHilitedColor(); + + TreeItem *getCurItem(); + + TreeItem *hitTest(POINT pos); + TreeItem *hitTest(int x, int y); + + void editItemLabel(TreeItem *item); + void cancelEditLabel(int destroyit=0); + void setAutoEdit(int ae); + int getAutoEdit(); + // use a NULL item to search all items. returns first item found + TreeItem *getByLabel(TreeItem *item, const wchar_t *name); + + int getItemRect(TreeItem *item, RECT *r); + + int ownerDraw(); + + int getNumRootItems(); + TreeItem *enumRootItem(int which); + + void setSorted(bool dosort); + bool getSorted(); + + void sortTreeItems(); + + TreeItem *getSibling(TreeItem *item); + + TreeItem *getItemFromPoint(POINT *pt); + + void setAutoCollapse(bool doautocollapse); + + virtual int setFontSize(int newsize); + int getFontSize(); + + int getNumVisibleChildItems(TreeItem *c); + int getNumVisibleItems(); + TreeItem *enumVisibleItems(int n); + TreeItem *enumVisibleChildItems(TreeItem *c, int n); + int findItem(TreeItem *i); // reverse + int findChildItem(TreeItem *c, TreeItem *i, int *n); + + TreeItem *enumAllItems(int n); // unsorted + + void onSelectItem(TreeItem *i); + void onDeselectItem(TreeItem *i); + +protected: + void hiliteDropItem(TreeItem *item); + void unhiliteDropItem(TreeItem *item); + void invalidateMetrics(); + +private: + TreeItemList items; // root-level stuff + + PtrList<TreeItem> all_items; // unsorted + + TreeItem *curSelected; + + BltCanvas *dCanvas; + + void drawItems(Canvas *c, const Wasabi::FontInfo *fontInfo); + void setCurItem(TreeItem *item, bool expandCollapse=true, bool editifselected=false); + void countSubItems(PtrList<TreeItem> &drawlist, TreeItemList *list, int indent, int *c, int *m, int z); + void getMetrics(int *numItemsShow, int *maxWidth); + void ensureMetricsValid(); + int getLinkLine(TreeItem *item, int level); + void endEditLabel(const wchar_t *newlabel); + void editUpdate(); + int jumpToNextSubItems(TreeItemList *list, wchar_t c); + + int itemHeight; + + AutoSkinBitmap tabClosed, tabOpen; + AutoSkinBitmap linkTopRight, linkTopBottom, linkTopRightBottom; + AutoSkinBitmap linkTabTopRight, linkTabTopBottom, linkTabTopRightBottom; + + TreeItem *firstItemVisible; + TreeItem *lastItemVisible; + + TreeItem *mousedown_item, *prevbdownitem; + POINT mousedown_anchor; + bool mousedown_dragdone; + TreeItem *hitItem, // the dest item + *draggedItem; // the source item + + int inHitTest; + + bool metrics_ok; + int maxWidth; + int maxHeight; + + StringW defaultTip; + + const wchar_t *getLiveTip(); + void setLiveTip(const wchar_t *tip); + TreeItem *tipitem; + + bool redraw; + + PtrList<TreeItem> drawList; + TreeItem *edited; + + EditWnd *editwnd; + wchar_t editbuffer[256]; + + int deleteItems; + bool firstFound; + + TreeItem *currentItem; + StringW hilitedColorName; + SkinColor hilitedColor; + int autoedit; + int autocollapse; + int textsize; + StringW oldtip; + StringW accValue; +}; + +template<class T> class TreeItemParam : public TreeItem { +public: + TreeItemParam(T _param, const wchar_t *label=NULL) : TreeItem(label) { param = _param; } + + T getParam() { return param; } + operator T() { return getParam(); } + +private: + T param; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/typesheet.cpp b/Src/Wasabi/api/wnd/wndclass/typesheet.cpp new file mode 100644 index 00000000..7df91946 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/typesheet.cpp @@ -0,0 +1,38 @@ +#include <precomp.h> + +#include "typesheet.h" +#include <api/wnd/wndclass/svcwndhold.h> +#include <api/wnd/wndclass/buttbar.h> +#include <api/service/svcs/svc_wndcreate.h> + +TypeSheet::TypeSheet(const wchar_t *_windowtype) : + TabSheet(ButtBar::STACK), windowtype(_windowtype) +{ } + +int TypeSheet::onInit() { + TYPESHEET_PARENT::onInit(); + load(); + return 1; +} + +void TypeSheet::setWindowType(const wchar_t *wtype) { + windowtype = wtype; +} + +void TypeSheet::load() { + if (windowtype == NULL || !*windowtype) return; + WindowCreateByTypeEnum se(windowtype); + svc_windowCreate *svc; + while (svc = se.getNext()) { + for (int i = 0; ; i++) { + ServiceWndHolder *svcwnd = new ServiceWndHolder; + ifc_window *wnd = svc->createWindowOfType(windowtype, svcwnd, i); + if (wnd == NULL) { + delete svcwnd; + break; + } + svcwnd->setChild(wnd, svc); + addChild(svcwnd); + } + } +} diff --git a/Src/Wasabi/api/wnd/wndclass/typesheet.h b/Src/Wasabi/api/wnd/wndclass/typesheet.h new file mode 100644 index 00000000..8f580084 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/typesheet.h @@ -0,0 +1,49 @@ +#ifndef _TYPESHEET_H +#define _TYPESHEET_H + +#include "tabsheet.h" + +#define TYPESHEET_PARENT TabSheet +/** + Class TypeSheet + + @short A Typesheet Control. + @author Nullsoft + @ver 1.0 + @see +*/ +class TypeSheet : public TYPESHEET_PARENT { +public: + + /** + TypeSheet constructor + + @param _windowtype + */ + TypeSheet(const wchar_t *windowtype); + + /** + TypeSheet method onInit . + + @ret 1 + @param None + */ + virtual int onInit(); + + /** + TypeSheet method load + */ + virtual void load(); + + /** + TypeSheet method setWindowType . + + @param windowtype The type of the window. + */ + virtual void setWindowType(const wchar_t *windowtype); + +private: + StringW windowtype; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/virtualhostwnd.cpp b/Src/Wasabi/api/wnd/wndclass/virtualhostwnd.cpp new file mode 100644 index 00000000..ef91ebc1 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/virtualhostwnd.cpp @@ -0,0 +1,198 @@ +#include "precomp.h" +#include "virtualhostwnd.h" +#include <tataki/canvas/ifc_canvas.h> +#include <tataki/region/api_region.h> + +VirtualHostWnd::VirtualHostWnd() { + group = new GuiObjectWnd(); + fittoclient = 0; + xoffset = 0; + yoffset = 0; + groupwidth = 0; + groupheight = 0; + scripts_enabled = 1; +} + +VirtualHostWnd::~VirtualHostWnd() { + delete group; +} + +int VirtualHostWnd::onInit() { + GuiObjectWnd::onInit(); + group->setVirtual(0); + group->setStartHidden(1); + group->setParent(this); + group->init(this); + group->deferedInvalidate(); + group->setCloaked(1); // magic! + group->setVisible(1); + return 1; +} + +void VirtualHostWnd::virtualhostwnd_setContent(const char *_groupname) { + group->setContent(_groupname); + if (isPostOnInit()) + virtualhostwnd_onNewContent(); +} + +void VirtualHostWnd::virtualhostwnd_setContent(SkinItem *groupitem) { + group->setContentBySkinItem(groupitem); + if (isPostOnInit()) + virtualhostwnd_onNewContent(); +} + +void VirtualHostWnd::virtualhostwnd_onNewContent() { +} + +#ifdef WASABI_COMPILE_SCRIPT +ScriptObject *VirtualHostWnd::virtualhostwnd_findScriptObject(const char *object_id) { + return group->findScriptObject(object_id); +} +#endif + +#ifdef WASABI_COMPILE_SKIN +GuiObject *VirtualHostWnd::virtualhostwnd_getContent() { + return group->getContent(); +} + +ScriptObject *VirtualHostWnd::virtualhostwnd_getContentScriptObject() { + return group->getContentScriptObject(); +} + +GuiObject *VirtualHostWnd::virtualhostwnd_findObject(const char *object_id) { + return group->findObject(object_id); +} +#endif + +api_window *VirtualHostWnd::virtualhostwnd_getContentRootWnd() { + return group->getContentRootWnd(); +} + +int VirtualHostWnd::onPaint(Canvas *c) { + GuiObjectWnd::onPaint(c); + + virtualhostwnd_onPaintBackground(c); + + if (group == NULL) return 1; + + RECT wr; + Canvas *cv = NULL; + + group->getNonClientRect(&wr); + group->paint(NULL, NULL); + + cv = group->getFrameBuffer(); + + if (cv != NULL) { + BltCanvas *bltcanvas = static_cast<BltCanvas *>(cv); // HACK! + bltcanvas->/*getSkinBitmap()->*/blitAlpha(c, xoffset, yoffset); + } + return 1; +} + +void VirtualHostWnd::virtualhostwnd_onPaintBackground(Canvas *c) { + RECT r; + getClientRect(&r); + c->fillRect(&r, RGB(255,255,255)); +} + +int VirtualHostWnd::onResize() { + GuiObjectWnd::onResize(); + if (group != NULL) { + RECT r; + getClientRect(&r); + if (fittoclient) { + xoffset = 0; + yoffset = 0; + groupwidth = r.right-r.left; + groupheight = r.bottom-r.top; + } else { + groupwidth = group->getGuiObject()->guiobject_getAutoWidth(); + groupheight = group->getGuiObject()->guiobject_getAutoHeight(); + if (groupwidth == AUTOWH) groupwidth = 320; + if (groupheight == AUTOWH) groupheight = 200; + int cw = r.right-r.left; + int ch = r.bottom-r.top; + xoffset = (cw - groupwidth)/2; + yoffset = (ch - groupheight)/2; + } + group->resize(xoffset+r.left, yoffset+r.top, groupwidth, groupheight); + } + return 1; +} + +void VirtualHostWnd::virtualhostwnd_getContentRect(RECT *r) { + ASSERT(r != NULL); + getClientRect(r); + r->left += xoffset; + r->top += yoffset; + r->right = r->left + groupwidth; + r->bottom = r->top + groupheight; +} + + +void VirtualHostWnd::virtualhostwnd_fitToClient(int fit) { + fittoclient = fit; + if (isPostOnInit()) { + onResize(); + invalidate(); + } +} + +void VirtualHostWnd::onChildInvalidate(api_region *r, api_window *who) { + GuiObjectWnd::onChildInvalidate(r, who); + api_region *clone = r->clone(); + clone->offset(xoffset, yoffset); + invalidateRgn(clone); + r->disposeClone(clone); +} + +int VirtualHostWnd::onLeftButtonDown(int x, int y) { + // DO NOT CALL GuiObjectWnd::onLeftButtonDown(x, y); + x -= xoffset; + y -= yoffset; + return group->wndProc(group->gethWnd(), WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(x, y)); +} + +int VirtualHostWnd::onLeftButtonUp(int x, int y){ + // DO NOT CALL GuiObjectWnd::onLeftButtonUp(x, y); + x -= xoffset; + y -= yoffset; + return group->wndProc(group->gethWnd(), WM_LBUTTONUP, 0, MAKELPARAM(x, y)); +} + +int VirtualHostWnd::onRightButtonDown(int x, int y){ + // DO NOT CALL GuiObjectWnd::onRightButtonDown(x, y); + x -= xoffset; + y -= yoffset; + return group->wndProc(group->gethWnd(), WM_RBUTTONDOWN, MK_RBUTTON, MAKELPARAM(x, y)); +} + +int VirtualHostWnd::onRightButtonUp(int x, int y){ + // DO NOT CALL GuiObjectWnd::onRightButtonUp(x, y); + x -= xoffset; + y -= yoffset; + return group->wndProc(group->gethWnd(), WM_RBUTTONUP, 0, MAKELPARAM(x, y)); +} + +int VirtualHostWnd::onLeftButtonDblClk(int x, int y){ + // DO NOT CALL GuiObjectWnd::onLeftButtonDblClk(x, y); + x -= xoffset; + y -= yoffset; + return group->wndProc(group->gethWnd(), WM_RBUTTONDBLCLK, 0, MAKELPARAM(x, y)); +} + +int VirtualHostWnd::onRightButtonDblClk(int x, int y){ + // DO NOT CALL GuiObjectWnd::onRightButtonDblClk(x, y); + x -= xoffset; + y -= yoffset; + return group->wndProc(group->gethWnd(), WM_LBUTTONDBLCLK, 0, MAKELPARAM(x, y)); +} + +int VirtualHostWnd::onMouseMove(int x, int y){ + // DO NOT CALL GuiObjectWnd::onMouseMove(x, y); + x -= xoffset; + y -= yoffset; + return group->wndProc(group->gethWnd(), WM_MOUSEMOVE, 0, MAKELPARAM(x, y)); +} + diff --git a/Src/Wasabi/api/wnd/wndclass/virtualhostwnd.h b/Src/Wasabi/api/wnd/wndclass/virtualhostwnd.h new file mode 100644 index 00000000..8dbeb93f --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/virtualhostwnd.h @@ -0,0 +1,51 @@ +#ifndef __VIRTUALHOSTWND_H +#define __VIRTUALHOSTWND_H + +#include "../../common/guiobjwnd.h" + +class VirtualHostWnd : public GuiObjectWnd { + public: + VirtualHostWnd(); + virtual ~VirtualHostWnd(); + + virtual int onInit(); + virtual int onPaint(Canvas *c); + virtual int onResize(); + virtual void onChildInvalidate(api_region *r, ifc_window *who); + + virtual int onLeftButtonDown(int x, int y); + virtual int onLeftButtonUp(int x, int y); + virtual int onRightButtonDown(int x, int y); + virtual int onRightButtonUp(int x, int y); + virtual int onLeftButtonDblClk(int x, int y); + virtual int onRightButtonDblClk(int x, int y); + virtual int onMouseMove(int x, int y); + + virtual void virtualhostwnd_setContent(const wchar_t *groupid); + virtual void virtualhostwnd_setContent(SkinItem *item); + virtual void virtualhostwnd_onNewContent(); + virtual void virtualhostwnd_onPaintBackground(Canvas *c); + + virtual void virtualhostwnd_fitToClient(int fit); + virtual void virtualhostwnd_getContentRect(RECT *r); + + virtual ifc_window *virtualhostwnd_getContentRootWnd(); +#ifdef WASABI_COMPILE_SCRIPT + virtual ScriptObject *virtualhostwnd_findScriptObject(const wchar_t *object_id); +#endif +#ifdef WASABI_COMPILE_SKIN + virtual GuiObject *virtualhostwnd_findObject(const wchar_t *object_id); + virtual GuiObject *virtualhostwnd_getContent(); + virtual ScriptObject *virtualhostwnd_getContentScriptObject(); +#endif + + private: + + GuiObjectWnd *group; + int fittoclient; + int xoffset, yoffset; + int groupwidth, groupheight; + int scripts_enabled; +}; + +#endif diff --git a/Src/Wasabi/api/wnd/wndclass/wndholder.cpp b/Src/Wasabi/api/wnd/wndclass/wndholder.cpp new file mode 100644 index 00000000..272298a3 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/wndholder.cpp @@ -0,0 +1,369 @@ +#include <precomp.h> +#include "wndholder.h" +#include <api/service/svcs/svc_wndcreate.h> +#include <api/service/svc_enum.h> +#include <api/syscb/callbacks/wndcb.h> + +//#pragma CHAT("lone", "benski", "needs to dispatch Layout & Containers!") +#include <api/wndmgr/container.h> +#include <api/wndmgr/layout.h> + +#define CBCLASS WindowHolderI +START_DISPATCH; + CB(WNDHOLDER_ONINSERTWINDOW, onInsertWindow); + VCB(WNDHOLDER_ONREMOVEWINDOW, onRemoveWindow); + CB(WNDHOLDER_WANTGUID, wantGuid); + CB(WNDHOLDER_WANTGROUP, wantGroup); + CB(WNDHOLDER_GETROOTWNDPTR, getRootWndPtr); + CB(WNDHOLDER_GETCURGROUPID, getCurGroupId); + CB(WNDHOLDER_GETCURGUID, getCurGuid); + CB(WNDHOLDER_GETCURROOTWND, getCurRootWnd); + CB(WNDHOLDER_GETCURID, getCurId); + CB(WNDHOLDER_ISGENERICGUID, acceptsGenericGuid); + CB(WNDHOLDER_ISGENERICGROUP, acceptsGenericGroup); + VCB(WNDHOLDER_CANCELDEFERREDREMOVE, cancelDeferredRemove); + VCB(WNDHOLDER_ONNEEDRELOADGRP, wndholder_onNeedReloadGroup); + CB(WNDHOLDER_WANTAUTOFOCUS, wndholder_wantAutoFocus); + CB(WNDHOLDER_ISAUTOAVAILABLE, wndholder_isAutoAvailable); +END_DISPATCH; + +WindowHolderI::WindowHolderI() { + wnd = NULL; + dr = NULL; + generic_guid = 1; + generic_group = 1; + cur_guid = INVALID_GUID; + cur_groupid = NULL; + wc_svc = NULL; + WASABI_API_WNDMGR->wndholder_register(this); +} + +WindowHolderI::~WindowHolderI() { + delete dr; + if (wnd != NULL) { + if (wc_svc) { + ifc_window *w = wnd; + wnd = NULL; + wc_svc->destroyWindow(w); + SvcEnum::release(wc_svc); + wc_svc = NULL; + } else { + ifc_window *w = wnd; + wnd = NULL; + WASABI_API_SKIN->group_destroy(w); + } + } + accepted_groups.deleteAll(); + accepted_guids.deleteAll(); + WASABI_API_WNDMGR->wndholder_unregister(this); +} + +ifc_window *WindowHolderI::onInsertWindow(GUID g, const wchar_t *groupid) +{ + cancelDeferredRemove(); + + defered_guid = INVALID_GUID; + if (wnd != NULL) return NULL; + + cur_groupid = groupid; + if (g != INVALID_GUID) + { + cur_guid = g; + wchar_t cguid[256] = {0}; + nsGUID::toCharW(g, cguid); + cur_id = cguid; + } else + cur_id = groupid; + + wnd = createWindow(g == INVALID_GUID ? NULL : &g, groupid); + if (!wnd) { + cur_guid = INVALID_GUID; + cur_id = L""; + cur_groupid = L""; + } + if (wnd) { + onInsert(wnd, cur_id); + } else { + if (g != INVALID_GUID) { + defered_guid = g; + } + } + return wnd; +} + +void WindowHolderI::onRemoveWindow(int deferred) { + if (deferred) { + if (!dr) + dr = new DeferredRemove(this); + dr->post(); + return; + } + if (wnd != NULL) { + ifc_window *w = getCurRootWnd(); + onRemove(w, cur_id); + destroyWindow(); + cur_guid = INVALID_GUID; + cur_groupid = NULL; + defered_guid = INVALID_GUID; + } +} + +void WindowHolderI::cancelDeferredRemove() { + delete dr; + dr = NULL; +} + +void WindowHolderI::wndholder_onNeedReloadGroup(const wchar_t *id) +{ + if (cur_groupid.isempty()) return; + ifc_window *w = getCurRootWnd(); + if (w == NULL) return; + if (w->isInited() && !WCSICMP(cur_groupid, id)) + { + onRemove(w, cur_id); + destroyWindow(); + createWindow(&INVALID_GUID, id); + onInsert(wnd, cur_id); + } +} + +ifc_window *WindowHolderI::createWindow(const GUID *g, const wchar_t *groupid) +{ + ASSERT(wnd == NULL); + if (g != NULL && *g != INVALID_GUID) { + wc_svc = WindowCreateByGuidEnum(*g).getFirst(); + if (wc_svc) + wnd = wc_svc->createWindowByGuid(*g, getRootWndPtr()); + } + else if (groupid != NULL) + { + wc_svc = NULL; + wnd = WASABI_API_SKIN->group_create(groupid); + } + if (wnd) { + if (!wnd->isInited()) { + if (!wnd->getParent()) wnd->setParent(getRootWndPtr()); + wnd->init(getRootWndPtr()); + } + } + return wnd; +} + +void WindowHolderI::destroyWindow() { + ASSERT(wnd != NULL); + + ifc_window *w = wnd->getDesktopParent(); + + if (wc_svc) { + ifc_window *w = wnd; + wnd = NULL; + wc_svc->destroyWindow(w); + SvcEnum::release(wc_svc); + wc_svc = NULL; + } else { + ifc_window *w = wnd; + wnd = NULL; + WASABI_API_SKIN->group_destroy(w); + } + + if (w != NULL) { + if (w->getInterface(layoutGuid)) { + static_cast<Layout *>(w)->updateTransparency(); + } + } + +} + +void WindowHolderI::addAcceptGuid(GUID g) { + accepted_guids.addItem(new GUID(g)); +} + +void WindowHolderI::addAcceptGroup(const wchar_t *groupid) +{ + accepted_groups.addItem(new StringW(groupid)); +} + +void WindowHolderI::setAcceptAllGuids(int tf) { + generic_guid = tf; +} + +void WindowHolderI::setAcceptAllGroups(int tf) { + generic_group = tf; +} + +int WindowHolderI::wantGuid(GUID g) { + if (acceptsGenericGuid()) return 1; + for (int i=0;i<accepted_guids.getNumItems();i++) { + if (*accepted_guids.enumItem(i) == g) return 1; + } + return 0; +} + +int WindowHolderI::wantGroup(const wchar_t *groupid) +{ + if (acceptsGenericGroup()) return 1; + for (int i=0;i<accepted_groups.getNumItems();i++) { + if (!WCSICMP(accepted_groups.enumItem(i)->getValue(), groupid)) + return 1; + } + return 0; +} + +GUID *WindowHolderI::getFirstAcceptedGuid() { + if (accepted_guids.getNumItems() == 0) return NULL; + return accepted_guids.enumItem(0); +} + +const wchar_t *WindowHolderI::getFirstAcceptedGroup() +{ + if (accepted_guids.getNumItems() == 0) + return NULL; + return + accepted_groups.enumItem(0)->getValue(); +} + +int WindowHolderI::wndholder_wantAutoFocus() { + return 1; +} + +WindowHolderWnd::WindowHolderWnd() { + autoavail = 1; + autoopen = 1; + autoclose = 0; + nocmdbar = 0; + noanim = 0; + has_wnd = 0; + autofocus = 1; + WASABI_API_SYSCB->syscb_registerCallback(this); +} + +WindowHolderWnd::~WindowHolderWnd() { + if (has_wnd) { + notifyOnRemove(); + } + WASABI_API_SYSCB->syscb_deregisterCallback(this); +} + +int WindowHolderWnd::onResize() { + WINDOWHOLDER_PARENT::onResize(); + if (getCurRootWnd()) { + RECT r; + getClientRect(&r); + if (!getCurRootWnd()->handleRatio()) + multRatio(&r); + getCurRootWnd()->resize(r.left, r.top, r.right-r.left, r.bottom-r.top); + } + return 1; +} + +int WindowHolderWnd::handleRatio() { + return 1; +} + +int WindowHolderWnd::handleDesktopAlpha() { + if (getCurRootWnd()) return getCurRootWnd()->handleDesktopAlpha(); + return 1; +} + +int WindowHolderWnd::handleTransparency() { + if (getCurRootWnd()) return getCurRootWnd()->handleTransparency(); + return 1; +} + +int WindowHolderWnd::onInit() { + WINDOWHOLDER_PARENT::onInit(); + if (isVisible() && autoopen && getFirstAcceptedGuid()) { + onInsertWindow(*getFirstAcceptedGuid(), NULL); + } else if (isVisible() && autoopen && getFirstAcceptedGroup()) { + onInsertWindow(INVALID_GUID, getFirstAcceptedGroup()); + } + return 1; +} + +#define DC_NOTIFYONREMOVE 0x205 + +void WindowHolderWnd::onRemove(ifc_window *w, const wchar_t *id) +{ + ifc_window *dw = getDesktopParent(); + if (dw) dw->removeMinMaxEnforcer(this); + postDeferredCallback(DC_NOTIFYONREMOVE); +} + +int WindowHolderWnd::onDeferredCallback(intptr_t p1, intptr_t p2) { + if (p1 == DC_NOTIFYONREMOVE) { + notifyOnRemove(); + } else return WINDOWHOLDER_PARENT::onDeferredCallback(p1, p2); + return 1; +} + +void WindowHolderWnd::onInsert(ifc_window *w, const wchar_t *id) +{ + if (isPostOnInit()) + onResize(); + ifc_window *dw = getDesktopParent(); + if (dw) dw->addMinMaxEnforcer(this); + notifyOnInsert(); + if (wndholder_wantAutoFocus()) w->setFocus(); +} + +int WindowHolderWnd::getPreferences(int what) { + if (getCurRootWnd()) return getCurRootWnd()->getPreferences(what); + return WINDOWHOLDER_PARENT::getPreferences(what); +} + +void WindowHolderWnd::notifyOnRemove() { + has_wnd = 0; + Layout *l = getGuiObject()->guiobject_getParentLayout(); + if (l != NULL) { + Container *c = l->getParentContainer(); + if (c != NULL) { + c->notifyRemoveContent(this); + } + } +} + +void WindowHolderWnd::notifyOnInsert() { + has_wnd = 1; + Layout *l = getGuiObject()->guiobject_getParentLayout(); + if (l != NULL) { + Container *c = l->getParentContainer(); + if (c != NULL) + { + c->notifyAddContent(this, getCurGroupId(), getCurGuid()); + } + } +} + +int WindowHolderWnd::onGroupChange(const wchar_t *grpid) +{ + WINDOWHOLDER_PARENT::onGroupChange(grpid); + wndholder_onNeedReloadGroup(grpid); + return 1; +} + +void WindowHolderWnd::onSetVisible(int show) { + if (show && getCurRootWnd() == NULL) { + if (autoopen && getFirstAcceptedGuid()) { + onInsertWindow(*getFirstAcceptedGuid(), NULL); + } else if (autoopen && getFirstAcceptedGroup()) { + onInsertWindow(INVALID_GUID, getFirstAcceptedGroup()); + } + } else if (!show && getCurRootWnd() != NULL) { + if (autoclose && WASABI_API_SKIN->skin_getVersion() >= 1.0) + onRemoveWindow(0); + } + if (getDeferedGuid() != INVALID_GUID) { + if (show) { + #ifdef ON_CREATE_EXTERNAL_WINDOW_GUID + int y; + ON_CREATE_EXTERNAL_WINDOW_GUID(getDeferedGuid(), y); + #endif + } + } + WINDOWHOLDER_PARENT::onSetVisible(show); +} + +int WindowHolderWnd::wndholder_wantAutoFocus() { + return autofocus; +} + diff --git a/Src/Wasabi/api/wnd/wndclass/wndholder.h b/Src/Wasabi/api/wnd/wndclass/wndholder.h new file mode 100644 index 00000000..580b8ba2 --- /dev/null +++ b/Src/Wasabi/api/wnd/wndclass/wndholder.h @@ -0,0 +1,285 @@ +#ifndef __WINDOWHOLDER_H +#define __WINDOWHOLDER_H + +#include <api/wnd/wndclass/guiobjwnd.h> +#include <bfc/ptrlist.h> +#include <api/syscb/callbacks/wndcb.h> +#include <api/timer/timerclient.h> + +class svc_windowCreate; + +#define WINDOWHOLDER_PARENT GuiObjectWnd + + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class WindowHolder : public Dispatchable +{ + public: + ifc_window *onInsertWindow(GUID g, const wchar_t *groupid); + void onRemoveWindow(int deferred=0); + int wantGuid(GUID g); + int wantGroup(const wchar_t *groupid); + GUID getCurGuid(); + const wchar_t *getCurGroupId(); + ifc_window *getCurRootWnd(); + const wchar_t *getCurId(); + ifc_window *getRootWndPtr(); + int acceptsGenericGuid(); + int acceptsGenericGroup(); + int wndholder_getPreferences(int what); + void wndholder_onNeedReloadGroup(const wchar_t *id); + void cancelDeferredRemove(); + int wndholder_wantAutoFocus(); + int wndholder_isAutoAvailable(); + + enum { + WNDHOLDER_ONINSERTWINDOW=50, + WNDHOLDER_ONREMOVEWINDOW=100, + WNDHOLDER_WANTGUID=150, + WNDHOLDER_WANTGROUP=200, + WNDHOLDER_GETROOTWNDPTR=250, + WNDHOLDER_GETCURGUID=300, + WNDHOLDER_GETCURGROUPID=350, + WNDHOLDER_GETCURROOTWND=400, + WNDHOLDER_GETCURID=450, + WNDHOLDER_ISGENERICGUID=500, + WNDHOLDER_ISGENERICGROUP=550, + WNDHOLDER_GETPREFERENCES=600, + WNDHOLDER_ONNEEDRELOADGRP=650, + WNDHOLDER_CANCELDEFERREDREMOVE=660, + WNDHOLDER_WANTAUTOFOCUS=670, + WNDHOLDER_ISAUTOAVAILABLE=680, + }; +}; + +inline ifc_window *WindowHolder::onInsertWindow(GUID g, const wchar_t *groupid) { + return _call(WNDHOLDER_ONINSERTWINDOW, (ifc_window *)NULL, g, groupid); +} + +inline void WindowHolder::onRemoveWindow(int def) { + _voidcall(WNDHOLDER_ONREMOVEWINDOW, def); +} + +inline int WindowHolder::wantGuid(GUID g) { + return _call(WNDHOLDER_WANTGUID, 0, g); +} + +inline int WindowHolder::wantGroup(const wchar_t *groupid) { + return _call(WNDHOLDER_WANTGROUP, 0, groupid); +} + +inline ifc_window *WindowHolder::getRootWndPtr() { + return _call(WNDHOLDER_GETROOTWNDPTR, (ifc_window *)NULL); +} + +inline GUID WindowHolder::getCurGuid() { + return _call(WNDHOLDER_GETCURGUID, INVALID_GUID); +} + +inline const wchar_t *WindowHolder::getCurGroupId() { + return _call(WNDHOLDER_GETCURGROUPID, (const wchar_t *)NULL); +} + +inline ifc_window *WindowHolder::getCurRootWnd() { + return _call(WNDHOLDER_GETCURROOTWND, (ifc_window *)NULL); +} + +inline const wchar_t *WindowHolder::getCurId() { + return _call(WNDHOLDER_GETCURID, (const wchar_t *)NULL); +} + +inline int WindowHolder::acceptsGenericGuid() { + return _call(WNDHOLDER_ISGENERICGUID, 0); +} + +inline int WindowHolder::acceptsGenericGroup() { + return _call(WNDHOLDER_ISGENERICGROUP, 0); +} + +inline int WindowHolder::wndholder_getPreferences(int what) { + return _call(WNDHOLDER_GETPREFERENCES, 0, what); +} + +inline void WindowHolder::wndholder_onNeedReloadGroup(const wchar_t *id) { + _voidcall(WNDHOLDER_ONNEEDRELOADGRP, id); +} + +inline void WindowHolder::cancelDeferredRemove() { + _voidcall(WNDHOLDER_CANCELDEFERREDREMOVE); +} + +inline int WindowHolder::wndholder_wantAutoFocus() { + return _call(WNDHOLDER_WANTAUTOFOCUS, 1); +} + +inline int WindowHolder::wndholder_isAutoAvailable() { + return _call(WNDHOLDER_ISAUTOAVAILABLE, 1); +} + +class DeferredRemove; + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class WindowHolderI : public WindowHolder { + public: + + WindowHolderI(); + virtual ~WindowHolderI(); + + virtual ifc_window *onInsertWindow(GUID g, const wchar_t *groupid); + + + virtual void onRemoveWindow(int deferred); + + + void addAcceptGuid(GUID g); + void addAcceptGroup(const wchar_t *groupid); + void setAcceptAllGuids(int tf); + + + void setAcceptAllGroups(int tf); + + virtual int acceptsGenericGroup() { return generic_group; } + + virtual int acceptsGenericGuid() { return generic_guid; } + + virtual int wantGuid(GUID g); + virtual int wantGroup(const wchar_t *groupid); + + virtual void onInsert(ifc_window *w, const wchar_t *id) {}; + + virtual void onRemove(ifc_window *w, const wchar_t *id) {}; + + virtual ifc_window *getRootWndPtr()=0; + virtual GUID getCurGuid() { return cur_guid; } + virtual const wchar_t *getCurGroupId() { return cur_groupid; } + + virtual ifc_window *getCurRootWnd() { return wnd; } + + virtual GUID *getFirstAcceptedGuid(); + virtual const wchar_t *getFirstAcceptedGroup(); + virtual const wchar_t *getCurId() { return cur_id; } + + virtual void cancelDeferredRemove(); + virtual int wndholder_isAutoAvailable() { return 1; } + + GUID getDeferedGuid() { return defered_guid; } + + virtual int wndholder_getPreferences(int what)=0; + + virtual void wndholder_onNeedReloadGroup(const wchar_t *id); + + private: + + + ifc_window *createWindow(const GUID *g, const wchar_t *groupid); + virtual int wndholder_wantAutoFocus(); + + + void destroyWindow(); + + ifc_window *wnd; + GUID cur_guid; + StringW cur_groupid; + StringW cur_id; + PtrList<GUID> accepted_guids; + PtrList<StringW> accepted_groups; + int generic_guid; + int generic_group; + + svc_windowCreate *wc_svc; + GUID defered_guid; + + DeferredRemove *dr; + + protected: + + RECVS_DISPATCH; +}; + + +/** + Class + + @short + @author Nullsoft + @ver 1.0 + @see +*/ +class WindowHolderWnd : public WINDOWHOLDER_PARENT, public WindowHolderI +{ + + public: + + WindowHolderWnd(); + virtual ~WindowHolderWnd(); + virtual int onInit(); + + virtual ifc_window *getRootWndPtr() { return this; } + virtual void onInsert(ifc_window *w, const wchar_t *id); + virtual void onRemove(ifc_window *w, const wchar_t *id); + virtual int onResize(); + virtual int handleRatio(); + virtual int handleDesktopAlpha(); + virtual int handleTransparency(); + virtual int wndholder_getPreferences(int what) { return getPreferences(what); } + virtual int getPreferences(int what); + void setAutoOpen(int tf) { autoopen = tf; } + void setAutoClose(int tf) { autoclose = tf; } + void setNoCmdBar(int tf) { nocmdbar = tf; if (isInited()) invalidate(); } + void setNoAnim(int tf) { noanim = tf; } + virtual int onGroupChange(const wchar_t *grpid); + virtual int wndholder_wantAutoFocus(); + void setAutoFocus(int autof) { autofocus = autof; } + void setAutoAvailable(int autoa) { autoavail = autoa; } + virtual int wndholder_isAutoAvailable() { return autoavail; } + + private: + void notifyOnRemove(); // no virtual please + void notifyOnInsert(); // no virtual please + virtual void onSetVisible(int show); + virtual int onDeferredCallback(intptr_t p1, intptr_t p2); + + int autoopen; + int autoclose; + int nocmdbar; + int noanim; + int has_wnd; + int autofocus; + int autoavail; +}; + +class DeferredRemove : public TimerClientDI +{ + public: + DeferredRemove(WindowHolderI *parent) : whi(parent) {} + virtual ~DeferredRemove() {} + + void post() { + timerclient_postDeferredCallback(1, 0); + } + + virtual int timerclient_onDeferredCallback(intptr_t p1, intptr_t p2) { + if (p1 == 1 && whi != NULL) whi->onRemoveWindow(0); + else return TimerClientDI::timerclient_onDeferredCallback(p1, p2); + return 0; + } + + private: + WindowHolderI *whi; +}; + +#endif |