- MGR: Allow Notices tab to scroll smoothly, disallow selection of items in Notices tab

svn path=/trunk/boinc/; revision=22242
This commit is contained in:
Charlie Fenton 2010-08-15 14:17:01 +00:00
parent cc44cb4fdb
commit 945470d3ba
9 changed files with 1899 additions and 23 deletions

View File

@ -6083,3 +6083,16 @@ David 14 Aug 2010
sched/
handle_request.cpp
Charlie 15 Aug 2010
- MGR: Allow Notices tab to scroll smoothly, disallow selection of
items in Notices tab.
clientgui/
BOINCHtmlListBox.cpp, .h (new)
BOINCVListBox.cpp, .h (new)
NoticeListCtrl.cpp, .h
ViewNotices.cpp
mac_build/
boinc.xcodeproj/
project.pbxproj

629
clientgui/BOINCHtmlLBox.cpp Normal file
View File

@ -0,0 +1,629 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// Adapted from htmllbox.cpp by Vadim Zeitlin <vadim@wxwindows.org>
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#if wxUSE_HTML
#include "BOINCHtmlLBox.h"
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
const wxChar BOINCHtmlListBoxNameStr[] = wxT("BOINCHtmlListBox");
// ============================================================================
// private classes
// ============================================================================
// ----------------------------------------------------------------------------
// CBOINCHtmlListBoxCache
// ----------------------------------------------------------------------------
// this class is used by CBOINCHtmlListBox to cache the parsed representation of
// the items to avoid doing it anew each time an item must be drawn
class CBOINCHtmlListBoxCache
{
private:
// invalidate a single item, used by Clear() and InvalidateRange()
void InvalidateItem(size_t n)
{
m_items[n] = (size_t)-1;
delete m_cells[n];
m_cells[n] = NULL;
}
public:
CBOINCHtmlListBoxCache()
{
for ( size_t n = 0; n < SIZE; n++ )
{
m_items[n] = (size_t)-1;
m_cells[n] = NULL;
}
m_next = 0;
}
~CBOINCHtmlListBoxCache()
{
for ( size_t n = 0; n < SIZE; n++ )
{
delete m_cells[n];
}
}
// completely invalidate the cache
void Clear()
{
for ( size_t n = 0; n < SIZE; n++ )
{
InvalidateItem(n);
}
}
// return the cached cell for this index or NULL if none
wxHtmlCell *Get(size_t item) const
{
for ( size_t n = 0; n < SIZE; n++ )
{
if ( m_items[n] == item )
return m_cells[n];
}
return NULL;
}
// returns true if we already have this item cached
bool Has(size_t item) const { return Get(item) != NULL; }
// ensure that the item is cached
void Store(size_t item, wxHtmlCell *cell)
{
delete m_cells[m_next];
m_cells[m_next] = cell;
m_items[m_next] = item;
// advance to the next item wrapping around if there are no more
if ( ++m_next == SIZE )
m_next = 0;
}
// forget the cached value of the item(s) between the given ones (inclusive)
void InvalidateRange(size_t from, size_t to)
{
for ( size_t n = 0; n < SIZE; n++ )
{
if ( m_items[n] >= from && m_items[n] <= to )
{
InvalidateItem(n);
}
}
}
private:
// the max number of the items we cache
enum { SIZE = 50 };
// the index of the LRU (oldest) cell
size_t m_next;
// the parsed representation of the cached item or NULL
wxHtmlCell *m_cells[SIZE];
// the index of the currently cached item (only valid if m_cells != NULL)
size_t m_items[SIZE];
};
// ----------------------------------------------------------------------------
// CBOINCHtmlListBoxStyle
// ----------------------------------------------------------------------------
// just forward wxDefaultHtmlRenderingStyle callbacks to the main class so that
// they could be overridden by the user code
class CBOINCHtmlListBoxStyle : public wxDefaultHtmlRenderingStyle
{
public:
CBOINCHtmlListBoxStyle(const CBOINCHtmlListBox& hlbox) : m_hlbox(hlbox) { }
virtual wxColour GetSelectedTextColour(const wxColour& colFg)
{
return m_hlbox.GetSelectedTextColour(colFg);
}
virtual wxColour GetSelectedTextBgColour(const wxColour& colBg)
{
return m_hlbox.GetSelectedTextBgColour(colBg);
}
private:
const CBOINCHtmlListBox& m_hlbox;
DECLARE_NO_COPY_CLASS(CBOINCHtmlListBoxStyle)
};
// ----------------------------------------------------------------------------
// event tables
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(CBOINCHtmlListBox, CBOINCVListBox)
EVT_SIZE(CBOINCHtmlListBox::OnSize)
EVT_MOTION(CBOINCHtmlListBox::OnMouseMove)
EVT_LEFT_DOWN(CBOINCHtmlListBox::OnLeftDown)
END_EVENT_TABLE()
// ============================================================================
// implementation
// ============================================================================
IMPLEMENT_ABSTRACT_CLASS(CBOINCHtmlListBox, CBOINCVListBox)
// ----------------------------------------------------------------------------
// CBOINCHtmlListBox creation
// ----------------------------------------------------------------------------
CBOINCHtmlListBox::CBOINCHtmlListBox()
: wxHtmlWindowMouseHelper(this)
{
Init();
}
// normal constructor which calls Create() internally
CBOINCHtmlListBox::CBOINCHtmlListBox(wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
: wxHtmlWindowMouseHelper(this)
{
Init();
(void)Create(parent, id, pos, size, style, name);
}
void CBOINCHtmlListBox::Init()
{
m_htmlParser = NULL;
m_htmlRendStyle = new CBOINCHtmlListBoxStyle(*this);
m_cache = new CBOINCHtmlListBoxCache;
}
bool CBOINCHtmlListBox::Create(wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
return CBOINCVListBox::Create(parent, id, pos, size, style, name);
}
CBOINCHtmlListBox::~CBOINCHtmlListBox()
{
delete m_cache;
if ( m_htmlParser )
{
delete m_htmlParser->GetDC();
delete m_htmlParser;
}
delete m_htmlRendStyle;
}
// ----------------------------------------------------------------------------
// CBOINCHtmlListBox appearance
// ----------------------------------------------------------------------------
wxColour CBOINCHtmlListBox::GetSelectedTextColour(const wxColour& colFg) const
{
return m_htmlRendStyle->
wxDefaultHtmlRenderingStyle::GetSelectedTextColour(colFg);
}
wxColour
CBOINCHtmlListBox::GetSelectedTextBgColour(const wxColour& WXUNUSED(colBg)) const
{
return GetSelectionBackground();
}
// ----------------------------------------------------------------------------
// CBOINCHtmlListBox items markup
// ----------------------------------------------------------------------------
wxString CBOINCHtmlListBox::OnGetItemMarkup(size_t n) const
{
// we don't even need to wrap the value returned by OnGetItem() inside
// "<html><body>" and "</body></html>" because wxHTML can parse it even
// without these tags
return OnGetItem(n);
}
// ----------------------------------------------------------------------------
// CBOINCHtmlListBox cache handling
// ----------------------------------------------------------------------------
void CBOINCHtmlListBox::CacheItem(size_t n) const
{
if ( !m_cache->Has(n) )
{
if ( !m_htmlParser )
{
CBOINCHtmlListBox *self = wxConstCast(this, CBOINCHtmlListBox);
self->m_htmlParser = new wxHtmlWinParser(self);
m_htmlParser->SetDC(new wxClientDC(self));
m_htmlParser->SetFS(&self->m_filesystem);
#if !wxUSE_UNICODE
if (GetFont().Ok())
m_htmlParser->SetInputEncoding(GetFont().GetEncoding());
#endif
// use system's default GUI font by default:
m_htmlParser->SetStandardFonts();
}
wxHtmlContainerCell *cell = (wxHtmlContainerCell *)m_htmlParser->
Parse(OnGetItemMarkup(n));
wxCHECK_RET( cell, _T("wxHtmlParser::Parse() returned NULL?") );
// set the cell's ID to item's index so that CellCoordsToPhysical()
// can quickly find the item:
cell->SetId(wxString::Format(_T("%lu"), (unsigned long)n));
cell->Layout(GetClientSize().x - (2 * GetMargins().x));
m_cache->Store(n, cell);
}
}
void CBOINCHtmlListBox::OnSize(wxSizeEvent& event)
{
// we need to relayout all the cached cells
m_cache->Clear();
event.Skip();
}
void CBOINCHtmlListBox::RefreshItem(size_t item)
{
wxRect rect;
m_cache->InvalidateRange(item, item);
GetItemRect(item, rect);
RefreshRect(rect);
}
void CBOINCHtmlListBox::RefreshItems(size_t from, size_t to)
{
wxRect rect;
m_cache->InvalidateRange(from, to);
for (size_t i = from; i <= to; ++i) {
GetItemRect(i, rect);
RefreshRect(rect);
}
}
void CBOINCHtmlListBox::SetItemCount(size_t count)
{
// the items are going to change, forget the old ones
m_cache->Clear();
CBOINCVListBox::SetItemCount(count);
}
// ----------------------------------------------------------------------------
// CBOINCHtmlListBox implementation of CBOINCVListBox pure virtuals
// ----------------------------------------------------------------------------
void CBOINCHtmlListBox::OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const
{
CacheItem(n);
wxHtmlCell *cell = m_cache->Get(n);
wxCHECK_RET( cell, _T("this cell should be cached!") );
wxHtmlRenderingInfo htmlRendInfo;
// draw the selected cell in selected state
if ( IsSelected(n) )
{
wxHtmlSelection htmlSel;
htmlSel.Set(wxPoint(0,0), cell, wxPoint(INT_MAX, INT_MAX), cell);
htmlRendInfo.SetSelection(&htmlSel);
if ( m_htmlRendStyle )
htmlRendInfo.SetStyle(m_htmlRendStyle);
htmlRendInfo.GetState().SetSelectionState(wxHTML_SEL_IN);
}
// note that we can't stop drawing exactly at the window boundary as then
// even the visible cells part could be not drawn, so always draw the
// entire cell
cell->Draw(dc,
rect.x + CELL_BORDER, rect.y + CELL_BORDER,
0, INT_MAX, htmlRendInfo);
}
wxCoord CBOINCHtmlListBox::OnMeasureItem(size_t n) const
{
CacheItem(n);
wxHtmlCell *cell = m_cache->Get(n);
wxCHECK_MSG( cell, 0, _T("this cell should be cached!") );
return cell->GetHeight() + cell->GetDescent() + (2 * CELL_BORDER);
}
wxCoord CBOINCHtmlListBox::GetMaxItemWidth() const
{
wxCoord w, maxWidth = 0;
size_t n = GetItemCount();
for (size_t i = 0; i < n; ++i) {
CacheItem(i);
wxHtmlCell *cell = m_cache->Get(i);
w = cell->GetWidth();
if (w > maxWidth) maxWidth = w;
}
return maxWidth;
}
// ----------------------------------------------------------------------------
// CBOINCHtmlListBox implementation of wxHtmlListBoxWinInterface
// ----------------------------------------------------------------------------
void CBOINCHtmlListBox::SetHTMLWindowTitle(const wxString& WXUNUSED(title))
{
// nothing to do
}
void CBOINCHtmlListBox::OnHTMLLinkClicked(const wxHtmlLinkInfo& link)
{
OnLinkClicked(GetItemForCell(link.GetHtmlCell()), link);
}
void CBOINCHtmlListBox::OnLinkClicked(size_t WXUNUSED(n),
const wxHtmlLinkInfo& link)
{
wxHtmlLinkEvent event(GetId(), link);
GetEventHandler()->ProcessEvent(event);
}
wxHtmlOpeningStatus
CBOINCHtmlListBox::OnHTMLOpeningURL(wxHtmlURLType WXUNUSED(type),
const wxString& WXUNUSED(url),
wxString *WXUNUSED(redirect)) const
{
return wxHTML_OPEN;
}
wxPoint CBOINCHtmlListBox::HTMLCoordsToWindow(wxHtmlCell *cell,
const wxPoint& pos) const
{
return CellCoordsToPhysical(pos, cell);
}
wxWindow* CBOINCHtmlListBox::GetHTMLWindow() { return this; }
wxColour CBOINCHtmlListBox::GetHTMLBackgroundColour() const
{
return GetBackgroundColour();
}
void CBOINCHtmlListBox::SetHTMLBackgroundColour(const wxColour& WXUNUSED(clr))
{
// nothing to do
}
void CBOINCHtmlListBox::SetHTMLBackgroundImage(const wxBitmap& WXUNUSED(bmpBg))
{
// nothing to do
}
void CBOINCHtmlListBox::SetHTMLStatusText(const wxString& WXUNUSED(text))
{
// nothing to do
}
wxCursor CBOINCHtmlListBox::GetHTMLCursor(HTMLCursor type) const
{
// we don't want to show text selection cursor in listboxes
if (type == HTMLCursor_Text)
return wxHtmlWindow::GetDefaultHTMLCursor(HTMLCursor_Default);
// in all other cases, use the same cursor as wxHtmlWindow:
return wxHtmlWindow::GetDefaultHTMLCursor(type);
}
// ----------------------------------------------------------------------------
// CBOINCHtmlListBox utilities
// ----------------------------------------------------------------------------
int CBOINCHtmlListBox::HitTest(const wxPoint& pos)
{
if ( CBOINCVListBox::HitTest(pos) == wxNOT_FOUND )
return wxNOT_FOUND;
int x, y;
size_t n = GetItemCount();
wxRect r;
GetViewStart(&x, &y);
wxPoint p(-x*PIXELS_PER_HORIZONTAL_SCROLL_UNIT, -y*PIXELS_PER_VERTICAL_SCROLL_UNIT);
p.y -= GetMargins().y + CELL_BORDER;
p.x += GetMargins().x - CELL_BORDER;
r.SetTopLeft(p);
r.SetHeight(0);
for (size_t i = 0; i < n; ++i) {
CacheItem(i);
wxHtmlCell *aCell = m_cache->Get(i);
r.height = OnGetItemHeight(i);
r.width = aCell->GetWidth();
if (r.Contains(pos)) {
// convert mouse coordinates to coords relative to item's wxHtmlCell:
return i;
}
r.y += r.height; // + (2 * GetMargins().y);
}
return wxNOT_FOUND;
}
void CBOINCHtmlListBox::GetItemRect(size_t item, wxRect& rect)
{
wxPoint pos = GetRootCellCoords(item);
pos.x += GetMargins().x - CELL_BORDER;
pos.y -= GetMargins().y + CELL_BORDER;
rect.SetTopLeft(pos);
CacheItem(item);
wxHtmlCell *cell = m_cache->Get(item);
rect.height = OnGetItemHeight(item);
rect.width = cell->GetWidth();
}
// ----------------------------------------------------------------------------
// CBOINCHtmlListBox handling of HTML links
// ----------------------------------------------------------------------------
wxPoint CBOINCHtmlListBox::GetRootCellCoords(size_t n) const
{
int x, y;
// wxPoint pos(GetMargins().x - CELL_BORDER, -GetMargins().y - CELL_BORDER);
wxPoint pos(0, 0);
for (size_t i = 0; i < n; ++i) {
pos.y += OnGetItemHeight(i);
}
GetViewStart(&x, &y);
pos.x -= x*PIXELS_PER_HORIZONTAL_SCROLL_UNIT;
pos.y -= y*PIXELS_PER_VERTICAL_SCROLL_UNIT;
return pos;
}
bool CBOINCHtmlListBox::PhysicalCoordsToCell(wxPoint& pos, wxHtmlCell*& cell) const
{
if ( CBOINCVListBox::HitTest(pos) == wxNOT_FOUND )
return false;
int x, y;
size_t n = GetItemCount();
wxRect r;
wxPoint p(CELL_BORDER, CELL_BORDER);
p += GetMargins();
GetViewStart(&x, &y);
p.x -= x*PIXELS_PER_HORIZONTAL_SCROLL_UNIT;
p.y -= y*PIXELS_PER_VERTICAL_SCROLL_UNIT;
r.SetTopLeft(p);
r.SetHeight(0);
for (size_t i = 0; i < n; ++i) {
CacheItem(i);
wxHtmlCell *aCell = m_cache->Get(i);
r.height = aCell->GetHeight() + aCell->GetDescent() + (2 * CELL_BORDER);
r.width = aCell->GetWidth();
if (r.Contains(pos)) {
cell = aCell;
// convert mouse coordinates to coords relative to item's wxHtmlCell:
pos -= r.GetTopLeft();
return true;
}
r.y += r.height + (2 * GetMargins().y);
}
return false;
}
size_t CBOINCHtmlListBox::GetItemForCell(const wxHtmlCell *cell) const
{
wxCHECK_MSG( cell, 0, _T("no cell") );
cell = cell->GetRootCell();
wxCHECK_MSG( cell, 0, _T("no root cell") );
// the cell's ID contains item index, see CacheItem():
unsigned long n;
if ( !cell->GetId().ToULong(&n) )
{
wxFAIL_MSG( _T("unexpected root cell's ID") );
return 0;
}
return n;
}
wxPoint
CBOINCHtmlListBox::CellCoordsToPhysical(const wxPoint& pos, wxHtmlCell *cell) const
{
return pos + GetRootCellCoords(GetItemForCell(cell));
}
void CBOINCHtmlListBox::OnInternalIdle()
{
CBOINCVListBox::OnInternalIdle();
if ( wxHtmlWindowMouseHelper::DidMouseMove() )
{
wxPoint pos = ScreenToClient(wxGetMousePosition());
wxHtmlCell *cell;
if ( !PhysicalCoordsToCell(pos, cell) )
return;
wxHtmlWindowMouseHelper::HandleIdle(cell, pos);
}
}
void CBOINCHtmlListBox::OnMouseMove(wxMouseEvent& event)
{
wxHtmlWindowMouseHelper::HandleMouseMoved();
event.Skip();
}
void CBOINCHtmlListBox::OnLeftDown(wxMouseEvent& event)
{
wxPoint pos = event.GetPosition();
wxHtmlCell *cell;
if ( !PhysicalCoordsToCell(pos, cell) )
{
event.Skip();
return;
}
if ( !wxHtmlWindowMouseHelper::HandleMouseClick(cell, pos, event) )
{
// no link was clicked, so let the listbox code handle the click (e.g.
// by selecting another item in the list):
event.Skip();
}
}
#endif // wxUSE_HTML

196
clientgui/BOINCHtmlLBox.h Normal file
View File

@ -0,0 +1,196 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// Adapted from htmllbox.h by Vadim Zeitlin <vadim@wxwindows.org>
#ifndef _HTMLLBOX_H_
#define _HTMLLBOX_H_
#include "BOINCVListBox.h" // base class
class WXDLLIMPEXP_FWD_HTML wxHtmlCell;
class WXDLLIMPEXP_FWD_HTML wxHtmlWinParser;
class WXDLLIMPEXP_FWD_HTML CBOINCHtmlListBoxCache;
class WXDLLIMPEXP_FWD_HTML CBOINCHtmlListBoxStyle;
extern WXDLLIMPEXP_DATA_HTML(const wxChar) BOINCHtmlListBoxNameStr[];
// ----------------------------------------------------------------------------
// CBOINCHtmlListBox
// ----------------------------------------------------------------------------
// small border always added to the cells:
#define CELL_BORDER 2
class WXDLLIMPEXP_HTML CBOINCHtmlListBox : public CBOINCVListBox,
public wxHtmlWindowInterface,
public wxHtmlWindowMouseHelper
{
DECLARE_ABSTRACT_CLASS(CBOINCHtmlListBox)
public:
// constructors and such
// ---------------------
// default constructor, you must call Create() later
CBOINCHtmlListBox();
// normal constructor which calls Create() internally
CBOINCHtmlListBox(wxWindow *parent,
wxWindowID id = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxString& name = BOINCHtmlListBoxNameStr);
// really creates the control and sets the initial number of items in it
// (which may be changed later with SetItemCount())
//
// the only special style which may be specified here is wxLB_MULTIPLE
//
// returns true on success or false if the control couldn't be created
bool Create(wxWindow *parent,
wxWindowID id = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxString& name = BOINCHtmlListBoxNameStr);
// destructor cleans up whatever resources we use
virtual ~CBOINCHtmlListBox();
// override some base class virtuals
virtual void RefreshItem(size_t line);
virtual void RefreshItems(size_t from, size_t to);
virtual void SetItemCount(size_t count);
#if wxUSE_FILESYSTEM
// retrieve the file system used by the wxHtmlWinParser: if you use
// relative paths in your HTML, you should use its ChangePathTo() method
wxFileSystem& GetFileSystem() { return m_filesystem; }
const wxFileSystem& GetFileSystem() const { return m_filesystem; }
#endif // wxUSE_FILESYSTEM
virtual void OnInternalIdle();
int HitTest(const wxPoint& pos);
void GetItemRect(size_t item, wxRect& rect) ;
protected:
// this method must be implemented in the derived class and should return
// the body (i.e. without <html>) of the HTML for the given item
virtual wxString OnGetItem(size_t n) const = 0;
// this function may be overridden to decorate HTML returned by OnGetItem()
virtual wxString OnGetItemMarkup(size_t n) const;
// this method allows to customize the selection appearance: it may be used
// to specify the colour of the text which normally has the given colour
// colFg when it is inside the selection
//
// by default, the original colour is not used at all and all text has the
// same (default for this system) colour inside selection
virtual wxColour GetSelectedTextColour(const wxColour& colFg) const;
// this is the same as GetSelectedTextColour() but allows to customize the
// background colour -- this is even more rarely used as you can change it
// globally using SetSelectionBackground()
virtual wxColour GetSelectedTextBgColour(const wxColour& colBg) const;
// These functions are not supposed to be overridden by our descendants
virtual void OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const;
virtual wxCoord OnMeasureItem(size_t n) const;
virtual wxCoord GetMaxItemWidth() const;
// This method may be overriden to handle clicking on a link in
// the listbox. By default, clicking links is ignored.
virtual void OnLinkClicked(size_t n, const wxHtmlLinkInfo& link);
// event handlers
void OnSize(wxSizeEvent& event);
void OnMouseMove(wxMouseEvent& event);
void OnLeftDown(wxMouseEvent& event);
// common part of all ctors
void Init();
// ensure that the given item is cached
void CacheItem(size_t n) const;
private:
// wxHtmlWindowInterface methods:
virtual void SetHTMLWindowTitle(const wxString& title);
virtual void OnHTMLLinkClicked(const wxHtmlLinkInfo& link);
virtual wxHtmlOpeningStatus OnHTMLOpeningURL(wxHtmlURLType type,
const wxString& url,
wxString *redirect) const;
virtual wxPoint HTMLCoordsToWindow(wxHtmlCell *cell,
const wxPoint& pos) const;
virtual wxWindow* GetHTMLWindow();
virtual wxColour GetHTMLBackgroundColour() const;
virtual void SetHTMLBackgroundColour(const wxColour& clr);
virtual void SetHTMLBackgroundImage(const wxBitmap& bmpBg);
virtual void SetHTMLStatusText(const wxString& text);
virtual wxCursor GetHTMLCursor(HTMLCursor type) const;
// returns index of item that contains given HTML cell
size_t GetItemForCell(const wxHtmlCell *cell) const;
// return physical coordinates of root wxHtmlCell of n-th item
wxPoint GetRootCellCoords(size_t n) const;
// Converts physical coordinates stored in @a pos into coordinates
// relative to the root cell of the item under mouse cursor, if any. If no
// cell is found under the cursor, returns false. Otherwise stores the new
// coordinates back into @a pos and pointer to the cell under cursor into
// @a cell and returns true.
bool PhysicalCoordsToCell(wxPoint& pos, wxHtmlCell*& cell) const;
// The opposite of PhysicalCoordsToCell: converts coordinates relative to
// given cell to physical coordinates in the window
wxPoint CellCoordsToPhysical(const wxPoint& pos, wxHtmlCell *cell) const;
private:
// this class caches the pre-parsed HTML to speed up display
CBOINCHtmlListBoxCache *m_cache;
// HTML parser we use
wxHtmlWinParser *m_htmlParser;
#if wxUSE_FILESYSTEM
// file system used by m_htmlParser
wxFileSystem m_filesystem;
#endif // wxUSE_FILESYSTEM
// rendering style for the parser which allows us to customize our colours
CBOINCHtmlListBoxStyle *m_htmlRendStyle;
// it calls our GetSelectedTextColour() and GetSelectedTextBgColour()
friend class CBOINCHtmlListBoxStyle;
friend class wxHtmlListBoxWinInterface;
DECLARE_EVENT_TABLE()
DECLARE_NO_COPY_CLASS(CBOINCHtmlListBox)
};
#endif // _WX_HTMLLBOX_H_

690
clientgui/BOINCVListBox.cpp Normal file
View File

@ -0,0 +1,690 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// Adapted from vlbox.cpp by Vadim Zeitlin <vadim@wxwindows.org>
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#if wxUSE_LISTBOX
#include "BOINCVListBox.h"
#include "wx/selstore.h"
// ----------------------------------------------------------------------------
// event tables
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(CBOINCVListBox, wxScrolledWindow)
EVT_PAINT(CBOINCVListBox::OnPaint)
EVT_SIZE(CBOINCVListBox::OnSize)
#if ALLOW_NOTICES_SELECTION
EVT_KEY_DOWN(CBOINCVListBox::OnKeyDown)
EVT_LEFT_DOWN(CBOINCVListBox::OnLeftDown)
EVT_LEFT_DCLICK(CBOINCVListBox::OnLeftDClick)
#endif // ALLOW_NOTICES_SELECTION
END_EVENT_TABLE()
// ============================================================================
// implementation
// ============================================================================
IMPLEMENT_ABSTRACT_CLASS(CBOINCVListBox, wxScrolledWindow)
// ----------------------------------------------------------------------------
// CBOINCVListBox creation
// ----------------------------------------------------------------------------
void CBOINCVListBox::Init()
{
m_current =
m_anchor = wxNOT_FOUND;
m_selStore = NULL;
m_iItemCount = 0;
}
bool CBOINCVListBox::Create(wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
style |= wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE;
if ( !wxScrolledWindow::Create(parent, id, pos, size, style, name) )
return false;
if ( style & wxLB_MULTIPLE )
m_selStore = new wxSelectionStore;
// make sure the native widget has the right colour since we do
// transparent drawing by default
SetBackgroundColour(GetBackgroundColour());
m_colBgSel = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
// flicker-free drawing requires this
SetBackgroundStyle(wxBG_STYLE_CUSTOM);
return true;
}
CBOINCVListBox::~CBOINCVListBox()
{
delete m_selStore;
}
void CBOINCVListBox::SetItemCount(size_t count)
{
// don't leave the current index invalid
if ( m_current != wxNOT_FOUND && (size_t)m_current >= count )
m_current = count - 1; // also ok when count == 0 as wxNOT_FOUND == -1
if ( m_selStore )
{
// tell the selection store that our number of items has changed
m_selStore->SetItemCount(count);
}
m_iItemCount = count;
UpdateScrollbar();
}
void CBOINCVListBox::UpdateScrollbar() {
// do sum up their heights
int height = 0, width = GetMaxItemWidth();
for ( int i = 0; i < m_iItemCount; ++i )
{
height += OnGetItemHeight(i);
}
SetVirtualSize(width, height);
SetScrollRate(0, PIXELS_PER_VERTICAL_SCROLL_UNIT);
}
#if ALLOW_NOTICES_SELECTION
// ----------------------------------------------------------------------------
// selection handling
// ----------------------------------------------------------------------------
bool CBOINCVListBox::IsSelected(size_t line) const
{
return m_selStore ? m_selStore->IsSelected(line) : (int)line == m_current;
}
bool CBOINCVListBox::Select(size_t item, bool select)
{
wxCHECK_MSG( m_selStore, false,
_T("Select() may only be used with multiselection listbox") );
wxCHECK_MSG( item < GetItemCount(), false,
_T("Select(): invalid item index") );
bool changed = m_selStore->SelectItem(item, select);
if ( changed )
{
// selection really changed
RefreshItem(item);
}
DoSetCurrent(item);
return changed;
}
bool CBOINCVListBox::SelectRange(size_t from, size_t to)
{
wxCHECK_MSG( m_selStore, false,
_T("SelectRange() may only be used with multiselection listbox") );
// make sure items are in correct order
if ( from > to )
{
size_t tmp = from;
from = to;
to = tmp;
}
wxCHECK_MSG( to < GetItemCount(), false,
_T("SelectRange(): invalid item index") );
wxArrayInt changed;
if ( !m_selStore->SelectRange(from, to, true, &changed) )
{
// too many items have changed, we didn't record them in changed array
// so we have no choice but to refresh everything between from and to
RefreshItems(from, to);
}
else // we've got the indices of the changed items
{
const size_t count = changed.GetCount();
if ( !count )
{
// nothing changed
return false;
}
// refresh just the lines which have really changed
for ( size_t n = 0; n < count; n++ )
{
RefreshItem(changed[n]);
}
}
// something changed
return true;
}
bool CBOINCVListBox::DoSelectAll(bool select)
{
wxCHECK_MSG( m_selStore, false,
_T("SelectAll may only be used with multiselection listbox") );
size_t count = GetItemCount();
if ( count )
{
wxArrayInt changed;
if ( !m_selStore->SelectRange(0, count - 1, select) ||
!changed.IsEmpty() )
{
Refresh();
// something changed
return true;
}
}
return false;
}
bool CBOINCVListBox::DoSetCurrent(int current)
{
wxASSERT_MSG( current == wxNOT_FOUND ||
(current >= 0 && (size_t)current < GetItemCount()),
_T("CBOINCVListBox::DoSetCurrent(): invalid item index") );
if ( current == m_current )
{
// nothing to do
return false;
}
if ( m_current != wxNOT_FOUND )
RefreshItem(m_current);
m_current = current;
if ( m_current != wxNOT_FOUND )
{
// if the line is not visible at all, we scroll it into view but we
// don't need to refresh it -- it will be redrawn anyhow
if ( !IsVisible(m_current) )
{
ScrollToLine(m_current);
}
else // line is at least partly visible
{
// it is, indeed, only partly visible, so scroll it into view to
// make it entirely visible
while ( (size_t)m_current == GetLastVisibleLine() &&
ScrollToLine(GetVisibleBegin()+1) ) ;
// but in any case refresh it as even if it was only partly visible
// before we need to redraw it entirely as its background changed
RefreshItem(m_current);
}
}
return true;
}
void CBOINCVListBox::SendSelectedEvent()
{
wxASSERT_MSG( m_current != wxNOT_FOUND,
_T("SendSelectedEvent() shouldn't be called") );
wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, GetId());
event.SetEventObject(this);
event.SetInt(m_current);
(void)GetEventHandler()->ProcessEvent(event);
}
void CBOINCVListBox::SetSelection(int selection)
{
wxCHECK_RET( selection == wxNOT_FOUND ||
(selection >= 0 && (size_t)selection < GetItemCount()),
_T("CBOINCVListBox::SetSelection(): invalid item index") );
if ( HasMultipleSelection() )
{
if (selection != wxNOT_FOUND)
Select(selection);
else
DeselectAll();
m_anchor = selection;
}
DoSetCurrent(selection);
}
size_t CBOINCVListBox::GetSelectedCount() const
{
return m_selStore ? m_selStore->GetSelectedCount()
: m_current == wxNOT_FOUND ? 0 : 1;
}
int CBOINCVListBox::GetFirstSelected(unsigned long& cookie) const
{
cookie = 0;
return GetNextSelected(cookie);
}
int CBOINCVListBox::GetNextSelected(unsigned long& cookie) const
{
wxCHECK_MSG( m_selStore, wxNOT_FOUND,
_T("GetFirst/NextSelected() may only be used with multiselection listboxes") );
while ( cookie < GetItemCount() )
{
if ( IsSelected(cookie++) )
return cookie - 1;
}
return wxNOT_FOUND;
}
#endif // ALLOW_NOTICES_SELECTION
// ----------------------------------------------------------------------------
// CBOINCVListBox appearance parameters
// ----------------------------------------------------------------------------
void CBOINCVListBox::SetMargins(const wxPoint& pt)
{
if ( pt != m_ptMargins )
{
m_ptMargins = pt;
Refresh();
}
}
void CBOINCVListBox::SetSelectionBackground(const wxColour& col)
{
m_colBgSel = col;
}
// ----------------------------------------------------------------------------
// CBOINCVListBox painting
// ----------------------------------------------------------------------------
wxCoord CBOINCVListBox::OnGetItemHeight(size_t item) const
{
return OnMeasureItem(item) + (2 * m_ptMargins.y);
}
void CBOINCVListBox::OnDrawSeparator(wxDC& WXUNUSED(dc),
wxRect& WXUNUSED(rect),
size_t WXUNUSED(n)) const
{
}
void CBOINCVListBox::OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const
{
#if ALLOW_NOTICES_SELECTION
// we need to render selected and current items differently
const bool isSelected = IsSelected(n),
isCurrent = IsCurrent(n);
if ( isSelected || isCurrent )
{
if ( isSelected )
{
dc.SetBrush(wxBrush(m_colBgSel, wxSOLID));
}
else // !selected
{
dc.SetBrush(*wxTRANSPARENT_BRUSH);
}
dc.SetPen(*(isCurrent ? wxBLACK_PEN : wxTRANSPARENT_PEN));
dc.DrawRectangle(rect);
}
//else: do nothing for the normal items
#endif // ALLOW_NOTICES_SELECTION
}
void CBOINCVListBox::OnPaint(wxPaintEvent& WXUNUSED(event))
{
int x, y;
int itemMax;
wxRect rectItem;
wxSize clientSize = GetClientSize();
wxAutoBufferedPaintDC dc(this);
DoPrepareDC(dc);
// the update rectangle
wxRect rectUpdate = GetUpdateClientRect();
GetViewStart(&x, &y);
rectUpdate.x += x*PIXELS_PER_HORIZONTAL_SCROLL_UNIT;
rectUpdate.y += y*PIXELS_PER_VERTICAL_SCROLL_UNIT;
// fill it with background colour
dc.SetBackground(GetBackgroundColour());
dc.Clear();
// the bounding rectangle of the current item
rectItem.width = clientSize.x;
// iterate over all items
itemMax = GetItemCount();
for ( int item = 0; item < itemMax; item++ )
{
const wxCoord hItem = OnGetItemHeight(item);
rectItem.height = hItem;
// and draw the ones which intersect the update rect
if ( rectItem.Intersects(rectUpdate) )
{
// don't allow drawing outside of the items rectangle
wxDCClipper clip(dc, rectItem);
wxRect rect = rectItem;
OnDrawBackground(dc, rect, item);
OnDrawSeparator(dc, rect, item);
rect.Deflate(m_ptMargins.x, m_ptMargins.y);
OnDrawItem(dc, rect, item);
}
else // no intersection
{
if ( rectItem.GetTop() > rectUpdate.GetBottom() )
{
// we are already below the update rect, no need to continue
// further
break;
}
//else: the next item may intersect the update rect
}
rectItem.y += hItem;
}
}
void CBOINCVListBox::OnSize(wxSizeEvent& event)
{
UpdateScrollbar();
event.Skip();
}
#if ALLOW_NOTICES_SELECTION
// ============================================================================
// CBOINCVListBox keyboard/mouse handling
// ============================================================================
void CBOINCVListBox::DoHandleItemClick(int item, int flags)
{
// has anything worth telling the client code about happened?
bool notify = false;
if ( HasMultipleSelection() )
{
// select the iteem clicked?
bool select = true;
// NB: the keyboard interface we implement here corresponds to
// wxLB_EXTENDED rather than wxLB_MULTIPLE but this one makes more
// sense IMHO
if ( flags & ItemClick_Shift )
{
if ( m_current != wxNOT_FOUND )
{
if ( m_anchor == wxNOT_FOUND )
m_anchor = m_current;
select = false;
// only the range from the selection anchor to new m_current
// must be selected
if ( DeselectAll() )
notify = true;
if ( SelectRange(m_anchor, item) )
notify = true;
}
//else: treat it as ordinary click/keypress
}
else // Shift not pressed
{
m_anchor = item;
if ( flags & ItemClick_Ctrl )
{
select = false;
if ( !(flags & ItemClick_Kbd) )
{
Toggle(item);
// the status of the item has definitely changed
notify = true;
}
//else: Ctrl-arrow pressed, don't change selection
}
//else: behave as in single selection case
}
if ( select )
{
// make the clicked item the only selection
if ( DeselectAll() )
notify = true;
if ( Select(item) )
notify = true;
}
}
// in any case the item should become the current one
if ( DoSetCurrent(item) )
{
if ( !HasMultipleSelection() )
{
// this has also changed the selection for single selection case
notify = true;
}
}
if ( notify )
{
// notify the user about the selection change
SendSelectedEvent();
}
//else: nothing changed at all
}
// ----------------------------------------------------------------------------
// keyboard handling
// ----------------------------------------------------------------------------
void CBOINCVListBox::OnKeyDown(wxKeyEvent& event)
{
// flags for DoHandleItemClick()
int flags = ItemClick_Kbd;
int current;
switch ( event.GetKeyCode() )
{
case WXK_HOME:
current = 0;
break;
case WXK_END:
current = GetLineCount() - 1;
break;
case WXK_DOWN:
if ( m_current == (int)GetLineCount() - 1 )
return;
current = m_current + 1;
break;
case WXK_UP:
if ( m_current == wxNOT_FOUND )
current = GetLineCount() - 1;
else if ( m_current != 0 )
current = m_current - 1;
else // m_current == 0
return;
break;
case WXK_PAGEDOWN:
PageDown();
current = GetFirstVisibleLine();
break;
case WXK_PAGEUP:
if ( m_current == (int)GetFirstVisibleLine() )
{
PageUp();
}
current = GetFirstVisibleLine();
break;
case WXK_SPACE:
// hack: pressing space should work like a mouse click rather than
// like a keyboard arrow press, so trick DoHandleItemClick() in
// thinking we were clicked
flags &= ~ItemClick_Kbd;
current = m_current;
break;
#ifdef __WXMSW__
case WXK_TAB:
// Since we are using wxWANTS_CHARS we need to send navigation
// events for the tabs on MSW
{
wxNavigationKeyEvent ne;
ne.SetDirection(!event.ShiftDown());
ne.SetCurrentFocus(this);
ne.SetEventObject(this);
GetParent()->GetEventHandler()->ProcessEvent(ne);
}
// fall through to default
#endif
default:
event.Skip();
current = 0; // just to silent the stupid compiler warnings
wxUnusedVar(current);
return;
}
if ( event.ShiftDown() )
flags |= ItemClick_Shift;
if ( event.ControlDown() )
flags |= ItemClick_Ctrl;
DoHandleItemClick(current, flags);
}
// ----------------------------------------------------------------------------
// CBOINCVListBox mouse handling
// ----------------------------------------------------------------------------
void CBOINCVListBox::OnLeftDown(wxMouseEvent& event)
{
SetFocus();
int item = HitTest(event.GetPosition());
if ( item != wxNOT_FOUND )
{
int flags = 0;
if ( event.ShiftDown() )
flags |= ItemClick_Shift;
// under Mac Apple-click is used in the same way as Ctrl-click
// elsewhere
#ifdef __WXMAC__
if ( event.MetaDown() )
#else
if ( event.ControlDown() )
#endif
flags |= ItemClick_Ctrl;
DoHandleItemClick(item, flags);
}
}
void CBOINCVListBox::OnLeftDClick(wxMouseEvent& eventMouse)
{
int item = HitTest(eventMouse.GetPosition());
if ( item != wxNOT_FOUND )
{
// if item double-clicked was not yet selected, then treat
// this event as a left-click instead
if ( item == m_current )
{
wxCommandEvent event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, GetId());
event.SetEventObject(this);
event.SetInt(item);
(void)GetEventHandler()->ProcessEvent(event);
}
else
{
OnLeftDown(eventMouse);
}
}
}
#endif // ALLOW_NOTICES_SELECTION
// ----------------------------------------------------------------------------
// use the same default attributes as wxListBox
// ----------------------------------------------------------------------------
//static
wxVisualAttributes
CBOINCVListBox::GetClassDefaultAttributes(wxWindowVariant variant)
{
return wxListBox::GetClassDefaultAttributes(variant);
}
#endif

