PageRenderTime 40ms CodeModel.GetById 17ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llui/llscrolllistcolumn.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 336 lines | 237 code | 44 blank | 55 comment | 46 complexity | e4cfc0fcc7dfc30049270525ae96276c MD5 | raw file
  1/** 
  2 * @file llscrollcolumnheader.cpp
  3 * @brief Scroll lists are composed of rows (items), each of which 
  4 * contains columns (cells).
  5 *
  6 * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  7 * Second Life Viewer Source Code
  8 * Copyright (C) 2010, Linden Research, Inc.
  9 * 
 10 * This library is free software; you can redistribute it and/or
 11 * modify it under the terms of the GNU Lesser General Public
 12 * License as published by the Free Software Foundation;
 13 * version 2.1 of the License only.
 14 * 
 15 * This library is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18 * Lesser General Public License for more details.
 19 * 
 20 * You should have received a copy of the GNU Lesser General Public
 21 * License along with this library; if not, write to the Free Software
 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 23 * 
 24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 25 * $/LicenseInfo$
 26 */
 27
 28#include "linden_common.h"
 29
 30#include "llscrolllistcolumn.h"
 31
 32#include "llbutton.h"
 33#include "llresizebar.h"
 34#include "llscrolllistcell.h"
 35#include "llscrolllistctrl.h"
 36#include "llscrolllistitem.h"
 37#include "lluictrlfactory.h"
 38
 39const S32 MIN_COLUMN_WIDTH = 20;
 40
 41// defaults for LLScrollColumnHeader param block pulled from widgets/scroll_column_header.xml
 42static LLWidgetNameRegistry::StaticRegistrar sRegisterColumnHeaderParams(&typeid(LLScrollColumnHeader::Params), "scroll_column_header");
 43
 44//---------------------------------------------------------------------------
 45// LLScrollColumnHeader
 46//---------------------------------------------------------------------------
 47LLScrollColumnHeader::Params::Params()
 48:	column("column")
 49{}
 50
 51
 52LLScrollColumnHeader::LLScrollColumnHeader(const LLScrollColumnHeader::Params& p) 
 53:	LLButton(p), // use combobox params to steal images
 54	mColumn(p.column),
 55	mHasResizableElement(FALSE)
 56{
 57	setClickedCallback(boost::bind(&LLScrollColumnHeader::onClick, this, _2));
 58	
 59	// resize handles on left and right
 60	const S32 RESIZE_BAR_THICKNESS = 3;
 61	LLResizeBar::Params resize_bar_p;
 62	resize_bar_p.resizing_view(this);
 63	resize_bar_p.rect(LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0));
 64	resize_bar_p.min_size(MIN_COLUMN_WIDTH);
 65	resize_bar_p.side(LLResizeBar::RIGHT);
 66	resize_bar_p.enabled(false);
 67	mResizeBar = LLUICtrlFactory::create<LLResizeBar>(resize_bar_p);
 68	addChild(mResizeBar);
 69}
 70
 71LLScrollColumnHeader::~LLScrollColumnHeader()
 72{}
 73
 74void LLScrollColumnHeader::draw()
 75{
 76	std::string sort_column = mColumn->mParentCtrl->getSortColumnName();
 77	BOOL draw_arrow = !mColumn->mLabel.empty() 
 78			&& mColumn->mParentCtrl->isSorted()
 79			// check for indirect sorting column as well as column's sorting name
 80			&& (sort_column == mColumn->mSortingColumn || sort_column == mColumn->mName);
 81
 82	BOOL is_ascending = mColumn->mParentCtrl->getSortAscending();
 83	if (draw_arrow)
 84	{
 85		setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, LLColor4::white);
 86	}
 87	else
 88	{
 89		setImageOverlay(LLUUID::null);
 90	}
 91
 92	// Draw children
 93	LLButton::draw();
 94}
 95
 96BOOL LLScrollColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask)
 97{
 98	if (canResize() && mResizeBar->getRect().pointInRect(x, y))
 99	{
100		// reshape column to max content width
101		LLRect column_rect = getRect();
102		column_rect.mRight = column_rect.mLeft + mColumn->mMaxContentWidth;
103		setShape(column_rect, true);
104	}
105	else
106	{
107		onClick(LLSD());
108	}
109	return TRUE;
110}
111
112void LLScrollColumnHeader::onClick(const LLSD& data)
113{
114	if (mColumn)
115	{
116		LLScrollListCtrl::onClickColumn(mColumn);
117	}
118}
119
120LLView*	LLScrollColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding)
121{
122	// this logic assumes dragging on right
123	llassert(snap_edge == SNAP_RIGHT);
124
125	// use higher snap threshold for column headers
126	threshold = llmin(threshold, 10);
127
128	LLRect snap_rect = getSnapRect();
129
130	S32 snap_delta = mColumn->mMaxContentWidth - snap_rect.getWidth();
131
132	// x coord growing means column growing, so same signs mean we're going in right direction
133	if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) 
134	{
135		new_edge_val = snap_rect.mRight + snap_delta;
136	}
137	else 
138	{
139		LLScrollListColumn* next_column = mColumn->mParentCtrl->getColumn(mColumn->mIndex + 1);
140		while (next_column)
141		{
142			if (next_column->mHeader)
143			{
144				snap_delta = (next_column->mHeader->getSnapRect().mRight - next_column->mMaxContentWidth) - snap_rect.mRight;
145				if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 ) 
146				{
147					new_edge_val = snap_rect.mRight + snap_delta;
148				}
149				break;
150			}
151			next_column = mColumn->mParentCtrl->getColumn(next_column->mIndex + 1);
152		}
153	}
154
155	return this;
156}
157
158void LLScrollColumnHeader::handleReshape(const LLRect& new_rect, bool by_user)
159{
160	S32 new_width = new_rect.getWidth();
161	S32 delta_width = new_width - (getRect().getWidth() /*+ mColumn->mParentCtrl->getColumnPadding()*/);
162
163	if (delta_width != 0)
164	{
165		S32 remaining_width = -delta_width;
166		S32 col;
167		for (col = mColumn->mIndex + 1; col < mColumn->mParentCtrl->getNumColumns(); col++)
168		{
169			LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
170			if (!columnp) continue;
171
172			if (columnp->mHeader && columnp->mHeader->canResize())
173			{
174				// how many pixels in width can this column afford to give up?
175				S32 resize_buffer_amt = llmax(0, columnp->getWidth() - MIN_COLUMN_WIDTH);
176				
177				// user shrinking column, need to add width to other columns
178				if (delta_width < 0)
179				{
180					if (columnp->getWidth() > 0)
181					{
182						// statically sized column, give all remaining width to this column
183						columnp->setWidth(columnp->getWidth() + remaining_width);
184						if (columnp->mRelWidth > 0.f)
185						{
186							columnp->mRelWidth = (F32)columnp->getWidth() / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
187						}
188						// all padding went to this widget, we're done
189						break;
190					}
191				}
192				else
193				{
194					// user growing column, need to take width from other columns
195					remaining_width += resize_buffer_amt;
196
197					if (columnp->getWidth() > 0)
198					{
199						columnp->setWidth(columnp->getWidth() - llmin(columnp->getWidth() - MIN_COLUMN_WIDTH, delta_width));
200						if (columnp->mRelWidth > 0.f)
201						{
202							columnp->mRelWidth = (F32)columnp->getWidth() / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
203						}
204					}
205
206					if (remaining_width >= 0)
207					{
208						// width sucked up from neighboring columns, done
209						break;
210					}
211				}
212			}
213		}
214
215		// clamp resize amount to maximum that can be absorbed by other columns
216		if (delta_width > 0)
217		{
218			delta_width += llmin(remaining_width, 0);
219		}
220
221		// propagate constrained delta_width to new width for this column
222		new_width = getRect().getWidth() + delta_width - mColumn->mParentCtrl->getColumnPadding();
223
224		// use requested width
225		mColumn->setWidth(new_width);
226
227		// update proportional spacing
228		if (mColumn->mRelWidth > 0.f)
229		{
230			mColumn->mRelWidth = (F32)new_width / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
231		}
232
233		// tell scroll list to layout columns again
234		// do immediate update to get proper feedback to resize handle
235		// which needs to know how far the resize actually went
236		mColumn->mParentCtrl->updateColumns();
237	}
238}
239
240void LLScrollColumnHeader::setHasResizableElement(BOOL resizable)
241{
242	if (mHasResizableElement != resizable)
243	{
244		mColumn->mParentCtrl->dirtyColumns();
245		mHasResizableElement = resizable;
246	}
247}
248
249void LLScrollColumnHeader::updateResizeBars()
250{
251	S32 num_resizable_columns = 0;
252	S32 col;
253	for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++)
254	{
255		LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
256		if (columnp->mHeader && columnp->mHeader->canResize())
257		{
258			num_resizable_columns++;
259		}
260	}
261
262	S32 num_resizers_enabled = 0;
263
264	// now enable/disable resize handles on resizable columns if we have at least two
265	for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++)
266	{
267		LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
268		if (!columnp->mHeader) continue;
269		BOOL enable = num_resizable_columns >= 2 && num_resizers_enabled < (num_resizable_columns - 1) && columnp->mHeader->canResize();
270		columnp->mHeader->enableResizeBar(enable);
271		if (enable)
272		{
273			num_resizers_enabled++;
274		}
275	}
276}
277
278void LLScrollColumnHeader::enableResizeBar(BOOL enable)
279{
280	mResizeBar->setEnabled(enable);
281}
282
283BOOL LLScrollColumnHeader::canResize()
284{
285	return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth);
286}
287
288void LLScrollListColumn::SortNames::declareValues()
289{
290	declare("ascending", LLScrollListColumn::ASCENDING);
291	declare("descending", LLScrollListColumn::DESCENDING);
292}
293
294//
295// LLScrollListColumn
296//
297//static 
298const LLScrollListColumn::Params& LLScrollListColumn::getDefaultParams()
299{
300	return LLUICtrlFactory::getDefaultParams<LLScrollListColumn>();
301}
302
303
304LLScrollListColumn::LLScrollListColumn(const Params& p, LLScrollListCtrl* parent)
305:	mWidth(0),
306	mIndex (-1),
307	mParentCtrl(parent),
308	mName(p.name),
309	mLabel(p.header.label),
310	mHeader(NULL),
311	mMaxContentWidth(0),
312	mDynamicWidth(p.width.dynamic_width),
313	mRelWidth(p.width.relative_width),
314	mFontAlignment(p.halign),
315	mSortingColumn(p.sort_column)
316{
317	if (p.sort_ascending.isProvided())
318	{
319		mSortDirection = p.sort_ascending() ? ASCENDING : DESCENDING;
320	}
321	else
322	{
323		mSortDirection = p.sort_direction;
324	}
325
326	setWidth(p.width.pixel_width);
327}
328
329void LLScrollListColumn::setWidth(S32 width) 
330{ 
331	if (!mDynamicWidth && mRelWidth <= 0.f) 
332	{
333		mParentCtrl->updateStaticColumnWidth(this, width);
334	}
335	mWidth = width;
336}