aboutsummaryrefslogtreecommitdiff
path: root/Src/Wasabi/api/wnd/wndclass
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Wasabi/api/wnd/wndclass
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Wasabi/api/wnd/wndclass')
-rw-r--r--Src/Wasabi/api/wnd/wndclass/SelItemList.cpp72
-rw-r--r--Src/Wasabi/api/wnd/wndclass/SelItemList.h31
-rw-r--r--Src/Wasabi/api/wnd/wndclass/abstractwndhold.cpp287
-rw-r--r--Src/Wasabi/api/wnd/wndclass/abstractwndhold.h267
-rw-r--r--Src/Wasabi/api/wnd/wndclass/appbarwnd.cpp1113
-rw-r--r--Src/Wasabi/api/wnd/wndclass/appbarwnd.h155
-rw-r--r--Src/Wasabi/api/wnd/wndclass/backbufferwnd.cpp86
-rw-r--r--Src/Wasabi/api/wnd/wndclass/backbufferwnd.h53
-rw-r--r--Src/Wasabi/api/wnd/wndclass/blankwnd.cpp24
-rw-r--r--Src/Wasabi/api/wnd/wndclass/blankwnd.h41
-rw-r--r--Src/Wasabi/api/wnd/wndclass/bufferpaintwnd.cpp113
-rw-r--r--Src/Wasabi/api/wnd/wndclass/bufferpaintwnd.h40
-rw-r--r--Src/Wasabi/api/wnd/wndclass/buttbar.cpp208
-rw-r--r--Src/Wasabi/api/wnd/wndclass/buttbar.h168
-rw-r--r--Src/Wasabi/api/wnd/wndclass/buttwnd.cpp761
-rw-r--r--Src/Wasabi/api/wnd/wndclass/buttwnd.h602
-rw-r--r--Src/Wasabi/api/wnd/wndclass/clickwnd.cpp319
-rw-r--r--Src/Wasabi/api/wnd/wndclass/clickwnd.h74
-rw-r--r--Src/Wasabi/api/wnd/wndclass/ddrawwnd.cpp219
-rw-r--r--Src/Wasabi/api/wnd/wndclass/ddrawwnd.h60
-rw-r--r--Src/Wasabi/api/wnd/wndclass/editwnd.cpp1001
-rw-r--r--Src/Wasabi/api/wnd/wndclass/editwnd.h134
-rw-r--r--Src/Wasabi/api/wnd/wndclass/editwndstring.cpp17
-rw-r--r--Src/Wasabi/api/wnd/wndclass/editwndstring.h24
-rw-r--r--Src/Wasabi/api/wnd/wndclass/embeddedxui.cpp143
-rw-r--r--Src/Wasabi/api/wnd/wndclass/embeddedxui.h88
-rw-r--r--Src/Wasabi/api/wnd/wndclass/foreignwnd.cpp60
-rw-r--r--Src/Wasabi/api/wnd/wndclass/foreignwnd.h56
-rw-r--r--Src/Wasabi/api/wnd/wndclass/framewnd.cpp777
-rw-r--r--Src/Wasabi/api/wnd/wndclass/framewnd.h124
-rw-r--r--Src/Wasabi/api/wnd/wndclass/gradientwnd.cpp82
-rw-r--r--Src/Wasabi/api/wnd/wndclass/gradientwnd.h31
-rw-r--r--Src/Wasabi/api/wnd/wndclass/guiobjwnd.cpp476
-rw-r--r--Src/Wasabi/api/wnd/wndclass/guiobjwnd.h237
-rw-r--r--Src/Wasabi/api/wnd/wndclass/itemlistwnd.cpp286
-rw-r--r--Src/Wasabi/api/wnd/wndclass/itemlistwnd.h426
-rw-r--r--Src/Wasabi/api/wnd/wndclass/labelwnd.cpp203
-rw-r--r--Src/Wasabi/api/wnd/wndclass/labelwnd.h46
-rw-r--r--Src/Wasabi/api/wnd/wndclass/listwnd.cpp2151
-rw-r--r--Src/Wasabi/api/wnd/wndclass/listwnd.h459
-rw-r--r--Src/Wasabi/api/wnd/wndclass/oswnd.cpp32
-rw-r--r--Src/Wasabi/api/wnd/wndclass/oswnd.h19
-rw-r--r--Src/Wasabi/api/wnd/wndclass/oswndhost.cpp14
-rw-r--r--Src/Wasabi/api/wnd/wndclass/oswndhost.h53
-rw-r--r--Src/Wasabi/api/wnd/wndclass/qpaintwnd.cpp357
-rw-r--r--Src/Wasabi/api/wnd/wndclass/qpaintwnd.h154
-rw-r--r--Src/Wasabi/api/wnd/wndclass/rootwndholder.cpp113
-rw-r--r--Src/Wasabi/api/wnd/wndclass/rootwndholder.h41
-rw-r--r--Src/Wasabi/api/wnd/wndclass/scbkgwnd.cpp869
-rw-r--r--Src/Wasabi/api/wnd/wndclass/scbkgwnd.h463
-rw-r--r--Src/Wasabi/api/wnd/wndclass/scrollbar.cpp679
-rw-r--r--Src/Wasabi/api/wnd/wndclass/scrollbar.h423
-rw-r--r--Src/Wasabi/api/wnd/wndclass/sepwnd.cpp63
-rw-r--r--Src/Wasabi/api/wnd/wndclass/sepwnd.h27
-rw-r--r--Src/Wasabi/api/wnd/wndclass/slider.cpp705
-rw-r--r--Src/Wasabi/api/wnd/wndclass/slider.h451
-rw-r--r--Src/Wasabi/api/wnd/wndclass/status.cpp242
-rw-r--r--Src/Wasabi/api/wnd/wndclass/status.h178
-rw-r--r--Src/Wasabi/api/wnd/wndclass/svcwndhold.cpp53
-rw-r--r--Src/Wasabi/api/wnd/wndclass/svcwndhold.h51
-rw-r--r--Src/Wasabi/api/wnd/wndclass/tabsheet.cpp544
-rw-r--r--Src/Wasabi/api/wnd/wndclass/tabsheet.h268
-rw-r--r--Src/Wasabi/api/wnd/wndclass/tabsheetbar.cpp130
-rw-r--r--Src/Wasabi/api/wnd/wndclass/tabsheetbar.h39
-rw-r--r--Src/Wasabi/api/wnd/wndclass/textbar.cpp148
-rw-r--r--Src/Wasabi/api/wnd/wndclass/textbar.h219
-rw-r--r--Src/Wasabi/api/wnd/wndclass/tooltip.cpp47
-rw-r--r--Src/Wasabi/api/wnd/wndclass/tooltip.h18
-rw-r--r--Src/Wasabi/api/wnd/wndclass/treewnd.cpp1645
-rw-r--r--Src/Wasabi/api/wnd/wndclass/treewnd.h624
-rw-r--r--Src/Wasabi/api/wnd/wndclass/typesheet.cpp38
-rw-r--r--Src/Wasabi/api/wnd/wndclass/typesheet.h49
-rw-r--r--Src/Wasabi/api/wnd/wndclass/virtualhostwnd.cpp198
-rw-r--r--Src/Wasabi/api/wnd/wndclass/virtualhostwnd.h51
-rw-r--r--Src/Wasabi/api/wnd/wndclass/wndholder.cpp369
-rw-r--r--Src/Wasabi/api/wnd/wndclass/wndholder.h285
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(&reg);
+ back_buffer->selectClipRgn(&reg);
+#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(&reg);
+ RegionI clip(&r);
+ reg.offset(0, offset);
+ clip.subtractRegion(&reg);
+ 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(&reg);
+ RegionI clip(&r);
+ reg.offset(0, offset);
+ clip.subtractRegion(&reg);
+ 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(&reg);
+ RegionI clip(&r);
+ reg.offset(offset, 0);
+ clip.subtractRegion(&reg);
+ 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(&reg);
+
+ 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(&reg);
+ RegionI clip(&r);
+ reg.offset(offset, 0);
+ clip.subtractRegion(&reg);
+ 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(&reg);
+
+ 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(&reg);
+ 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(&reg);
+ 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