340
clientgui/BOINCVListBox.h Normal file
View File

@ -0,0 +1,340 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// Adapted from vlbox.h by Vadim Zeitlin <vadim@wxwindows.org>
#ifndef _VLBOX_H_
#define _VLBOX_H_
//#include "wx/vscroll.h" // base class
//#include "wx/bitmap.h"
class WXDLLIMPEXP_FWD_CORE wxSelectionStore;
#define BOINCVListBoxNameStr _T("BOINCVListBox")
#define ALLOW_NOTICES_SELECTION false
#define PIXELS_PER_VERTICAL_SCROLL_UNIT 10
#define PIXELS_PER_HORIZONTAL_SCROLL_UNIT 10
// ----------------------------------------------------------------------------
// CBOINCVListBox
// ----------------------------------------------------------------------------
/*
This class has two main differences from a regular listbox: it can have an
arbitrarily huge number of items because it doesn't store them itself but
uses OnDrawItem() callback to draw them and its items can have variable
height as determined by OnMeasureItem().
It emits the same events as wxListBox and the same event macros may be used
with it.
*/
class WXDLLEXPORT CBOINCVListBox : public wxScrolledWindow
{
public:
// constructors and such
// ---------------------
// default constructor, you must call Create() later
CBOINCVListBox() { Init(); }
// normal constructor which calls Create() internally
CBOINCVListBox(wxWindow *parent,
wxWindowID id = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxString& name = BOINCVListBoxNameStr)
{
Init();
(void)Create(parent, id, pos, size, style, name);
}
// really creates the control and sets the initial number of items in it
// (which may be changed later with SetItemCount())
//
// the only special style which may be specified here is wxLB_MULTIPLE
//
// returns true on success or false if the control couldn't be created
bool Create(wxWindow *parent,
wxWindowID id = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxString& name = BOINCVListBoxNameStr);
// dtor does some internal cleanup (deletes m_selStore if any)
virtual ~CBOINCVListBox();
// common part of all ctors
void Init();
// accessors
// ---------
// get the number of items in the control
size_t GetItemCount() const { return m_iItemCount; }
// is this item the current one?
bool IsCurrent(size_t item) const { return item == (size_t)m_current; }
#if ALLOW_NOTICES_SELECTION
// does this control use multiple selection?
bool HasMultipleSelection() const { return m_selStore != NULL; }
// get the currently selected item or wxNOT_FOUND if there is no selection
//
// this method is only valid for the single selection listboxes
int GetSelection() const
{
wxASSERT_MSG( !HasMultipleSelection(),
_T("GetSelection() can't be used with wxLB_MULTIPLE") );
return m_current;
}
#ifdef __WXUNIVERSAL__
bool IsCurrent() const { return wxScrolledWindow::IsCurrent(); }
#endif
// is this item selected?
bool IsSelected(size_t item) const;
// get the number of the selected items (maybe 0)
//
// this method is valid for both single and multi selection listboxes
size_t GetSelectedCount() const;
// get the first selected item, returns wxNOT_FOUND if none
//
// cookie is an opaque parameter which should be passed to
// GetNextSelected() later
//
// this method is only valid for the multi selection listboxes
int GetFirstSelected(unsigned long& cookie) const;
// get next selection item, return wxNOT_FOUND if no more
//
// cookie must be the same parameter that was passed to GetFirstSelected()
// before
//
// this method is only valid for the multi selection listboxes
int GetNextSelected(unsigned long& cookie) const;
#else
bool IsSelected(size_t item) const { return false; }
#endif // ALLOW_NOTICES_SELECTION
// get the margins around each item
wxPoint GetMargins() const { return m_ptMargins; }
// get the background colour of selected cells
const wxColour& GetSelectionBackground() const { return m_colBgSel; }
// operations
// ----------
// set the number of items to be shown in the control
virtual void SetItemCount(size_t count);
// delete all items from the control
void Clear() { SetItemCount(0); }
void UpdateScrollbar();
#if ALLOW_NOTICES_SELECTION
// set the selection to the specified item, if it is wxNOT_FOUND the
// selection is unset
//
// this function is only valid for the single selection listboxes
void SetSelection(int selection);
// selects or deselects the specified item which must be valid (i.e. not
// equal to wxNOT_FOUND)
//
// return true if the items selection status has changed or false
// otherwise
//
// this function is only valid for the multiple selection listboxes
bool Select(size_t item, bool select = true);
// selects the items in the specified range whose end points may be given
// in any order
//
// return true if any items selection status has changed, false otherwise
//
// this function is only valid for the single selection listboxes
bool SelectRange(size_t from, size_t to);
// toggle the selection of the specified item (must be valid)
//
// this function is only valid for the multiple selection listboxes
void Toggle(size_t item) { Select(item, !IsSelected(item)); }
// select all items in the listbox
//
// the return code indicates if any items were affected by this operation
// (true) or if nothing has changed (false)
bool SelectAll() { return DoSelectAll(true); }
// unselect all items in the listbox
//
// the return code has the same meaning as for SelectAll()
bool DeselectAll() { return DoSelectAll(false); }
#else
void SetSelection(int selection) { };
#endif // ALLOW_NOTICES_SELECTION
virtual void RefreshItem(size_t line) = 0;
virtual void RefreshItems(size_t from, size_t to) = 0;
// set the margins: horizontal margin is the distance between the window
// border and the item contents while vertical margin is half of the
// distance between items
//
// by default both margins are 0
void SetMargins(const wxPoint& pt);
void SetMargins(wxCoord x, wxCoord y) { SetMargins(wxPoint(x, y)); }
// change the background colour of the selected cells
void SetSelectionBackground(const wxColour& col);
virtual wxVisualAttributes GetDefaultAttributes() const
{
return GetClassDefaultAttributes(GetWindowVariant());
}
static wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL);
protected:
// the derived class must implement this function to actually draw the item
// with the given index on the provided DC
virtual void OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const = 0;
// the derived class must implement this method to return the height of the
// specified item
virtual wxCoord OnMeasureItem(size_t n) const = 0;
// the derived class must implement this method to return the max width of
// all the items
virtual wxCoord GetMaxItemWidth() const = 0;
// this method may be used to draw separators between the lines; note that
// the rectangle may be modified, typically to deflate it a bit before
// passing to OnDrawItem()
//
// the base class version doesn't do anything
virtual void OnDrawSeparator(wxDC& dc, wxRect& rect, size_t n) const;
// this method is used to draw the items background and, maybe, a border
// around it
//
// the base class version implements a reasonable default behaviour which
// consists in drawing the selected item with the standard background
// colour and drawing a border around the item if it is either selected or
// current
virtual void OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const;
// we implement OnGetLineHeight() in terms of OnMeasureItem() because this
// allows us to add borders to the items easily
//
// this function is not supposed to be overridden by the derived classes
virtual wxCoord OnGetItemHeight(size_t item) const;
// event handlers
void OnPaint(wxPaintEvent& event);
void OnSize(wxSizeEvent& event);
#if ALLOW_NOTICES_SELECTION
void OnKeyDown(wxKeyEvent& event);
void OnLeftDown(wxMouseEvent& event);
void OnLeftDClick(wxMouseEvent& event);
// send the wxEVT_COMMAND_LISTBOX_SELECTED event
void SendSelectedEvent();
// common implementation of SelectAll() and DeselectAll()
bool DoSelectAll(bool select);
// change the current item (in single selection listbox it also implicitly
// changes the selection); current may be wxNOT_FOUND in which case there
// will be no current item any more
//
// return true if the current item changed, false otherwise
bool DoSetCurrent(int current);
// flags for DoHandleItemClick
enum
{
ItemClick_Shift = 1, // item shift-clicked
ItemClick_Ctrl = 2, // ctrl
ItemClick_Kbd = 4 // item selected from keyboard
};
// common part of keyboard and mouse handling processing code
void DoHandleItemClick(int item, int flags);
#endif // ALLOW_NOTICES_SELECTION
private:
int m_iItemCount;
// the current item or wxNOT_FOUND
//
// if m_selStore == NULL this is also the selected item, otherwise the
// selections are managed by m_selStore
int m_current;
// the anchor of the selection for the multiselection listboxes:
// shift-clicking an item extends the selection from m_anchor to the item
// clicked, for example
//
// always wxNOT_FOUND for single selection listboxes
int m_anchor;
// the object managing our selected items if not NULL
wxSelectionStore *m_selStore;
// margins
wxPoint m_ptMargins;
// the selection bg colour
wxColour m_colBgSel;
DECLARE_EVENT_TABLE()
DECLARE_NO_COPY_CLASS(CBOINCVListBox)
DECLARE_ABSTRACT_CLASS(CBOINCVListBox)
};
#endif // _VLBOX_H_

