// ****************************************************************************
// Copyright (C) 1998 by Dieter Windau
// All rights reserved
//
// dockingex.cpp: implementation file
// Version: 1.4
// Date: 25.10.1998
// Author: Dieter Windau
//
// Freeware OWL classes that extents the dockable and gadget system
//
// You are free to use/modify this code but leave this header intact.
// May not be sold for profit.
//
// Tested with Borland C++ 5.02, OWL 5.02 with Windows NT 4.0 SP3 but I think
// the class should work with Windows 95 too.
// This file is provided "as is" with no expressed or implied warranty.
// Use at your own risk.
//
// This package contains many classes and source that are based on other OWL
// developers work. Very special thanks to Alan Chambers, Christopher Kohlhoff,
// Jo Parrello and Yura Bidus
//
// Please send me bug reports, bug fixes, enhancements, requests, etc., and
// I'll try to keep this in next versions:
// EMail: dieter.windau@kvwl.de
// Web: http://members.aol.com/softengage/index.htm
// ****************************************************************************
#include <owlext/pch.h>
#pragma hdrstop
#include <owl/uihelper.h> // for TUIBorder edge painting
#include <owl/registry.h>
#include <owl/profile.h>
#include <owl/filename.h>
#include <owlext/harborex.h>
#include <owlext/dockingex.h>
#include <owlext/gadgetex.h>
#include <owlext/gadgctrl.h>
#include <owlext/util.h>
#include <owlext/dockingex.rh>
#ifdef CTXHELP
#include <owlext/ctxhelpm.h>
#endif
using namespace owl;
using namespace std;
namespace OwlExt {
// ******************************* constants ***********************************
const int GripperDistSize = 3; // Distance from gripper to gripper and size of gripper
const int FloatMarginsX = 4; // Left and Right Margins, FloatingSlip
const int FloatMarginsY = 3; // Top and Bottom Margins, FloatingSlip
const int EdgeMaginsX = 5; // Left and Right Margins, EdgeSlip
const int EdgeMaginsY = 5; // Top and Bottom Margins, EdgeSlip
// ****************** TDockableControlBarEx ***********************************
DEFINE_RESPONSE_TABLE1(TDockableControlBarEx, TDockableControlBar)
#if (OWLInternalVersion >= 0x06000000L)
EV_OWLWINDOWDOCKED,
#endif
EV_WM_LBUTTONDOWN,
EV_WM_LBUTTONDBLCLK,
EV_WM_RBUTTONDOWN,
EV_COMMAND(IDCANCEL, CmCancel),
EV_COMMAND(IDOK, CmOk),
END_RESPONSE_TABLE;
#if (OWLInternalVersion < 0x06000000L)
bool TDockableControlBarEx::FlatStyle = false;
#endif
TDockableControlBarEx::TDockableControlBarEx(TWindow* parent,
TTileDirection direction, TFont* font, TModule* module):
TDockableControlBar(parent, direction, font, module),
DefaultGadgets(5,0,5)
// default constructor
{
// SetMargins(TMargins(TMargins::Pixels,0,0,0,0));
// Margins are setting in TEdgeSlipEx::DockableInsert()
// and TFloatingSlipEx::DockableInsert()
Attr.Id = 0;
Default = true;
HasHelp = false;
// no default position and location
Rect = ToggleRect = TRect(-1,-1,-1,-1);
Location = ToggleLocation = alTop;
}
TDockableControlBarEx::TDockableControlBarEx(uint id, LPCTSTR title,
TWindow* parent, bool _Default, bool hasHelp,
TTileDirection direction, TFont* font, TModule* module):
TDockableControlBar(parent, direction, font, module),
DefaultGadgets(5,0,5)
// constructor that set additional the Attr.id for context help and OLE 2,
// title for the caption, and the flag for a default controlbar
{
Attr.Id = id;
SetCaption(title ? title : _T(""));
Default = _Default;
HasHelp = hasHelp;
// no default position and location
Rect = ToggleRect = TRect(-1,-1,-1,-1);
Location = ToggleLocation = alTop;
}
TDockableControlBarEx::~TDockableControlBarEx()
{
Destroy(IDCANCEL);
}
void TDockableControlBarEx::SetDefaultGadgets(const TIntArray& defaultGadgets)
// Set the dafault gadgets with the id. All id's should be added in
// GadgetsDescriptors. Best place to set the default gadgets is after
// the toolbar is inserted in harbor
{
DefaultGadgets.Flush();
for (uint i=0; i<defaultGadgets.GetItemsInContainer(); i++)
DefaultGadgets.Add(defaultGadgets[i]);
InsertDefaultGadgets();
}
void TDockableControlBarEx::SetDefaultGadgets(const int iArray[], uint numGadgets)
// Set the dafault gadgets with the id. All id's should be added in
// GadgetsDescriptors. Best place to set the default gadgets is after
// the toolbar is inserted in harbor
{
PRECONDITION(iArray);
DefaultGadgets.Flush();
for (uint i=0; i<numGadgets; i++)
DefaultGadgets.Add(iArray[i]);
InsertDefaultGadgets();
}
void TDockableControlBarEx::InsertDefaultGadgets()
// Remove all existing gadgets and insert the default gadgets in the tool bar
// If layoutSession=true the toolbar will be updated
{
PRECONDITION(GadgetDescriptors);
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
if (harbor) {
RemoveAllGadgets();
if (DefaultGadgets.GetItemsInContainer() > 0) {
for (uint i=0; i<DefaultGadgets.GetItemsInContainer(); i++) {
int id = DefaultGadgets[i];
harbor->CheckBeforeInsertDefaultGadget(id);
TGadget* gadget = GadgetDescriptors->ConstructGadget(id);
if (gadget)
Insert(*gadget);
}
LayoutSession();
}
CheckOnLastGadget();
}
}
#if (OWLInternalVersion >= 0x06000000L)
bool TDockableControlBarEx::Create()
{
bool retValue = TDockableControlBar::Create();
// Don't create FlatHandleGadget because. Gripper is drawn by this class
//
TGadget* gadget = FirstGadget();
if (gadget && gadget->GetId() == IDG_FLATHANDLE) {
delete Remove(*gadget);
delete Cursor;
Cursor = 0;
}
return retValue;
}
#endif
void TDockableControlBarEx::Hide()
{
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
if (harbor) {
harbor->Remove(*this);
harbor->UpdateShowHideCtlBar();
}
}
void TDockableControlBarEx::Show()
{
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
if (harbor) {
harbor->Insert(*this, Location, &Rect.TopLeft());
// If the inserted docking slip is floating one with location alNone
// compute the real size. Thanks to Jo Parrello
//
TFloatingSlipEx* flslip = TYPESAFE_DOWNCAST(this, TFloatingSlipEx);
TSize size(Rect.Width(), Rect.Height());
if (flslip && (Location == alNone)) {
TSize newsize = flslip->ComputeSize(alNone, &size);
flslip->Layout(alNone, &newsize);
}
//D: This works also not correct
// harbor->Move(*this, Location, &Rect.TopLeft());
harbor->UpdateShowHideCtlBar();
}
}
void TDockableControlBarEx::PreRemoved()
// Called by TFloatingSlipEx or TEdgeSlipEx if the conrolbar will be removed
{
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
if (harbor) {
if (harbor->GetCustWindow() == this)
harbor->SetCustGadgetNull();
}
}
TResult TDockableControlBarEx::WindowProc(TMsgId msg, TParam1 p1, TParam2 p2)
// Processes incoming messages when toolbar is hide
{
if (msg == WM_SHOWWINDOW && !p1) // we are being hidden
StorePosAndLocation();
return TDockableControlBar::WindowProc(msg, p1, p2);
}
void TDockableControlBarEx::PaintGadgets(TDC& dc, bool b, TRect& rect)
// Overridden to paint the actual customize gadget with a black rectangle
{
TDockableControlBar::PaintGadgets(dc, b, rect);
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
if (harbor && harbor->GetCustWindow() == this) {
TGadget* gadget = FirstGadget();
while (gadget) {
if (gadget == harbor->GetCustGadget()) {
TRect bounds = gadget->GetBounds();
TControlGadgetEx* ctrlgad = TYPESAFE_DOWNCAST(gadget, TControlGadgetEx);
if (ctrlgad) {
TWindow* win = ctrlgad->GetControl();
TGadgetComboBox* gcb = TYPESAFE_DOWNCAST(win, TGadgetComboBox);
if (gcb) {
gcb->Invalidate();
gcb->UpdateWindow();
}
else {
TRect winRect;
GetRect(winRect);
bounds.Offset(winRect.left+1, winRect.top+1);
bounds.right--;
bounds.bottom--;
// If the actual customize gadget is a control gadget, then draw
// directly into the screen.
//
TScreenDC sdc;
DrawCustomizeRect(sdc, bounds);
return;
}
}
else {
bounds.left++;
bounds.top++;
DrawCustomizeRect(dc, bounds);
return;
}
}
gadget = gadget->NextGadget();
}
}
}
void TDockableControlBarEx::Paint(TDC& dc, bool erase, TRect& rect)
// Overridden to paint the etched toolbar border and the gripper
{
TDockableControlBar::Paint(dc, erase, rect);
TWindowDC MyDC(*this);
TDockingSlip* Slip = TYPESAFE_DOWNCAST(Parent, TDockingSlip);
// If toolbar is floating, we draw the horizontal divider lines and
// divider gadgets. Very special thanks to Jo Parrello
//
if (Slip && Slip->GetLocation() == alNone) {
TFloatingSlipEx* flslip = TYPESAFE_DOWNCAST(Slip, TFloatingSlipEx);
if (flslip && GetFlatStyle() != NonFlatNormal) {
TRect sliprect;
TPoint startpoint;
flslip->GetRect(sliprect);
// Be sure that the non-client left area will be erased.
//
TBrush brush(TColor::Sys3dFace);
TRect crect = flslip->GetClientRect();
crect.left = 0;
crect.right = Margins.Left;
MyDC.FillRect(crect, brush);
int maxwidth = sliprect.Width() - Margins.Right - Margins.Left;
int numrows, numcol, maxy, prevrowheight;
numrows = numcol = maxy = prevrowheight = 0;
int miny = 20000;
TGadget* firstgadg = 0;
for (TGadget* gad = FirstGadget(); gad; gad = gad->NextGadget()) {
if (numcol == 0)
firstgadg = gad;
numcol++;
TRect boundrect = gad->GetBounds();
if (boundrect.top < miny)
miny = boundrect.top;
if (boundrect.bottom > maxy)
maxy = boundrect.bottom;
bool islastgadget = gad->IsEndOfRow();
if (islastgadget || !gad->NextGadget()) {
if ((numrows > 0) && (miny > prevrowheight)) {
int middley = (miny - prevrowheight) / 2 + prevrowheight;
TPen pen (TColor::Sys3dShadow);
MyDC.SelectObject(pen);
MyDC.MoveTo(0, middley - 1);
MyDC.LineTo(maxwidth, middley - 1);
MyDC.RestorePen();
TPen pen2 (TColor::Sys3dHilight);
MyDC.SelectObject(pen2);
MyDC.MoveTo(0, middley);
MyDC.LineTo(maxwidth, middley);
MyDC.RestorePen();
}
DrawVerticalDividers(MyDC, firstgadg, gad, miny, maxy, false);
numcol = 0;
numrows++;
prevrowheight = maxy;
miny = 20000;
maxy = 0;
}
}
}
}
// When docked, this paints an etched border just
// inside the client area of the control bar
//
if (Slip && Slip->GetLocation() != alNone) {
TRect ThisClientRect = GetClientRect();
/*D: I think this looks not perfect, because the algorithm of A. Chambers
makes the rectangle 2 pixels on each side bigger. I try to find a solution
if (GetFlatStyle()) {
// Draw toolbar's edges in a flat mode like Office
// Very special thanks to Jo Parrello
//
TRect crect = ThisClientRect;
crect.Inflate(-2, -2);
TUIBorder border(crect, TUIBorder::TEdge(TUIBorder::RaisedInner),
TUIBorder::Rect);
border.Paint(MyDC);
}
else {
*/
TUIBorder(ThisClientRect, TUIBorder::EdgeEtched, TUIBorder::Left).Paint(MyDC);
TUIBorder(ThisClientRect, TUIBorder::EdgeEtched, TUIBorder::Right).Paint(MyDC);
TUIBorder(ThisClientRect, TUIBorder::EdgeEtched, TUIBorder::Bottom).Paint(MyDC);
TUIBorder(ThisClientRect, TUIBorder::EdgeEtched, TUIBorder::Top).Paint(MyDC);
// }
//D: I think I must draw the space beetween gadgets and border here.
// Because in some situations there are some rest pixels in customize mode
// Draw the gripper
//
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
if (harbor->IsDrawGripper()) {
TRect gripperRect = ThisClientRect;
if (Slip->GetLocation() == alTop || Slip->GetLocation() == alBottom) {
gripperRect.Inflate(-GripperDistSize-1, -GripperDistSize);
gripperRect.right = gripperRect.left + GripperDistSize;
Draw3dRect(MyDC, gripperRect, TColor::Sys3dHilight, TColor::Sys3dShadow);
gripperRect.Offset(GripperDistSize, 0);
Draw3dRect(MyDC, gripperRect, TColor::Sys3dHilight, TColor::Sys3dShadow);
}
else {
gripperRect.Inflate(-GripperDistSize, -GripperDistSize-1);
gripperRect.bottom = gripperRect.top + GripperDistSize;
Draw3dRect(MyDC, gripperRect, TColor::Sys3dHilight, TColor::Sys3dShadow);
gripperRect.Offset(0, GripperDistSize);
Draw3dRect(MyDC, gripperRect, TColor::Sys3dHilight, TColor::Sys3dShadow);
}
}
// We draw the divider gadgets if toolbar is vertical or horizontal.
// Very special thanks to Jo Parrello
//
if (GetFlatStyle()) {
if (Slip->GetLocation() == alTop || Slip->GetLocation() == alBottom)
DrawVerticalDividers(MyDC, FirstGadget(), 0, Margins.Top,
ThisClientRect.bottom - Margins.Bottom);
else if (Slip->GetLocation() == alLeft || Slip->GetLocation() == alRight)
DrawHorizontalDividers(MyDC, FirstGadget(), 0, Margins.Left,
ThisClientRect.right - Margins.Right);
}
}
}
void TDockableControlBarEx::ToggleSlip()
// toggles it from docked position to floating position and back again
{
// Check that we're parented to the right window. In OLE server situation,
// the toolbar could have been reparented to another HWND [i.e. container's
// window]
//
TWindow* w = GetParentO();
if (w && ::GetParent(*this) != w->GetHandle())
return;
StorePosAndLocation();
bool ToggleSlips = false;
TAbsLocation setLoc;
if (TYPESAFE_DOWNCAST(w, TEdgeSlipEx) && Location != alNone) {
// Change from EdgeSlip to FloatingSlip
//
setLoc = alNone;
ToggleLocation = Location;
ToggleSlips = true;
}
else {
if (TYPESAFE_DOWNCAST(w, TFloatingSlipEx) &&
Location == alNone && ToggleLocation != alNone) {
// Change from FloatingSlip to EdgeSlip
//
setLoc = ToggleLocation;
ToggleLocation = alNone;
ToggleSlips = true;
}
}
if (ToggleSlips) {
// get and set the rect before toggle the slips
//
TRect setRect;
bool noDefaultPos = false;
if (ToggleRect == TRect(-1,-1,-1,-1))
noDefaultPos = true;
else
setRect = ToggleRect;
ToggleRect = Rect;
THarbor* harbor = GetHarbor();
if (harbor) {
harbor->Remove(*this);
TDockingSlip* dslip = harbor->Insert(*this,
setLoc, noDefaultPos ? 0 : &setRect.TopLeft());
// If the inserted docking slip is floating one with location alNone
// compute the real size. Thanks to Jo Parrello
//
TFloatingSlipEx* flslip = TYPESAFE_DOWNCAST(dslip, TFloatingSlipEx);
TSize size(setRect.Width(), setRect.Height());
if (flslip && (setLoc == alNone)) {
TSize newsize = flslip->ComputeSize(alNone, &size);
flslip->Layout(alNone, &newsize);
}
}
}
}
TGadget* TDockableControlBarEx::RemoveEx(TGadget* gadget, bool del)
{
TGadget* gad = 0;
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
CheckOnNeighbourSeparatorGadgets(gadget);
if (harbor && harbor->GetCustGadget() == gadget)
harbor->SetCustGadgetNull();
if (del) {
delete Remove(*gadget);
}
else
gad = Remove(*gadget);
LayoutSession();
CheckOnLastGadget();
Invalidate();
UpdateWindow();
return gad;
}
void TDockableControlBarEx::InsertEx(TGadget& gadget, TPlacement placement,
TGadget* sibling)
{
Insert(gadget, placement, sibling);
// If the sibling gadget is the invisible gadget remove and delete them
//
if (sibling && sibling->GetId() == CM_INVISIBLEGADGET) {
delete Remove(*sibling);
}
LayoutSession();
}
void TDockableControlBarEx::SetupWindow()
{
TDockableControlBar::SetupWindow();
StorePosAndLocation();
}
void TDockableControlBarEx::RemoveAllGadgets()
// Remove all gadgets from toolbar
{
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
TGadget* gadget = FirstGadget();
while (gadget) {
TGadget* delGadget = gadget;
gadget = gadget->NextGadget();
if (harbor && harbor->GetCustGadget() == delGadget)
harbor->SetCustGadgetNull();
delete Remove(*delGadget);
}
}
TGadget* TDockableControlBarEx::PrevGadget(TGadget* gadget) const
// Returns previous gadget
{
PRECONDITION(gadget);
TGadget* prevGadget = FirstGadget();
while (prevGadget) {
if (prevGadget->NextGadget() == gadget)
return prevGadget;
prevGadget = prevGadget->NextGadget();
}
return 0;
}
TGadget* TDockableControlBarEx::GetAndSetCustomizeGadget(TPoint& point)
// Get the actual customize gadget and set them in harbor or returns 0
{
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
if (harbor && harbor->IsCustomizeMode()) {
TGadget* gad = Capture ? Capture : GadgetFromPoint(point);
if (gad) {
harbor->SetCustGadget(this, gad);
return gad;
}
}
return 0;
}
void TDockableControlBarEx::CheckOnLastGadget()
// If the controlbar has no visible gadgets a "invisible" gadget is inserted
{
bool novisible = true;
TGadget* gad = FirstGadget();
while (gad) {
if (gad->IsVisible()) {
novisible = false;
break;
}
gad = gad->NextGadget();
}
if (novisible) {
TGadget* gadget = GadgetDescriptors->ConstructGadget(CM_INVISIBLEGADGET);
CHECK(gadget);
if (gadget)
Insert(*gadget);
LayoutSession();
}
}
void TDockableControlBarEx::CheckOnNeighbourSeparatorGadgets(TGadget* actGadget)
// Check the neighbours gadgets of actGadget. If they are seperator gadgets
// that are not used they will be removed
{
PRECONDITION(actGadget);
TGadgetDesc* desc;
bool isValid, nextGadgetIsSeparator, beforeGadgetIsSeparator;
isValid = nextGadgetIsSeparator = beforeGadgetIsSeparator = false;
TGadget* beforeGadget = 0;
TGadget* nextGadget = actGadget->NextGadget();
TGadget* gad = FirstGadget();
while(gad) {
if (gad == actGadget)
isValid = true;
if (gad->NextGadget() == actGadget)
beforeGadget = gad;
gad = gad->NextGadget();
}
if (isValid) {
if (nextGadget) {
desc = GadgetDescriptors->Find(nextGadget->GetId());
if (desc && desc->Type == TGadgetDesc::SeparatorGadgetDesc)
nextGadgetIsSeparator = true;
}
if (beforeGadget) {
desc = GadgetDescriptors->Find(beforeGadget->GetId());
if (desc && desc->Type == TGadgetDesc::SeparatorGadgetDesc)
beforeGadgetIsSeparator = true;
}
if ((nextGadget == 0 || nextGadgetIsSeparator) &&
beforeGadget && beforeGadgetIsSeparator) {
delete Remove(*beforeGadget);
}
else if ((beforeGadget == 0 || beforeGadgetIsSeparator) &&
nextGadget && nextGadgetIsSeparator) {
delete Remove(*nextGadget);
}
}
}
TGadget* TDockableControlBarEx::GetDragDropGadget(const TPoint& pt,
TPoint& p1, TPoint& p2, TPlacement& placement)
{
bool horizontal = true;
TDockingSlip* Slip = TYPESAFE_DOWNCAST(Parent, TDockingSlip);
if (Slip) {
if (Slip->GetLocation() == alLeft || Slip->GetLocation() == alRight)
horizontal = false;
}
// Similar to GadgetFromPoint, but this get non visible gadgets too
//
TGadget* gadget = FirstGadget();
TGadget* gadbefore = 0;
TRect gadbounds;
int refbottom = 0;
int maxright = 0;
while(gadget) {
gadbounds = gadget->GetBounds();
refbottom = std::max(refbottom, (int)gadbounds.bottom);
maxright = std::max(maxright, (int)gadbounds.right);
if (pt.y <= refbottom) {
if (gadget->IsEndOfRow() && pt.x <= maxright)
break;
if (pt.x <= gadbounds.right)
break;
}
gadbefore = gadget;
gadget = gadget->NextGadget();
}
if (gadget == 0) {
if (gadbefore && gadbefore->GetId() != 0)
gadget = gadbefore;
}
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
if (gadget && harbor) {
int midx = gadbounds.left+(gadbounds.right-gadbounds.left)/2;
int midy = gadbounds.top+(gadbounds.bottom-gadbounds.top)/2;
if (gadget->GetId() == 0) {
TRect bounds;
if (horizontal) {
if (pt.x > midx && gadget->IsEndOfRow() == false) {
if (gadget->NextGadget()) {
placement = After;
bounds = gadget->NextGadget()->GetBounds();
p1.x = p2.x = bounds.left;
p1.y = bounds.top;
p2.y = bounds.bottom;
return gadget;
}
}
else {
if (gadbefore) {
placement = Before;
bounds = gadbefore->GetBounds();
p1.x = p2.x = bounds.right;
p1.y = bounds.top;
p2.y = bounds.bottom;
return gadget;
}
}
}
else {
if (pt.y > midy) {
if (gadget->NextGadget()) {
placement = After;
bounds = gadget->NextGadget()->GetBounds();
p1.y = p2.y = bounds.top;
p1.x = bounds.left;
p2.x = bounds.right;
return gadget;
}
}
else {
if (gadbefore) {
placement = Before;
bounds = gadbefore->GetBounds();
p1.y = p2.y = bounds.bottom;
p1.x = bounds.left;
p2.x = bounds.right;
return gadget;
}
}
}
}
else {
if (horizontal) {
if (pt.x > midx) {
placement = After;
p1.x = p2.x = gadbounds.right;
}
else {
placement = Before;
p1.x = p2.x = gadbounds.left;
}
p1.y = gadbounds.top;
p2.y = gadbounds.bottom;
}
else {
if (pt.y > midy) {
placement = After;
p1.y = p2.y = gadbounds.bottom;
}
else {
placement = Before;
p1.y = p2.y = gadbounds.top;
}
p1.x = gadbounds.left;
p2.x = gadbounds.right;
}
return gadget;
}
}
return 0;
}
#if (OWLInternalVersion >= 0x06000000L)
void TDockableControlBarEx::EvOwlWindowDocked(uint /*pos*/, const TDockingSlip& /*slip*/)
{
// OWL60: Don't create FlatHandleGadget because, gripper is drawn in this class
}
#endif
void TDockableControlBarEx::EvLButtonDown(uint modKeys, const TPoint& point)
{
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
#ifdef CTXHELP
TCtxHelpFileManager* ctxHelpM = TYPESAFE_DOWNCAST(GetApplication(),
TCtxHelpFileManager);
if (ctxHelpM && ctxHelpM->IsContextHelp()) {
TGadget* gadget = Capture ? Capture : GadgetFromPoint(point);
TGadgetDesc* desc = 0;
if (gadget)
desc = GadgetDescriptors->Find(gadget->GetId());
if (desc && desc->Attr & GADG_HASHELP) {
// click in a gadget: call help with gadget id > 0
//
int HelpId = gadget->GetId();
if (HelpId > 0)
ctxHelpM->WinHelp(HelpId);
}
else {
// click in a seperator or the gripper: call help with Attr.Id != 0
//
if (HasHelp)
ctxHelpM->WinHelp(Attr.Id);
}
}
else
#endif
{
TPoint p(point);
if (GetAndSetCustomizeGadget(p)) {
harbor->GadgetDraggingBegin();
return;
}
else
TDockableControlBar::EvLButtonDown(modKeys, point);
}
}
void TDockableControlBarEx::EvLButtonDblClk(uint modKeys, const TPoint& point)
{
TPoint p(point);
if (GetAndSetCustomizeGadget(p))
return;
// Check that the DBLClk position is in a visible enabled Gadget
// an the Dockable flag is set true
//
if (ShouldBeginDrag(p)) {
ToggleSlip();
return;
}
TDockableControlBar::EvLButtonDblClk(modKeys, point);
}
void TDockableControlBarEx::EvRButtonDown(uint modKeys, const TPoint& point)
{
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
TPoint p(point);
if (GetAndSetCustomizeGadget(p)) {
POINT pp;
::GetCursorPos(&pp);
HMENU hMenu = harbor->GetGadgetMenu();
if (hMenu && ::GetSubMenu(hMenu, 0)) {
::TrackPopupMenu(::GetSubMenu(hMenu, 0),
TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
pp.x, pp.y, 0, GetApplication()->GetMainWindow()->GetHandle(), 0);
::DestroyMenu(hMenu);
}
}
TDockableControlBar::EvRButtonDown(modKeys, point);
}
void TDockableControlBarEx::StorePosAndLocation()
{
// get the actual absolute location of the toolbar
//
TDockingSlip* dockingSlip = TYPESAFE_DOWNCAST(Parent, TDockingSlip);
TAbsLocation actLocation = (dockingSlip ? dockingSlip->GetLocation() : alNone);
// Location changed
//
if (actLocation != Location) {
// if the actual location is equal with the saved toggled slip location
//
if ((actLocation == alNone && ToggleLocation == alNone) ||
(actLocation != alNone && ToggleLocation != alNone)) {
ToggleRect = Rect;
ToggleLocation = Location;
}
Location = actLocation;
}
// get the rectangle of the toolbar
//
TFloatingSlipEx* floatingSlip = TYPESAFE_DOWNCAST(Parent, TFloatingSlipEx);
if (floatingSlip)
floatingSlip->GetWindowRect(Rect);
else
GetRect(Rect);
}
void TDockableControlBarEx::LoadSettings(THarborManagement& harborMan)
{
int bVisible, bDefault, iLocation, iGadgetCount, id;
TRect rect;
uint32 size = sizeof(int);
TCHAR title[255];
uint32 sizeTitle = sizeof(title);
TCHAR idstr[80];
wsprintf(idstr, TEXT("%s%d"), ToolbarSection, Attr.Id);
owl::tstring toolbarstr = harborMan.GetRegistryName() + owl::tstring(_T("\\")) +
ToolbarsSection + owl::tstring(_T("\\")) + idstr;
TRegKey regKey(TRegKey::GetCurrentUser(), toolbarstr.c_str());
if (regKey.QueryValue(NameSection, 0, (uint8*)(TCHAR*)title, &sizeTitle) !=
ERROR_SUCCESS) return;
if (regKey.QueryValue(VisibleSection, 0, (uint8*)&bVisible, &size) !=
ERROR_SUCCESS) return;
if (regKey.QueryValue(DefaultSection, 0, (uint8*)&bDefault, &size) !=
ERROR_SUCCESS) return;
if (regKey.QueryValue(LocationSection, 0, (uint8*)&iLocation, &size) !=
ERROR_SUCCESS) return;
if (regKey.QueryValue(LeftSection, 0, (uint8*)&rect.left, &size) !=
ERROR_SUCCESS) return;
if (regKey.QueryValue(TopSection, 0, (uint8*)&rect.top, &size) !=
ERROR_SUCCESS) return;
if (regKey.QueryValue(RightSection, 0, (uint8*)&rect.right, &size) !=
ERROR_SUCCESS) return;
if (regKey.QueryValue(BottomSection, 0, (uint8*)&rect.bottom, &size) !=
ERROR_SUCCESS) return;
if (regKey.QueryValue(GadgetCountSection, 0, (uint8*)&iGadgetCount, &size) !=
ERROR_SUCCESS) return;
RemoveAllGadgets();
for (int i=1; i<=iGadgetCount; i++) {
TCHAR posstr[80];
wsprintf(posstr, TEXT("%d"), i);
TRegKey regKey(TRegKey::GetCurrentUser(), toolbarstr.c_str());
if(regKey.QueryValue(posstr, 0, (uint8*)&id, &size) != ERROR_SUCCESS)
return;
TGadget* gadget = GadgetDescriptors->ConstructGadget(id);
CHECK(gadget);
if (gadget) {
Insert(*gadget);
// Load Button Appearance values
//
TButtonTextGadgetEx* btg = TYPESAFE_DOWNCAST(gadget, TButtonTextGadgetEx);
if (btg && THarborManagement::GetInternVersion() >= 2) {
bool foundBtnStr = false;
owl::tstring tbBtnStr = toolbarstr+owl::tstring(_T("\\"))+ButtonSection+posstr;
TRegKey tbBtnRegKey(TRegKey::GetCurrentUser(), tbBtnStr.c_str());
int bmpId;
if (tbBtnRegKey.QueryValue(
BmpIdSection, 0, (uint8*)&bmpId, &size) == ERROR_SUCCESS) {
btg->SetResId(bmpId);
foundBtnStr = true;
}
int dispType;
if (tbBtnRegKey.QueryValue(
DisplayTypeSection, 0, (uint8*)&dispType, &size) == ERROR_SUCCESS) {
btg->SetDisplayType((TDisplayType)dispType);
foundBtnStr = true;
}
TCHAR text[255];
uint32 sizeText = sizeof(text);
if (tbBtnRegKey.QueryValue(
TextSection, 0, (uint8*)(TCHAR*)text, &sizeText) == ERROR_SUCCESS) {
btg->SetCommandText(owl::tstring(text));
foundBtnStr = true;
}
if (foundBtnStr == false) {
TRegKey::GetCurrentUser().NukeKey(tbBtnStr.c_str());
}
}
}
}
CheckOnLastGadget();
if ((bool)bDefault == false)
SetCaption(title);
THarbor* harbor = GetHarbor();
if (harbor) {
harbor->Remove(*this);
TDockingSlip* dslip = harbor->Insert(
*this, (TAbsLocation)iLocation, &rect.TopLeft());
// If the inserted docking slip is floating one with location alNone
// compute the real size. Thanks to Jo Parrello
//
TFloatingSlipEx* flslip = TYPESAFE_DOWNCAST(dslip, TFloatingSlipEx);
TSize size(rect.Width(), rect.Height());
if (flslip && ((TAbsLocation)iLocation == alNone)) {
TSize newsize = flslip->ComputeSize(alNone, &size);
flslip->Layout(alNone, &newsize);
}
}
if (IsWindowVisible() != (bool)bVisible)
{
if ((bool)bVisible)
Show();
else
Hide();
}
}
void TDockableControlBarEx::SaveSettings(THarborManagement& harborMan)
{
// If we are visible save the location and position
//
if (IsWindowVisible())
StorePosAndLocation();
TCHAR idstr[80];
wsprintf(idstr, TEXT("%s%d"), ToolbarSection, Attr.Id);
int len = GetWindowTextLength();
TCHAR* title = new TCHAR[len+1]; // +1 for 0 terminator
if (len > 0)
GetWindowText(title, len+1);
else
*title=0;
owl::tstring toolbarstr = harborMan.GetRegistryName() + owl::tstring(_T("\\")) +
ToolbarsSection + owl::tstring(_T("\\")) + idstr;
TRegKey toolbarRegKey(TRegKey::GetCurrentUser(), toolbarstr.c_str());
toolbarRegKey.SetValue(NameSection, REG_SZ, (const uint8*)title, len);
toolbarRegKey.SetValue(VisibleSection, (int)IsWindowVisible());
toolbarRegKey.SetValue(DefaultSection, (int)Default);
toolbarRegKey.SetValue(LocationSection, Location);
toolbarRegKey.SetValue(LeftSection, Rect.left);
toolbarRegKey.SetValue(TopSection, Rect.top);
toolbarRegKey.SetValue(RightSection, Rect.right);
toolbarRegKey.SetValue(BottomSection, Rect.bottom);
toolbarRegKey.SetValue(GadgetCountSection, NumGadgets);
delete[] title;
// Save Gadgets
int i = 1;
TGadget* gadget = FirstGadget();
while (gadget) {
TCHAR posstr[80];
wsprintf(posstr, TEXT("%d"), i);
TRegKey toolbarRegKey(TRegKey::GetCurrentUser(), toolbarstr.c_str());
toolbarRegKey.SetValue(posstr, gadget->GetId());
// Save Button Appearance values
//
TButtonTextGadgetEx* btg = TYPESAFE_DOWNCAST(gadget, TButtonTextGadgetEx);
if (btg && btg->HasDefaultValues() == false) {
owl::tstring tbBtnStr = toolbarstr + owl::tstring(_T("\\")) + ButtonSection + posstr;
TRegKey tbBtnRegKey(TRegKey::GetCurrentUser(), tbBtnStr.c_str());
TGadgetDesc* desc = GadgetDescriptors->Find(gadget->GetId());
TButtonTextGadgetDesc* btgd = TYPESAFE_DOWNCAST(desc,
TButtonTextGadgetDesc);
uint32 rid = btg->GetResId().GetInt();
if (btgd && btgd->BmpResId != rid) {
tbBtnRegKey.SetValue(BmpIdSection, rid);
}
if (btg->GetDisplayType() != btgd->DispType) {
tbBtnRegKey.SetValue(DisplayTypeSection, btg->GetDisplayType());
}
if (btg->GetCommandText() != btgd->Text) {
owl::tstring commandtext = btg->GetCommandText();
tbBtnRegKey.SetValue(TextSection, REG_SZ,
(const uint8*)commandtext.c_str(), static_cast<uint32>(commandtext.size()));
}
}
i++;
gadget = gadget->NextGadget();
}
}
void TDockableControlBarEx::CmOk()
{
::SendMessage((HWND)GetFocus(), WM_KEYDOWN, VK_RETURN, MAKELPARAM(1, 0));
}
void TDockableControlBarEx::CmCancel()
{
::SendMessage((HWND)GetFocus(), WM_KEYDOWN, VK_ESCAPE, MAKELPARAM(1, 0));
}
void TDockableControlBarEx::DrawVerticalDividers(TDC& dc, TGadget* firstgadg,
TGadget* lastgadg, int miny, int maxy, bool skipEndOfRowCheck)
// Draw vertical dividers for SeparatorGadgetEx.
// Very special thanks to Jo Parrello
{
TGadget* currentgadg = firstgadg;
bool canCont = true;
while (canCont) {
if (currentgadg == lastgadg)
canCont = false;
if (currentgadg) {
if (currentgadg->GetId() == 0 && currentgadg->NextGadget() &&
(skipEndOfRowCheck || currentgadg->IsEndOfRow() == false)) {
TRect boundrect = currentgadg->GetBounds();
int midx = (boundrect.right - boundrect.left) / 2 + boundrect.left;
TPen pen(TColor::Sys3dShadow);
dc.SelectObject(pen);
dc.MoveTo(midx, miny);
dc.LineTo(midx, maxy);
dc.RestorePen();
TPen pen2(TColor::Sys3dHilight);
dc.SelectObject(pen2);
dc.MoveTo(midx + 1, miny);
dc.LineTo(midx + 1, maxy);
dc.RestorePen();
}
currentgadg = currentgadg->NextGadget();
}
}
}
void TDockableControlBarEx::DrawHorizontalDividers(TDC& dc, TGadget* firstgadg,
TGadget* lastgadg, int minx, int maxx)
// Draw horizontal dividers for SeparatorGadgetEx.
// Very special thanks to Jo Parello
{
TGadget* currentgadg = firstgadg;
bool canCont = true;
while (canCont) {
if (currentgadg == lastgadg)
canCont = false;
if (currentgadg) {
if (currentgadg->GetId() == 0 && currentgadg->NextGadget()) {
TRect boundrect = currentgadg->GetBounds();
int midy = (boundrect.bottom - boundrect.top) / 2 + boundrect.top;
TPen pen(TColor::Sys3dShadow);
dc.SelectObject(pen);
dc.MoveTo(minx, midy);
dc.LineTo(maxx, midy);
dc.RestorePen();
TPen pen2(TColor::Sys3dHilight);
dc.SelectObject(pen2);
dc.MoveTo(minx, midy + 1);
dc.LineTo(maxx, midy + 1);
dc.RestorePen();
}
currentgadg = currentgadg->NextGadget();
}
}
}
void TDockableControlBarEx::DrawCustomizeRect(TDC& dc, TRect& rect)
{
TPen pen (TColor::Black, 2);
dc.SelectObject(pen);
dc.SelectStockObject(NULL_BRUSH);
dc.Rectangle(rect);
dc.RestoreBrush();
dc.RestorePen();
}
// ****************** TGridItem ***********************************************
TGridItem::TGridItem(TWindow* Window, TAbsLocation Location)
{
// Initialise data
Dockable = Window;
TRect WindowRect = Dockable->GetWindowRect();
// All the workings are to be in client coordinates of the edge slip
::MapWindowPoints(0, Window->GetParentH(), (TPoint*)&WindowRect, 2);
if (Location == alTop || Location == alBottom) {
LeftEdge = WindowRect.left;
TopEdge = WindowRect.top; // Only needed to find grid line
Width = WindowRect.Width();
Height = WindowRect.Height();
}
else {
// Transpose the coordinates for internal calculations
LeftEdge = WindowRect.top;
TopEdge = WindowRect.left; // Only needed to find grid line
Width = WindowRect.Height();
Height = WindowRect.Width();
}
GridLine = 0;
NextItem = 0;
PrevItem = 0;
}
void TGridItem::ArrangeLeft()
// Recursive function tries to push dockables which are overlapped to the left.
// It is not possible to push them beyond the left edge of the slip and this
// leads to packing against the left edge.
{
// Can only be called where there is a following item
// Push this item to the immediate left of the following item
LeftEdge = NextItem->LeftEdge - Width + 2;
if (PrevItem) {
if(LeftEdge < PrevItem->LeftEdge + PrevItem->Width - 2) {
// Where this grid item overlaps the previous one, TRY to push the
// previous one leftwards
PrevItem->ArrangeLeft(); // This function recurses
if(LeftEdge < PrevItem->LeftEdge + PrevItem->Width - 2) {
// Where the previous item was prevented from moving far enough,
// push this item to the right of it
LeftEdge = PrevItem->LeftEdge + PrevItem->Width - 2;
}
}
}
else if (LeftEdge < -2) {
// Where this is the first grid item, ensure it does not stick off the left
LeftEdge = -2;
}
}
void TGridItem::ArrangeRight(bool Limit, int SlipWidth)
// Recursive function tries to push dockables which are overlapped to the right.
// Where Limit is true, it is not possible to push them beyond the right edge of
// the slip and this leads to packing against the right edge. If Limit is false,
// items are allowed to stick out past the right edge, or even be pushed wholly
// beyond it.
{
// Can only be called where there is a preceding item
// Push this item to the immediate left of the preceding item
LeftEdge = PrevItem->LeftEdge + PrevItem->Width - 2;
if (NextItem) {
if(LeftEdge + Width - 2 > NextItem->LeftEdge) {
// Where this grid item overlaps the next one, TRY to push the
// next one rightwards
NextItem->ArrangeRight(Limit, SlipWidth); // This function recurses
if(LeftEdge + Width - 2 > NextItem->LeftEdge) {
// Where the next item was prevented from moving far enough,
// push this item to the left of it
LeftEdge = NextItem->LeftEdge - Width + 2;
}
}
}
else if (LeftEdge + Width - 2 > SlipWidth && Limit)
{
// Where this is the last grid item, ensure it does not stick off the right
LeftEdge = SlipWidth - Width + 2;
}
}
void TGridItem::ArrangePrecedingItems()
// Function called to move overlapped dockabes to the left of an anchor (this).
// The anchor is moved to the right if necessary.
{
if (PrevItem) {
if(LeftEdge < PrevItem->LeftEdge + PrevItem->Width - 2) {
// Where this grid item overlaps the previous one, TRY to push the
// previous one leftwards
PrevItem->ArrangeLeft(); // This function recurses
if(LeftEdge < PrevItem->LeftEdge + PrevItem->Width - 2) {
// Where the previous item was prevented from moving far enough,
// push this item to the right of it
LeftEdge = PrevItem->LeftEdge + PrevItem->Width - 2;
}
}
}
else if (LeftEdge < -2) {
// Where this is the first grid item, ensure it does not stick off the left
LeftEdge = -2;
}
}
void TGridItem::ArrangeFollowingItems(int SlipWidth)
// Function called to move overlapped dockabes to the right of an anchor (this).
// The function first tries to ensure packing against the right edge, but
// tries again without packing if the anchor cannot move far enough leftwards.
// The anchor is moved to the left or right if necessary.
{
if (NextItem) {
if(LeftEdge + Width - 2 > NextItem->LeftEdge) {
// Where this grid item overlaps the next one, TRY to push the
// next one rightwards
NextItem->ArrangeRight(true, SlipWidth); // This function recurses
if(LeftEdge + Width - 2 > NextItem->LeftEdge) {
// Where the next item was prevented from moving far enough,
// TRY to push this item to the left of it
ArrangeLeft();
if (LeftEdge + Width - 2 > NextItem->LeftEdge) {
// Where this grid item still overlaps the next one, push the next one
// rightwards
NextItem->ArrangeRight(false, SlipWidth); // This function recurses
// Is this part superfluous/counterproductive?
}
}
}
}
else if (LeftEdge + Width - 2 > SlipWidth) {
// Where this is the last grid item, ensure it does not stick off the right
LeftEdge = SlipWidth - Width + 2;
// This may result in the item sticking off the right edge
ArrangePrecedingItems();
}
}
// ****************** TGridLine ***********************************************
TGridLine::TGridLine(uint32 Top)
{
TopEdge = Top;
Height = 0;
FirstItem = 0;
NextLine = 0;
PrevLine = 0;
}
TGridLine::~TGridLine()
{
// CodeGuard loves me!
TGridItem* Temp;
while (FirstItem)
{
Temp = FirstItem->NextItem;
delete FirstItem;
FirstItem = Temp;
}
}
TGridItem* TGridLine::LayoutGridItems(TGridItem* Anchor, int SlipWidth)
{
// We should check that Anchor is in this gridline
if (!Anchor)
Anchor = FirstItem;
// Confirm that the item is in the list
TGridItem* IterItem = FirstItem;
while (IterItem && IterItem != Anchor) {
IterItem = IterItem->NextItem;
}
// Only do something if the item is in the list
if (IterItem) {
// Layout the dockables on this gridline so there are no overlaps
Anchor->ArrangePrecedingItems();
Anchor->ArrangeFollowingItems(SlipWidth);
// Return a pointer to the first grid item pushed wholly off the right edge
// of the slip
TGridItem* FirstExcess;
TGridItem* Temp = FirstItem;
while (Temp && Temp->LeftEdge < SlipWidth)
Temp = Temp->NextItem;
FirstExcess = Temp;
return FirstExcess;
}
return 0;
}
void TGridLine::ComputeHeight()
{
int TempHeight = 0;
// Height of the tallest grid item
TGridItem* TempItem = FirstItem;
while (TempItem)
{
TempHeight = max(TempItem->Height, TempHeight);
TempItem = TempItem->NextItem;
}
Height = TempHeight;
}
void TGridLine::InsertGridItem(TGridItem* NewItem)
{
if (!FirstItem)
FirstItem = NewItem;
else {
TGridItem* InsertAfter = 0;
TGridItem* InsertBefore = 0;
TGridItem* IterNext = FirstItem;
// Loop to sort position by LeftEdge
while (IterNext) {
if (NewItem->LeftEdge > IterNext->LeftEdge)
InsertAfter = IterNext;
else if (!InsertBefore)
InsertBefore = IterNext;
IterNext = IterNext->NextItem;
}
if (InsertAfter) {
NewItem->PrevItem = InsertAfter;
InsertAfter->NextItem = NewItem;
}
else
FirstItem = NewItem;
if (InsertBefore) {
NewItem->NextItem = InsertBefore;
InsertBefore->PrevItem = NewItem;
}
else
NewItem->NextItem = 0; // Make sure the list is terminated properly
}
NewItem->GridLine = this;
NewItem->TopEdge = TopEdge;
ComputeHeight(); // Can't remember why I added this...
}
void TGridLine::RemoveGridItem(TGridItem* NewItem)
{
// Confirm that the item is in the list
TGridItem* IterItem = FirstItem;
while (IterItem && IterItem != NewItem)
IterItem = IterItem->NextItem;
// Only do something if the item is in the list
if (IterItem) {
if (NewItem->PrevItem)
NewItem->PrevItem->NextItem = NewItem->NextItem;
else {
// The first item is being removed
// Omitting this line caused the item in
// question to be deleted twice - Bang!
FirstItem = NewItem->NextItem;
}
if (NewItem->NextItem)
NewItem->NextItem->PrevItem = NewItem->PrevItem;
NewItem->PrevItem = 0;
//NewItem->NextItem = 0; I'll be needing this later
NewItem->GridLine = 0;
}
}
// ****************** TGridLayout *********************************************
TGridLayout::TGridLayout(TAbsLocation Loc)
{
FirstLine = 0;
Parent = 0;
Dockable = 0;
DockableItem = 0;
Location = Loc;
}
void TGridLayout::CreateLists(TWindow* parent, TWindow* dockable)
{
Parent = parent; // The edge slip
Dockable = dockable; // The just dropped dockable child of the edge slip
TWindow* FirstChild = Parent->GetFirstChild();
if (FirstChild) {
TWindow* ChildWindow = FirstChild;
do {
TGridItem* NewItem = new TGridItem(ChildWindow, Location);
if (ChildWindow == Dockable) {
// Don't insert the current item into the linked lists yet
DockableItem = NewItem;
}
else {
// Insert the item into the linked lists
InsertGridItem(NewItem);
}
ChildWindow = ChildWindow->Next();
}
while (ChildWindow != FirstChild); // Does Next() cycle?
}
}
TGridLine* TGridLayout::InsertDockableItem()
{
// Need some logic to determine which existing grid line it should be in
// or whether a new grid line should be inserted
if (!DockableItem)
return 0;
TGridLine* DockableLine;
if (FirstLine) {
// Find first grid line with TopEdge greater than that of the new item
TGridLine* IterPrev = 0;
TGridLine* IterNext = FirstLine;
while (IterNext && IterNext->TopEdge < DockableItem->TopEdge) {
IterPrev = IterNext;
IterNext = IterNext->NextLine;
}
// IterNext points to the first line with TopEdge >= DockableItem->TopEdge
// Find the comparator
int CompareTop;
if (IterNext)
CompareTop = IterNext->TopEdge;
else
CompareTop = IterPrev->TopEdge + IterPrev->Height - 2;
// Decide which grid line to use based on degree of overlap of the new
// dockable with the existing gridlines.
// I'm sure this could be done lots better, but it does the trick quite
// well.
if (CompareTop < DockableItem->TopEdge + (DockableItem->Height - 1) / 3) {
// Insert it on the next line - this will force a new line if no NextLine
DockableItem->TopEdge = CompareTop;
DockableLine = InsertGridItem(DockableItem);
}
else if (CompareTop < DockableItem->TopEdge + (2 * (DockableItem->Height - 1)) / 3) {
// Force a new line to be inserted below
DockableItem->TopEdge = CompareTop - 1;
DockableLine = InsertGridItem(DockableItem);
}
else if (IterPrev) {
CompareTop = IterPrev->TopEdge + IterPrev->Height - 2;
if (CompareTop > DockableItem->TopEdge + (2 * (DockableItem->Height - 1)) / 3) {
// Pull onto previous line
DockableItem->TopEdge = IterPrev->TopEdge;
DockableLine = InsertGridItem(DockableItem);
}
else {
// Force a new line to be inserted below previous line
DockableItem->TopEdge = IterPrev->TopEdge + 1;
DockableLine = InsertGridItem(DockableItem);
}
}
else {
// Force a new line to be inserted above
DockableItem->TopEdge = CompareTop - 1;
DockableLine = InsertGridItem(DockableItem);
}
}
else {
DockableItem->TopEdge = -2;
DockableLine = InsertGridItem(DockableItem);
}
return DockableLine;
}
TGridLayout::~TGridLayout()
{
// CodeGuard loves me!
TGridLine* Temp;
while (FirstLine) {
Temp = FirstLine->NextLine;
delete FirstLine;
FirstLine = Temp;
}
}
int TGridLayout::LayoutGridLines()
{
// Vertical layout - sets top edge mainly
int CumulativeTop = -2;
TGridLine* IterLine = FirstLine;
while (IterLine) {
IterLine->TopEdge = CumulativeTop;
IterLine->ComputeHeight();
CumulativeTop = CumulativeTop + IterLine->Height - 2;
IterLine = IterLine->NextLine;
}
// Return the required height/width of the slip, taking all the gridlines
// into consideration. The sides are slightly different to the top and
// bottom because the etched edge slip borders are oriented differently
// relative to the gridlines. CumulativeTop should not be negative - it
// screws up the 3D client edge of the main client window.
if (Location == alTop || Location == alBottom || CumulativeTop == -2)
CumulativeTop += 2;
return CumulativeTop;
}
void TGridLayout::LayoutDockables()
// This function iterates through the grid items and implements the results
// of all our calculations.
{
TPoint ItemPoint;
TPoint DockPoint;
// Iterate through the grid lines
TGridLine* IterLine = FirstLine;
while (IterLine) {
// Iterate through the grid items in the current grid line
TGridItem* IterItem = IterLine->FirstItem;
while (IterItem) {
// Do something to the dockables
// Make a point from the left of the dockable and the top of its grid line
if (Location == alTop || Location == alBottom) {
ItemPoint = TPoint(IterItem->LeftEdge, IterLine->TopEdge);
}
else {
// Swap the points back over for the real window
ItemPoint = TPoint(IterLine->TopEdge, IterItem->LeftEdge);
}
// Find the current top left point of the dockable
DockPoint = TPoint(IterItem->Dockable->GetWindowRect().TopLeft());
::MapWindowPoints(0, *Parent, &DockPoint, 1);
// Compare with the current top left point of the dockable
if (ItemPoint != DockPoint) {
IterItem->Dockable->SetWindowPos(0, ItemPoint.x, ItemPoint.y, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
}
IterItem = IterItem->NextItem;
}
IterLine = IterLine->NextLine;
}
}
void TGridLayout::InsertGridLine(TGridLine* NewLine)
{
if (!FirstLine)
FirstLine = NewLine;
else {
TGridLine* InsertAfter = 0;
TGridLine* InsertBefore = 0;
TGridLine* IterNext = FirstLine;
// Loop to sort position by LeftEdge
while (IterNext) {
if (NewLine->TopEdge > IterNext->TopEdge)
InsertAfter = IterNext;
else if (!InsertBefore)
InsertBefore = IterNext;
IterNext = IterNext->NextLine;
}
if (InsertAfter) {
NewLine->PrevLine = InsertAfter;
InsertAfter->NextLine = NewLine;
}
else
FirstLine = NewLine;
if (InsertBefore) {
NewLine->NextLine = InsertBefore;
InsertBefore->PrevLine = NewLine;
}
}
}
TGridLine* TGridLayout::InsertGridItem(TGridItem* NewItem)
{
NewItem->GridLine = 0; // Just to make sure
NewItem->NextItem = 0; // In case it was removed from another line
// Find which gridline the item should be inserted into
TGridLine* IterLine = FirstLine;
while (IterLine) {
if (NewItem->TopEdge == IterLine->TopEdge)
IterLine->InsertGridItem(NewItem);
IterLine = IterLine->NextLine;
}
if (!NewItem->GridLine) // No grid line in which to insert it
{
// Create a new gridline for the item
TGridLine* NewLine = new TGridLine(NewItem->TopEdge);
InsertGridLine(NewLine);
NewLine->InsertGridItem(NewItem);
}
// Return the grid line into which the item was inserted
return NewItem->GridLine;
}
// ***************************** TFloatingSlipEx ******************************
DEFINE_RESPONSE_TABLE1(TFloatingSlipEx, TFloatingSlip)
EV_WM_NCLBUTTONDOWN,
EV_WM_NCRBUTTONDOWN,
EV_WM_LBUTTONDOWN,
EV_WM_CLOSE,
EV_WM_NCLBUTTONDBLCLK,
END_RESPONSE_TABLE;
TFloatingSlipEx::TFloatingSlipEx(TWindow* parent, int x, int y, TWindow* clientWnd,
bool shrinkToClient, int captionHeight, bool popupPalette, TModule* module):
TFloatingSlip(parent, x, y, clientWnd, shrinkToClient, captionHeight,
popupPalette, module)
{
}
TFloatingSlipEx::~TFloatingSlipEx()
{
}
void TFloatingSlipEx::DockableInsert(TDockable& dockable, const TPoint* topLeft,
TRelPosition position, TDockable* relDockable)
{
TFloatingSlip::DockableInsert(dockable, topLeft, position, relDockable);
TDockableControlBarEx* cb = TYPESAFE_DOWNCAST(dockable.GetWindow(),
TDockableControlBarEx);
if (cb) {
TMargins m(TMargins::Pixels,
FloatMarginsX, FloatMarginsX, FloatMarginsY, FloatMarginsY);
cb->SetMargins(m);
}
}
void TFloatingSlipEx::DockableRemoved(const TRect& orgRect)
// Overwrite to call the TDockableControlBar that it will be removed
{
TDockableControlBarEx* cb = TYPESAFE_DOWNCAST(GetWindow(),
TDockableControlBarEx);
if (cb)
cb->PreRemoved();
TFloatingSlip::DockableRemoved(orgRect);
}
void TFloatingSlipEx::EvNCLButtonDown(uint hitTest, const TPoint& point)
{
#ifdef CTXHELP
TCtxHelpFileManager* ctxHelpM = TYPESAFE_DOWNCAST(GetApplication(),
TCtxHelpFileManager);
if (ctxHelpM && ctxHelpM->IsContextHelp()) {
// click in the titlebar of floating slip: call help with Attr.Id != 0
//
TDockableControlBarEx* cb = TYPESAFE_DOWNCAST(GetWindow(),
TDockableControlBarEx);
if (cb && cb->Attr.Id != 0)
ctxHelpM->WinHelp(cb->Attr.Id);
}
else
#endif
TFloatingSlip::EvNCLButtonDown(hitTest, point);
}
void TFloatingSlipEx::EvNCRButtonDown(uint hitTest, const TPoint& point)
{
TFloatingSlip::EvNCRButtonDown(hitTest, point);
// Show popup toolbars popup menu if the user right clicks into the title
//
if (GetApplication()) {
THarborManagement* hm = TYPESAFE_DOWNCAST(GetApplication(),
THarborManagement);
if (hm && hm->GetHarbor() && GetApplication()->GetMainWindow()) {
HMENU hMenu = hm->GetHarbor()->GetToolbarMenu(true);
if (hMenu && ::GetSubMenu(hMenu, 0) &&
GetApplication()->GetMainWindow()->GetHandle()) {
TPoint pt;
::GetCursorPos(&pt);
::TrackPopupMenu(::GetSubMenu(hMenu, 0),
TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
pt.x, pt.y, 0, GetApplication()->GetMainWindow()->GetHandle(), 0);
::DestroyMenu(hMenu);
}
}
}
}
void TFloatingSlipEx::EvLButtonDown(uint hitTest, const TPoint& point)
{
#ifdef CTXHELP
TCtxHelpFileManager* ctxHelpM = TYPESAFE_DOWNCAST(GetApplication(),
TCtxHelpFileManager);
if (ctxHelpM && ctxHelpM->IsContextHelp()) {
// click in the margings of floating slip: call help with Attr.Id != 0
//
TDockableControlBarEx* cb = TYPESAFE_DOWNCAST(GetWindow(),
TDockableControlBarEx);
if (cb && cb->Attr.Id != 0)
ctxHelpM->WinHelp(cb->Attr.Id);
}
else
#endif
TFloatingSlip::EvLButtonDown(hitTest, point);
}
void TFloatingSlipEx::EvClose()
// Update the customize dialog when the user click on x button in titlebar
{
TFloatingSlip::EvClose();
// Get x box rect. It's relative to to upper left of tiny caption
//
TPoint screenPt;
GetCursorPos(screenPt);
TRect rect = TTinyCaption::GetSysBoxRect();
TPoint winPt = screenPt - TTinyCaption::GetWindowRect().TopLeft();
rect.Offset(TTinyCaption::GetCaptionRect().Width()-rect.Width(), 0);
if (((TTinyCaption::GetStyle() & WS_SYSMENU) || TTinyCaption::CloseBox)&&
rect.Contains(winPt)) {
THarborEx* harbor = TYPESAFE_DOWNCAST(GetHarbor(), THarborEx);
if (harbor)
harbor->UpdateShowHideCtlBar();
}
}
void TFloatingSlipEx::EvNCLButtonDblClk(uint modKeys, const TPoint& point)
{
TDockableControlBarEx* cb = TYPESAFE_DOWNCAST(GetWindow(),
TDockableControlBarEx);
if (cb)
cb->ToggleSlip();
TFloatingSlip::EvNCLButtonDblClk(modKeys, point);
}
// ****************** TEdgeSlipEx *********************************************
DEFINE_RESPONSE_TABLE1(TEdgeSlipEx, TWindow)
EV_WM_LBUTTONDOWN,
EV_WM_LBUTTONDBLCLK,
EV_WM_NCCALCSIZE,
EV_WM_NCPAINT,
EV_WM_ERASEBKGND,
EV_WM_PARENTNOTIFY,
EV_WM_WINDOWPOSCHANGING,
END_RESPONSE_TABLE;
TEdgeSlipEx::TEdgeSlipEx(TDecoratedFrame& parent, TAbsLocation location, TModule* module)
:
TWindow(&parent, _T("EdgeSlipEx"), module),
Location(location),
GridType(Location == alTop || Location == alBottom ? YCoord : XCoord)
{
SetBkgndColor(TColor::Sys3dFace);
Attr.Style = (WS_VISIBLE|WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
InsertingDockable = false;
}
TEdgeSlipEx::~TEdgeSlipEx()
{
Destroy(IDCANCEL);
}
void TEdgeSlipEx::SetGripperMargins(TDockableControlBarEx* cb)
// Set the margins for the gripper
{
if (cb) {
THarborEx* harbor = TYPESAFE_DOWNCAST(cb->GetHarbor(), THarborEx);
if (harbor) {
int gripperSize = 3*GripperDistSize;
if (Location == alTop || Location == alBottom) {
TMargins m(TMargins::Pixels,
(harbor->IsDrawGripper()) ? gripperSize+EdgeMaginsX : EdgeMaginsX,
EdgeMaginsX,
EdgeMaginsY,
EdgeMaginsY);
cb->SetMargins(m);
}
else {
TMargins m(TMargins::Pixels,
EdgeMaginsX,
EdgeMaginsX,
(harbor->IsDrawGripper()) ? gripperSize+EdgeMaginsY : EdgeMaginsY,
EdgeMaginsY);
cb->SetMargins(m);
}
}
}
}
void TEdgeSlipEx::DockableInsert(TDockable& dockable, const TPoint* topLeft,
TRelPosition position, TDockable* relDockable)
// Similar to TEdgeSlip
{
// Get dockable's window & hide it in case we have to toss it around a bit
// Reparent the window to the edge slip
TWindow* dockableWindow = dockable.GetWindow();
dockableWindow->ShowWindow(SW_HIDE);
dockableWindow->SetParent(this);
//D: Set the margins for the Gripper
//
TDockableControlBarEx* cb = TYPESAFE_DOWNCAST(dockableWindow,
TDockableControlBarEx);
SetGripperMargins(cb);
// Let window know it was docked...
if (dockableWindow->IsWindow())
dockableWindow->SendMessage(WM_OWLWINDOWDOCKED, TParam1(position),
TParam2((TDockingSlip*)this));
// Make sure that the dockable is oriented the right way--horizontal layout
// for Y-gridded slips
InsertingDockable = true; // Prevent EvParentNotify from doing anything
dockable.Layout(GridType == YCoord ? alTop : alLeft);
InsertingDockable = false;
DockableMove(dockable, topLeft, position, relDockable);
dockableWindow->ShowWindow(SW_SHOWNA);
}
void TEdgeSlipEx::DockableMove(TDockable& dockable, const TPoint* topLeft,
TRelPosition /*position*/, TDockable* /*relDockable*/)
{
TWindow* dockableWindow = dockable.GetWindow();
if (topLeft) {
// Move the window into position relative to its parent (this)
::MapWindowPoints(0, *this, (TPoint*)topLeft, 1);
dockableWindow->SetWindowPos(0, topLeft->x, topLeft->y, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
}
else {
// A stand-in until a better method of approximation is used
dockableWindow->SetWindowPos(0, -2, -2, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
}
// Now do the magic which shuffles all the dockables about
ArrangeAllDockables(dockableWindow);
}
void TEdgeSlipEx::DockableRemoved(const TRect& /*orgRect*/)
// Overwrite to call the TDockableControlBar that it will be removed
{
TDockableControlBarEx* cb = TYPESAFE_DOWNCAST(GetFirstChild(),
TDockableControlBarEx);
if (cb)
cb->PreRemoved();
// Do the magic which shuffles all the dockables about
// Should just close up empty gridlines
ArrangeAllDockables(0);
}
void TEdgeSlipEx::ArrangeAllDockables(TWindow* NewDockable)
{
TGridLayout GridLayout(Location);
GridLayout.CreateLists(this, NewDockable);
TGridLine* DockableLine = GridLayout.InsertDockableItem();
if (DockableLine) {
int SlipWidth;
if (Location == alTop || Location == alBottom) {
// This gave zero if the slip was just created - first toolbar positioned
// always at the far left, not the drop position.
SlipWidth = GetWindowRect().Width();
//SlipWidth = std::max(Parent->GetClientRect().Width(), 0);
}
else {
// This gave zero if the slip was just created - first toolbar positioned
// always at the top, not the drop position.
SlipWidth = std::max(GetWindowRect().Height() - 4, 0); // Compensate for borders
//TFrameWindow* Frame = TYPESAFE_DOWNCAST(Parent, TFrameWindow);
// This makes no allowance for the possible increase if the new toolbar was
// dragged from the top or bottom slips, or for the offset if the new
// toolbar was dragged from the top slip.
//SlipWidth = std::max(Frame->GetClientWindow()->GetClientRect().Height() - 4, 0);
}
TGridItem* OverItem = DockableLine->LayoutGridItems(GridLayout.DockableItem, SlipWidth);
if (OverItem) {
// This will deal with wrapping items pushed off the right edge.
// Assumes we only need to wrap at most one new grid line.
TGridItem* IterItem = OverItem;
while (IterItem) {
TGridItem* IterNext = IterItem->NextItem;
DockableLine->RemoveGridItem(IterItem);
IterItem->TopEdge += 1; // Force a new line to be inserted
IterItem->LeftEdge = -2; // Jam over to the left side
GridLayout.InsertGridItem(IterItem);
IterItem = IterNext;
}
TGridLine* ExtraLine = OverItem->GridLine;
// Layout the new line
ExtraLine->LayoutGridItems(OverItem, SlipWidth);
}
}
int SlipHeight = GridLayout.LayoutGridLines();
// Translate the calculations to actual window positions
GridLayout.LayoutDockables();
// Alter the size of the slip itself if necessary
LayoutSlip(SlipHeight);
}
void TEdgeSlipEx::LayoutSlip(int Height)
{
// This resizes the edge slip by modifying the layout metrics held by its
// parent frame
bool SizeChanged;
if (Location == alTop || Location == alBottom) {
SizeChanged = Height != Attr.H;
Attr.H = Height;
}
else {
SizeChanged = Height != Attr.W;
Attr.W = Height;
}
if (SizeChanged) {
TDecoratedFrame* Frame = TYPESAFE_DOWNCAST(Parent, TDecoratedFrame);
TLayoutMetrics Metrics;
Frame->GetChildLayoutMetrics(*this, Metrics);
Frame->SetChildLayoutMetrics(*this, Metrics);
Frame->Layout();
}
}
TAbsLocation TEdgeSlipEx::GetLocation() const
// Copied more or less from TEdgeSlip implemenation
{
return Location;
}
TResult TEdgeSlipEx::EvCommand(uint id, THandle hWndCtl, uint notifyCode)
// Copied more or less from TEdgeSlip implemenation
{
if (notifyCode == 0 && Parent)
return Parent->EvCommand(id, hWndCtl, notifyCode);
return TWindow::EvCommand(id, hWndCtl, notifyCode);
}
void TEdgeSlipEx::EvCommandEnable(TCommandEnabler& commandEnabler)
// Copied more or less from TEdgeSlip implemenation
{
if (Parent) {
// Already being processed?
if (!commandEnabler.IsReceiver(Parent->GetHandle())) {
// No, so forward it up to our parent
commandEnabler.SetReceiver(Parent->GetHandle());
Parent->EvCommandEnable(commandEnabler);
}
}
}
void TEdgeSlipEx::SetupWindow()
{
TWindow::SetupWindow();
// Give an initial position to all the child windows
// This part may not be required if user wants to set positions
// directly via Attr
TWindow* FirstChild = GetFirstChild();
if (FirstChild) {
TWindow* ChildWindow = FirstChild;
do {
// A stand-in until a better method of approximation is used
ChildWindow->SetWindowPos(0, -2, -2, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
ChildWindow = ChildWindow->Next();
}
while (ChildWindow != FirstChild); // Does Next() cycle?
}
TGridLayout GridLayout(Location);
GridLayout.CreateLists(this, 0); //No Anchor
int SlipWidth;
if (Location == alTop || Location == alBottom)
SlipWidth = GetWindowRect().Width();
else
SlipWidth = std::max(GetWindowRect().Height() - 4, 0); // Compensate for borders
// This spaces the grid lines vertically to ensure there is space to insert
// new lines if necessary
GridLayout.LayoutGridLines();
TGridLine* DockableLine = GridLayout.FirstLine;
// Cycle through the grid lines to lay them all out
while (DockableLine) {
TGridItem* OverItem = DockableLine->LayoutGridItems(0, SlipWidth);
TGridLine* ExtraLine = DockableLine;
while (OverItem) {
// This will deal with wrapping items pushed off the right edge
// Loops in case of multiple wraps
TGridItem* IterItem = OverItem;
while (IterItem) {
TGridItem* IterNext = IterItem->NextItem;
ExtraLine->RemoveGridItem(IterItem);
IterItem->TopEdge += 1; // Force a new line to be inserted
IterItem->LeftEdge = -2; // Jam over to the left side
GridLayout.InsertGridItem(IterItem);
IterItem = IterNext;
}
// Change to the newly created line
ExtraLine = OverItem->GridLine;
// Layout the new line
OverItem = ExtraLine->LayoutGridItems(OverItem, SlipWidth);
}
// Not the best scheme since newly inserted lines will be laid out twice.
DockableLine = DockableLine->NextLine;
}
int SlipHeight = GridLayout.LayoutGridLines();
// Translate the calculations to actual window positions
GridLayout.LayoutDockables();
// Alter the size of the slip itself if necessary
LayoutSlip(SlipHeight);
}
void TEdgeSlipEx::EvLButtonDown(uint modKeys, const TPoint& point)
// Copied more or less from TEdgeSlip implemenation
{
TWindow* cw = GetWindowPtr(ChildWindowFromPoint(point));
// Only allow immediate children of the docking window to be clicked on.
if (cw && cw->GetParentO() == this) {
// Is the mouseDown in a area where we can move the docked window?
TPoint childPt = point;
MapWindowPoints(*cw, &childPt, 1);
TDockable* d = TYPESAFE_DOWNCAST(cw, TDockable);
if (d && d->ShouldBeginDrag(childPt)) {
TPoint p(point);
MapWindowPoints(0, &p, 1);
if (Harbor && Harbor->DockDraggingBegin(*d, p, Location, this))
return; // Successfully started
}
}
TWindow::EvLButtonDown(modKeys, point);
}
void TEdgeSlipEx::EvLButtonDblClk(uint /*modKeys*/, const TPoint& /*point*/)
// Copied more or less from TEdgeSlip implemenation
{
Parent->HandleMessage(WM_OWLSLIPDBLCLK);
}
uint TEdgeSlipEx::EvNCCalcSize(bool calcValidRects, NCCALCSIZE_PARAMS & calcSize)
// Copied more or less from TEdgeSlip implemenation
{
// Why is this function required?
// Does it affect the interpretation of the client coordinates?
// Yes - this appears to be the case.
uint ret = TWindow::EvNCCalcSize(calcValidRects, calcSize);
if (IsIconic())
return ret;
// Only add in space if this slip is not shrunk to nothing
if (calcSize.rgrc[0].bottom - calcSize.rgrc[0].top > 0) {
if (!(Attr.Style & WS_BORDER)) {
if (Location != alBottom)
calcSize.rgrc[0].top += 2;
if (Location != alTop)
calcSize.rgrc[0].bottom -= 2;
}
}
return 0;
}
void TEdgeSlipEx::EvNCPaint(HRGN)
// Copied more or less from TEdgeSlip implemenation
{
// Non-3d style
if (Attr.Style & WS_BORDER) {
DefaultProcessing();
}
// Use 3-d style
else {
// Paint etched line along the top for left, top & right slips, and along
// the bottom for bottom, left & right slips to separate from the menubar,
// statusbar & each other.
TWindowDC dc(*this);
int height(GetWindowRect().Height());
if (Location != alBottom)
TUIBorder(TRect(0,0,9999,2), TUIBorder::EdgeEtched, TUIBorder::Top).Paint(dc);
if (Location != alTop)
TUIBorder(TRect(0,height-2,9999,height), TUIBorder::EdgeEtched, TUIBorder::Bottom).Paint(dc);
}
}
bool TEdgeSlipEx::EvEraseBkgnd(HDC hDC)
// Copied more or less from TEdgeSlip implemenation
{
TWindow::EvEraseBkgnd(hDC); // Let TWindow erase everything
return true;
}
void TEdgeSlipEx::EvParentNotify(const TParentNotify& n)
// Copied more or less from TEdgeSlip implemenation
{
if (n.Event == WM_SIZE && !InsertingDockable)
ArrangeAllDockables(0); // Can cause double Layout of frame window
else
DefaultProcessing();
}
bool TEdgeSlipEx::EvWindowPosChanging(WINDOWPOS& windowPos)
// Copied more or less from TEdgeSlip implemenation
{
return TWindow::EvWindowPosChanging(windowPos);
}
} // OwlExt namespace
//========================================================================
↑ V595 The 'IterPrev' pointer was utilized before it was verified against nullptr. Check lines: 1431, 1447.
↑ V1004 The 'actGadget' pointer was used unsafely after it was verified against nullptr. Check lines: 576, 582.
↑ V522 There might be dereferencing of a potential null pointer 'harbor'.
↑ V522 There might be dereferencing of a potential null pointer 'harbor'.
↑ V522 There might be dereferencing of a potential null pointer 'harbor'.
↑ V522 There might be dereferencing of a potential null pointer 'btgd'.
↑ V522 There might be dereferencing of a potential null pointer 'Frame'.
↑ V1027 Pointer to an object of the 'TRect' class is cast to unrelated 'TPoint' class.