1/////////////////////////////////////////////////////////////////////////////
2// Name:        foldpanelbar.cpp
3// Purpose:
4// Author:      Jorgen Bodde
5// Modified by: ABX - 19/12/2004 : possibility of horizontal orientation
6//                               : wxWidgets coding standards
7// Created:     22/06/2004
8// RCS-ID:      $Id: foldpanelbar.cpp 34972 2005-07-28 23:23:27Z VZ $
9// Copyright:   (c) Jorgen Bodde
10// Licence:     wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13// For compilers that support precompilation, includes "wx/wx.h".
14#include "wx/wxprec.h"
15
16#ifdef __BORLANDC__
17    #pragma hdrstop
18#endif
19
20#ifndef WX_PRECOMP
21    #include "wx/wx.h"
22#endif
23
24#include "wx/foldbar/foldpanelbar.h"
25#include "icon_collapsed.xpm"
26#include "icon_expanded.xpm"
27#include "icon_theresmore.xpm"
28
29//----------------------------------------------------------------------------
30// wxFoldPanelBar
31//----------------------------------------------------------------------------
32
33IMPLEMENT_DYNAMIC_CLASS( wxFoldPanelBar, wxPanel )
34
35BEGIN_EVENT_TABLE(wxFoldPanelBar,wxPanel)
36    EVT_SIZE(wxFoldPanelBar::OnSizePanel)
37    //EVT_PAINT(wxFoldPanelBar::OnPaint)
38    EVT_CAPTIONBAR(wxID_ANY, wxFoldPanelBar::OnPressCaption)
39END_EVENT_TABLE()
40
41wxFoldPanelBar::wxFoldPanelBar()
42    : wxPanel()
43    , m_foldPanel(NULL)
44    , m_bottomPanel(NULL)
45    , m_controlCreated(false)
46{
47
48}
49
50wxFoldPanelBar::wxFoldPanelBar( wxWindow *parent, wxWindowID id, const wxPoint &position,
51                                const wxSize& size, long style, long extraStyle)
52    : wxPanel()
53    , m_foldPanel(NULL)
54    , m_bottomPanel(NULL)
55    , m_controlCreated(false)
56{
57    Create( parent, id, position, size, style, extraStyle);
58}
59
60void wxFoldPanelBar::Create( wxWindow *parent, wxWindowID id, const wxPoint &position,
61                             const wxSize& size, long style, long extraStyle )
62{
63
64    m_extraStyle = extraStyle;
65
66    // make sure there is any orientation
67    if ( ( style & wxFPB_HORIZONTAL ) != wxFPB_HORIZONTAL )
68        style |= wxFPB_VERTICAL;
69
70    // create the panel (duh!). This causes a size event, which we are going
71    // to skip when we are not initialised
72
73    wxPanel::Create(parent, id, position, size, style);
74
75    // the fold panel area
76
77    m_foldPanel = new wxPanel(this, wxID_ANY, position, size, wxNO_BORDER|wxTAB_TRAVERSAL);
78
79    // the extra area for some icons / context menu etc
80
81#if 0
82    m_bottomPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,22), wxNO_BORDER|wxTAB_TRAVERSAL);
83    m_bottomPanel->SetBackgroundColour(*wxWHITE);
84#endif
85
86    // create the fold icons to be used in the captions
87
88    m_images = new wxImageList(16, 16);
89
90    wxBitmap *bmp = new wxBitmap(icon_expanded);
91    m_images->Add(*bmp);
92    delete bmp;
93
94    bmp = new wxBitmap(icon_collapsed);
95    m_images->Add(*bmp);
96    delete bmp;
97
98    m_moreBmp = new wxBitmap(icon_theresmore);
99
100    // do this as last, to check if create is already called
101
102    m_controlCreated = true;
103}
104
105wxFoldPanelBar::~wxFoldPanelBar()
106{
107    delete m_images;
108    delete m_moreBmp;
109}
110
111wxFoldPanel wxFoldPanelBar::AddFoldPanel(const wxString &caption, bool collapsedInitially, const wxCaptionBarStyle &style)
112{
113    wxASSERT(m_controlCreated);
114
115    // create a fold panel item, which is first only the caption.
116    // the user can now add a panel area which will be folded in
117    // when pressed.
118
119    wxFoldPanelItem *item = new wxFoldPanelItem(m_foldPanel, caption, m_images, collapsedInitially, style);
120
121    // look at the last added one and reposition this one
122    int pos = 0;
123    if(m_panels.GetCount() > 0)
124        pos = m_panels.Last()->GetItemPos() + m_panels.Last()->GetPanelLength();
125
126    item->Reposition(pos);
127    m_panels.Add(item);
128
129    //return wxFoldPanel(item);
130    return wxFoldPanel(item);
131}
132
133int wxFoldPanelBar::AddFoldPanelWindow(const wxFoldPanel &panel, wxWindow *window, int flags, int ySpacing, int leftSpacing,
134                                       int rightSpacing)
135{
136    wxCHECK(panel.IsOk(), -1);
137    panel.GetItem()->AddWindow(window, flags, ySpacing, leftSpacing, rightSpacing);
138
139    // TODO: Take old and new height, and if difference, reposition all the lower panels
140    // this is because the user can add new wxWindow controls somewhere in between
141    // when other panels are already present.
142
143    return 0;
144}
145
146int wxFoldPanelBar::AddFoldPanelSeperator(const wxFoldPanel &panel, const wxColour &color, int ySpacing, int leftSpacing,
147                                          int rightSpacing)
148{
149    wxCHECK(panel.IsOk(), -1);
150    panel.GetItem()->AddSeparator(color, ySpacing, leftSpacing, rightSpacing);
151
152    return 0;
153}
154
155void wxFoldPanelBar::OnSizePanel(wxSizeEvent &event)
156{
157    // skip all stuff when we are not initialised yet
158
159    if(!m_controlCreated)
160    {
161        event.Skip();
162        return;
163    }
164
165    // now size the fold panel area and the
166    // lower bar in such a way that the bar is always
167    // visible
168
169    wxRect foldrect = GetRect();
170
171    // fold panel itself. If too little space,
172    // don't show it
173
174#if 0
175    if(foldrect.GetHeight() < 23)
176        foldrect.SetHeight(0);
177    else
178        foldrect.SetHeight(foldrect.GetHeight() - 22);
179#endif
180
181    foldrect.SetX(0);
182    foldrect.SetY(0);
183    m_foldPanel->SetSize(foldrect);
184
185    if(m_extraStyle & wxFPB_COLLAPSE_TO_BOTTOM)
186    {
187        wxRect rect = RepositionCollapsedToBottom();
188        bool vertical = IsVertical();
189        if((vertical && rect.GetHeight() > 0) || (!vertical && rect.GetWidth() > 0))
190            RefreshRect(rect);
191    }
192
193    // TODO: A smart way to check wether the old - new width of the
194    // panel changed, if so no need to resize the fold panel items
195
196    RedisplayFoldPanelItems();
197
198    // tool panel for icons and other stuff
199
200#if 0
201    wxRect bottomrect = GetRect();
202    if(bottomrect.GetHeight() < 22)
203        bottomrect.SetY(0);
204    else
205        bottomrect.SetY(bottomrect.GetHeight() - 22);
206
207    bottomrect.SetHeight(22);
208    bottomrect.SetX(0);
209    m_bottomPanel->SetSize(bottomrect);
210
211    // TODO: redraw the bitmap properly
212    // use the captionbar algorithm for that
213
214    m_bottomPanel->Refresh();
215#endif
216}
217
218void wxFoldPanelBar::OnPaint(wxPaintEvent &event)
219{
220    if(!m_controlCreated)
221        return;
222#if 0
223    // paint the bottom panel only, where the
224    // arrow is shown when there is more to show the user
225    // just as informative icon
226
227    wxPaintDC dc(m_bottomPanel);
228
229    wxSize size = m_bottomPanel->GetSize();
230    int offset = (size.GetHeight() - m_moreBmp->GetHeight()) / 2;
231
232    dc.DrawBitmap(*m_moreBmp, size.GetWidth() - m_moreBmp->GetWidth() - 2, offset, true);
233#endif
234
235    event.Skip();
236}
237
238void wxFoldPanelBar::OnPressCaption(wxCaptionBarEvent &event)
239{
240    // act upon the folding or expanding status of the bar
241    // to expand or collapse the panel(s)
242
243    if(event.GetFoldStatus())
244        Collapse(wxFoldPanel((wxFoldPanelItem *)event.GetTag()));
245    else
246        Expand(wxFoldPanel((wxFoldPanelItem *)event.GetTag()));
247}
248
249void wxFoldPanelBar::RefreshPanelsFrom(wxFoldPanelItem *item)
250{
251    wxASSERT(item);
252
253    int i = m_panels.Index(item);
254    if(i != wxNOT_FOUND)
255        RefreshPanelsFrom(i);
256}
257
258void wxFoldPanelBar::RefreshPanelsFrom(size_t i)
259{
260    Freeze();
261
262    // if collapse to bottom is on, the panels that are not expanded
263    // should be drawn at the bottom. All panels that are expanded
264    // are drawn on top. The last expanded panel gets all the extra space
265
266    if(m_extraStyle & wxFPB_COLLAPSE_TO_BOTTOM)
267    {
268        int offset = 0;
269
270        for(size_t j = 0; j < m_panels.GetCount(); j++)
271        {
272            if(m_panels.Item(j)->IsExpanded())
273                offset += m_panels.Item(j)->Reposition(offset);
274        }
275
276        // put all non collapsed panels at the bottom where there is space, else
277        // put them right behind the expanded ones
278
279        RepositionCollapsedToBottom();
280    }
281    else
282    {
283        int pos = m_panels.Item(i)->GetItemPos() + m_panels.Item(i)->GetPanelLength();
284        for(i++; i < m_panels.GetCount(); i++)
285            pos += m_panels.Item(i)->Reposition(pos);
286    }
287    Thaw();
288}
289
290void wxFoldPanelBar::RedisplayFoldPanelItems()
291{
292    // resize them all. No need to reposition
293
294    wxFoldPanelItem *item;
295    for(size_t i = 0; i < m_panels.GetCount(); i++)
296    {
297        item = m_panels.Item(i);
298        wxASSERT(item);
299
300        item->ResizePanel();
301    }
302}
303
304wxRect wxFoldPanelBar::RepositionCollapsedToBottom()
305{
306    wxRect value(0,0,0,0);
307    bool vertical = IsVertical();
308
309    // determine wether the number of panels left
310    // times the size of their captions is enough
311    // to be placed in the left over space
312
313    int expanded = 0, collapsed = 0, offset;
314    GetPanelsLength(collapsed, expanded);
315
316    // if no room stick them behind the normal ones, else
317    // at the bottom
318
319    if(((vertical ? GetSize().GetHeight() : GetSize().GetWidth()) - expanded - collapsed) < 0)
320        offset = expanded;
321    else
322    {
323        // value is the region which is left unpainted
324        // I will send it back as 'slack' so it does not need to
325        // be recalulated.
326
327        value.SetHeight(GetSize().GetHeight());
328        value.SetWidth(GetSize().GetWidth());
329
330        if(vertical)
331        {
332            value.SetY(expanded);
333            value.SetHeight(value.GetHeight() - expanded);
334        }
335        else
336        {
337            value.SetX(expanded);
338            value.SetWidth(value.GetWidth() - expanded);
339        }
340
341        offset = (vertical ? GetSize().GetHeight() : GetSize().GetWidth()) - collapsed;
342    }
343
344    // go reposition
345
346    for(size_t i = 0; i < m_panels.GetCount(); i++)
347    {
348        if(!m_panels.Item(i)->IsExpanded())
349            offset += m_panels.Item(i)->Reposition(offset);
350    }
351
352    return value;
353}
354
355int wxFoldPanelBar::GetPanelsLength(int &collapsed, int &expanded)
356{
357    int value = 0;
358
359    // assumed here that all the panels that are expanded
360    // are positioned after each other from 0,0 to end.
361
362    for(size_t j = 0; j < m_panels.GetCount(); j++)
363    {
364        int offset = m_panels.Item(j)->GetPanelLength();
365        value += offset;
366        if(m_panels.Item(j)->IsExpanded())
367            expanded += offset;
368        else
369            collapsed += offset;
370    }
371
372    return value;
373}
374