View File

@ -99,23 +99,9 @@ wxAccStatus CNoticeListCtrlAccessible::GetLocation(wxRect& rect, int elementId)
}
else if (pCtrl && (0 != elementId))
{
// List item
wxSize cCtrlSize = pCtrl->GetClientSize();
pCtrl->GetItemRect(elementId - 1, rect);
pCtrl->ClientToScreen(&rect.x, &rect.y);
// Set the initial control postition to the absolute coords of the upper
// left hand position of the control
rect.SetPosition(pCtrl->GetScreenPosition());
rect.width = cCtrlSize.GetWidth() - 1;
rect.height = pCtrl->GetItemHeight(elementId - 1) - 1;
// Items can have different heights
int firstVisibleItem = (int)pCtrl->GetFirstVisibleLine();
int yOffset = 0;
for (int i=firstVisibleItem; i<(elementId - 1); ++i) {
yOffset += pCtrl->GetItemHeight((size_t)i);
}
rect.SetTop(rect.GetTop() + yOffset);
rect.height -= 1;
return wxACC_OK;
}
// Let the framework handle the other cases.
@ -143,6 +129,8 @@ wxAccStatus CNoticeListCtrlAccessible::GetChildCount(int* childCount)
// window (e.g. an edit control).
wxAccStatus CNoticeListCtrlAccessible::DoDefaultAction(int childId)
{
#if ALLOW_NOTICES_SELECTION
CNoticeListCtrl* pCtrl = wxDynamicCast(GetWindow(), CNoticeListCtrl);
CMainDocument* pDoc = wxDynamicCast(wxGetApp().GetDocument(), CMainDocument);
if (pCtrl && (childId != wxACC_SELF))
@ -168,6 +156,8 @@ wxAccStatus CNoticeListCtrlAccessible::DoDefaultAction(int childId)
return wxACC_OK;
}
#endif // ALLOW_NOTICES_SELECTION
// Let the framework handle the other cases.
return wxACC_NOT_IMPLEMENTED;
@ -403,7 +393,7 @@ DEFINE_EVENT_TYPE( wxEVT_NOTICELIST_ITEM_DISPLAY )
/*!
* CNoticeListCtrl type definition
*/
IMPLEMENT_DYNAMIC_CLASS( CNoticeListCtrl, wxHtmlListBox )
IMPLEMENT_DYNAMIC_CLASS( CNoticeListCtrl, CBOINCHtmlListBox )
IMPLEMENT_DYNAMIC_CLASS( NoticeListCtrlEvent, wxNotifyEvent )
@ -411,7 +401,7 @@ IMPLEMENT_DYNAMIC_CLASS( NoticeListCtrlEvent, wxNotifyEvent )
* CNoticeListCtrl event table definition
*/
BEGIN_EVENT_TABLE( CNoticeListCtrl, wxHtmlListBox )
BEGIN_EVENT_TABLE( CNoticeListCtrl, CBOINCHtmlListBox )
////@begin CNoticeListCtrl event table entries
EVT_LISTBOX(ID_LIST_NOTIFICATIONSVIEW, CNoticeListCtrl::OnSelected)
@ -456,7 +446,7 @@ bool CNoticeListCtrl::Create( wxWindow* parent )
////@end CNoticeListCtrl member initialisation
////@begin CNoticeListCtrl creation
wxHtmlListBox::Create( parent, ID_LIST_NOTIFICATIONSVIEW, wxDefaultPosition, wxDefaultSize,
CBOINCHtmlListBox::Create( parent, ID_LIST_NOTIFICATIONSVIEW, wxDefaultPosition, wxDefaultSize,
wxSUNKEN_BORDER | wxTAB_TRAVERSAL );
#if wxUSE_ACCESSIBILITY
@ -593,8 +583,12 @@ bool CNoticeListCtrl::UpdateUI()
wxASSERT(pDoc);
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
// Call Freeze() / Thaw() only when actually needed;
// otherwise it causes unnecessary redraws
if (pDoc->GetNoticeCount() < 0) {
Freeze();
SetItemCount(0);
Thaw();
return true;
}
@ -602,8 +596,10 @@ bool CNoticeListCtrl::UpdateUI()
((int)GetItemCount() != pDoc->GetNoticeCount()) ||
pDoc->notices.complete
) {
Freeze();
SetItemCount(pDoc->GetNoticeCount());
pDoc->notices.complete = false;
Thaw();
}
#ifdef __WXMAC__
// Enable accessibility only after drawing the page

View File

@ -22,6 +22,7 @@
#pragma interface "NoticeListCtrl.cpp"
#endif
#include "BOINCHtmlLBox.h"
#if wxUSE_ACCESSIBILITY || defined(__WXMAC__)
@ -85,7 +86,7 @@ public:
* CNoticeListCtrl class declaration
*/
class CNoticeListCtrl: public wxHtmlListBox
class CNoticeListCtrl: public CBOINCHtmlListBox
{
DECLARE_DYNAMIC_CLASS( CNoticeListCtrl )
DECLARE_EVENT_TABLE()
@ -112,7 +113,7 @@ public:
bool UpdateUI();
int GetItemHeight(size_t i) { return (int)OnMeasureItem(i); }
int GetItemHeight(size_t i) { return (int)OnGetItemHeight(i); }
private:
#ifdef __WXMAC__

View File

@ -138,9 +138,8 @@ void CViewNotices::OnListRender(wxTimerEvent& WXUNUSED(event)) {
}
}
m_pHtmlListPane->Freeze();
// Don't call Freeze() / Thaw() here because it causes an unnecessary redraw
m_pHtmlListPane->UpdateUI();
m_pHtmlListPane->Thaw();
pDoc->UpdateUnreadNoticeState();

View File

@ -169,6 +169,8 @@
DD4E704C0BCDAC360010A522 /* browser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD4E70490BCDAC360010A522 /* browser.cpp */; };
DD4EC65A08A0A7AF009AA08F /* gui_rpc_client_ops.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD73E34E08A0694000656EB1 /* gui_rpc_client_ops.cpp */; };
DD4EC65B08A0A83F009AA08F /* gui_rpc_client_print.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD73E34F08A0694100656EB1 /* gui_rpc_client_print.cpp */; };
DD4FA25D121828E400154856 /* BOINCHtmlLBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD4FA25B121828E400154856 /* BOINCHtmlLBox.cpp */; };
DD4FA2621218290600154856 /* BOINCVListBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD4FA2601218290600154856 /* BOINCVListBox.cpp */; };
DD51DC8F0A4943A300BD24E6 /* check_security.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD6617870A3FFD8C00FFEBEB /* check_security.cpp */; };
DD52C81108B5D44D008D9AA4 /* gui_rpc_client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD81C5CC07C5D7D90098A04D /* gui_rpc_client.cpp */; };
DD52C81208B5D44E008D9AA4 /* gui_rpc_client_ops.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD73E34E08A0694000656EB1 /* gui_rpc_client_ops.cpp */; };
@ -744,6 +746,10 @@
DD4E704A0BCDAC360010A522 /* browser.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = browser.h; path = ../clientgui/browser.h; sourceTree = SOURCE_ROOT; };
DD4EC60F08A0A083009AA08F /* texture.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = texture.cpp; sourceTree = "<group>"; };
DD4EC61008A0A083009AA08F /* texture.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = texture.h; path = ../api/texture.h; sourceTree = SOURCE_ROOT; };
DD4FA25B121828E400154856 /* BOINCHtmlLBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BOINCHtmlLBox.cpp; path = ../clientgui/BOINCHtmlLBox.cpp; sourceTree = SOURCE_ROOT; };
DD4FA25C121828E400154856 /* BOINCHtmlLBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BOINCHtmlLBox.h; path = ../clientgui/BOINCHtmlLBox.h; sourceTree = SOURCE_ROOT; };
DD4FA2601218290600154856 /* BOINCVListBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BOINCVListBox.cpp; path = ../clientgui/BOINCVListBox.cpp; sourceTree = SOURCE_ROOT; };
DD4FA2611218290600154856 /* BOINCVListBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BOINCVListBox.h; path = ../clientgui/BOINCVListBox.h; sourceTree = SOURCE_ROOT; };
DD531BC50C193D3800742E50 /* MacInstaller.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = MacInstaller.icns; path = ../clientgui/res/MacInstaller.icns; sourceTree = SOURCE_ROOT; };
DD531BC70C193D5200742E50 /* MacUninstaller.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = MacUninstaller.icns; path = ../clientgui/res/MacUninstaller.icns; sourceTree = SOURCE_ROOT; };
DD58C41808F3343F00C1DF66 /* AccountInfoPage.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = AccountInfoPage.cpp; path = ../clientgui/AccountInfoPage.cpp; sourceTree = SOURCE_ROOT; };
@ -1335,6 +1341,8 @@
DD8DD4A609D9432F0043019E /* BOINCDialupManager.h */,
DD81C40507C5D1020098A04D /* BOINCGUIApp.cpp */,
DD81C43B07C5D2240098A04D /* BOINCGUIApp.h */,
DD4FA25B121828E400154856 /* BOINCHtmlLBox.cpp */,
DD4FA25C121828E400154856 /* BOINCHtmlLBox.h */,
DD0A87FD11D950E00067C1F2 /* BOINCInternetFSHandler.cpp */,
DD0A87FE11D950E00067C1F2 /* BOINCInternetFSHandler.h */,
DD81C40407C5D1020098A04D /* BOINCListCtrl.cpp */,
@ -1343,6 +1351,8 @@
DD81C43D07C5D2240098A04D /* BOINCTaskBar.h */,
DD81C40607C5D1020098A04D /* BOINCTaskCtrl.cpp */,
DD81C43807C5D2240098A04D /* BOINCTaskCtrl.h */,
DD4FA2601218290600154856 /* BOINCVListBox.cpp */,
DD4FA2611218290600154856 /* BOINCVListBox.h */,
DD4E70490BCDAC360010A522 /* browser.cpp */,
DD4E704A0BCDAC360010A522 /* browser.h */,
DDCE78220A70BD29008218B6 /* common */,
@ -2571,6 +2581,8 @@
DDBB7E7611D2BCD400598DC1 /* NoticeListCtrl.cpp in Sources */,
DD0A87FF11D950E00067C1F2 /* BOINCInternetFSHandler.cpp in Sources */,
DDA850A41207EED900B473A6 /* WizardAttach.cpp in Sources */,
DD4FA25D121828E400154856 /* BOINCHtmlLBox.cpp in Sources */,
DD4FA2621218290600154856 /* BOINCVListBox.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};