1//Column list view source file
2
3
4//******************************************************************************************************
5//**** PROJECT HEADER FILES
6//******************************************************************************************************
7#define ColumnListView_CPP
8#include "ColumnListView.h"
9#include "CLVColumnLabelView.h"
10#include "CLVColumn.h"
11#include "CLVListItem.h"
12
13#include <stdio.h>
14#include <interface/Rect.h>
15
16//******************************************************************************************************
17//**** BITMAPS
18//******************************************************************************************************
19uint8 CLVRightArrowData[132] =
20{
21	0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
22	0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
23	0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
24	0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
25	0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
26	0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF,
27	0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
28	0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
29	0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
30	0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
31	0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
32};
33uint8 CLVDownArrowData[132] =
34{
35	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
36	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
37	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
38	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
39	0xFF, 0x00, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF,
40	0xFF, 0xFF, 0x00, 0x12, 0x12, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF,
41	0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
42	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
43	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
44	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
45	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
46};
47
48
49//******************************************************************************************************
50//**** ColumnListView CLASS DEFINITION
51//******************************************************************************************************
52class CLVContainerView : public BScrollView
53{
54	public:
55		CLVContainerView(char* name, BView* target, uint32 resizingMode, uint32 flags, bool horizontal,
56			bool vertical, border_style border);
57		~CLVContainerView();
58		bool IsBeingDestroyed;
59};
60
61
62CLVContainerView::CLVContainerView(char* name, BView* target, uint32 resizingMode, uint32 flags,
63	bool horizontal, bool vertical, border_style border)
64	:
65	BScrollView(name, target, resizingMode, flags, horizontal, vertical, border)
66{
67	IsBeingDestroyed = false;
68};
69
70
71CLVContainerView::~CLVContainerView()
72{
73	IsBeingDestroyed = true;
74}
75
76
77ColumnListView::ColumnListView(BRect frame, BScrollView **containerView,
78	const char *name, uint32 resizingMode, uint32 flags, list_view_type type,
79	bool hierarchical, bool horizontal, bool vertical, border_style border,
80	const BFont *labelFont)
81	:
82	BListView(frame, name, type, B_FOLLOW_ALL_SIDES, flags | B_PULSE_NEEDED),
83	fHierarchical(hierarchical),
84	fColumnList(6),
85	fColumnDisplayList(6),
86	fDataWidth(0),
87	fDataHeight(0),
88	fPageWidth(0),
89	fPageHeight(0),
90	fSortKeyList(6),
91	fRightArrow(BRect(0, 0, 10, 10), B_RGBA32, CLVRightArrowData, false, false),
92	fDownArrow(BRect(0, 0, 10, 10), B_RGBA32, CLVDownArrowData, false, false),
93	fFullItemList(32),
94	_selectedColumn(-1),
95	_editMessage(NULL)
96{
97	//Create the column titles bar view
98	font_height fontAttributes;
99	labelFont->GetHeight(&fontAttributes);
100	float fLabelFontHeight = ceil(fontAttributes.ascent)
101		+ ceil(fontAttributes.descent);
102	float columnLabelViewBottom = frame.top + 1 + fLabelFontHeight + 3;
103	fColumnLabelView = new CLVColumnLabelView(BRect(frame.left, frame.top,
104		frame.right, columnLabelViewBottom), this, labelFont);
105
106	//Create the container view
107	CreateContainer(horizontal, vertical, border, resizingMode, flags);
108	*containerView = fScrollView;
109
110	//Complete the setup
111	UpdateColumnSizesDataRectSizeScrollBars();
112	fColumnLabelView->UpdateDragGroups();
113	fExpanderColumn = -1;
114	fCompare = NULL;
115}
116
117
118ColumnListView::~ColumnListView()
119{
120	//Delete all list columns
121	int32 ColumnCount = fColumnList.CountItems();
122	for(int32 Counter = ColumnCount-1; Counter >= 0; Counter--)
123	{
124		CLVColumn* Item = (CLVColumn*)fColumnList.RemoveItem(Counter);
125		if(Item)
126			delete Item;
127	}
128	//Remove and delete the container view if necessary
129	if(!fScrollView->IsBeingDestroyed)
130	{
131		fScrollView->RemoveChild(this);
132		delete fScrollView;
133	}
134
135	delete _editMessage;
136}
137
138
139void ColumnListView::CreateContainer(bool horizontal, bool vertical, border_style border,
140	uint32 ResizingMode, uint32 flags)
141{
142	BRect ViewFrame = Frame();
143	BRect LabelsFrame = fColumnLabelView->Frame();
144
145	fScrollView = new CLVContainerView(NULL,this,ResizingMode,flags,horizontal,vertical,border);
146	BRect NewFrame = Frame();
147	//Resize the main view to make room for the CLVColumnLabelView
148	ResizeTo(ViewFrame.right-ViewFrame.left,ViewFrame.bottom-LabelsFrame.bottom-1.0);
149	MoveTo(NewFrame.left,NewFrame.top+(LabelsFrame.bottom-LabelsFrame.top+1.0));
150	fColumnLabelView->MoveTo(NewFrame.left,NewFrame.top);
151
152	//Add the ColumnLabelView
153	fScrollView->AddChild(fColumnLabelView);
154
155	//Remove and re-add the BListView so that it will draw after the CLVColumnLabelView
156	fScrollView->RemoveChild(this);
157	fScrollView->AddChild(this);
158
159	fFillerView = NULL;
160}
161
162
163void ColumnListView::AddScrollViewCorner()
164{
165	BPoint FarCorner = fScrollView->Bounds().RightBottom();
166	fFillerView = new ScrollViewCorner(FarCorner.x-B_V_SCROLL_BAR_WIDTH,FarCorner.y-B_H_SCROLL_BAR_HEIGHT);
167	fScrollView->AddChild(fFillerView);
168}
169
170
171void ColumnListView::UpdateColumnSizesDataRectSizeScrollBars()
172{
173	//Figure out the width
174	float ColumnBegin;
175	float ColumnEnd = -1.0;
176	fDataWidth = 0.0;
177	bool NextPushedByExpander = false;
178	int32 NumberOfColumns = fColumnDisplayList.CountItems();
179	for(int32 Counter = 0; Counter < NumberOfColumns; Counter++)
180	{
181		CLVColumn* Column = (CLVColumn*)fColumnDisplayList.ItemAt(Counter);
182		if(NextPushedByExpander)
183			Column->fPushedByExpander = true;
184		else
185			Column->fPushedByExpander = false;
186		if(Column->IsShown())
187		{
188			float ColumnWidth = Column->Width();
189			ColumnBegin = ColumnEnd + 1.0;
190			ColumnEnd = ColumnBegin + ColumnWidth;
191			Column->fColumnBegin = ColumnBegin;
192			Column->fColumnEnd = ColumnEnd;
193			fDataWidth = Column->fColumnEnd;
194			if(NextPushedByExpander)
195				if(!(Column->fFlags & CLV_PUSH_PASS))
196					NextPushedByExpander = false;
197			if(Column->fFlags & CLV_EXPANDER)
198				//Set the next column to be pushed
199				NextPushedByExpander = true;
200		}
201	}
202
203	//Figure out the height
204	fDataHeight = 0.0;
205	int32 NumberOfItems = CountItems();
206	for(int32 Counter2 = 0; Counter2 < NumberOfItems; Counter2++)
207		fDataHeight += ItemAt(Counter2)->Height()+1.0;
208	if(NumberOfItems > 0)
209		fDataHeight -= 1.0;
210
211	//Update the scroll bars
212	UpdateScrollBars();
213}
214
215
216void ColumnListView::UpdateScrollBars()
217{
218	if(fScrollView)
219	{
220		//Figure out the bounds and scroll if necessary
221		BRect ViewBounds;
222		float DeltaX,DeltaY;
223		do
224		{
225			ViewBounds = Bounds();
226			//Figure out the width of the page rectangle
227			fPageWidth = fDataWidth;
228			fPageHeight = fDataHeight;
229			//If view runs past the end, make more visible at the beginning
230			DeltaX = 0.0;
231			if(ViewBounds.right > fDataWidth && ViewBounds.left > 0)
232			{
233				DeltaX = ViewBounds.right-fDataWidth;
234				if(DeltaX > ViewBounds.left)
235					DeltaX = ViewBounds.left;
236			}
237			DeltaY = 0.0;
238			if(ViewBounds.bottom > fDataHeight && ViewBounds.top > 0)
239			{
240				DeltaY = ViewBounds.bottom-fDataHeight;
241				if(DeltaY > ViewBounds.top)
242					DeltaY = ViewBounds.top;
243			}
244			if(DeltaX != 0.0 || DeltaY != 0.0)
245			{
246				ScrollTo(BPoint(ViewBounds.left-DeltaX,ViewBounds.top-DeltaY));
247				ViewBounds = Bounds();
248			}
249			if(ViewBounds.right-ViewBounds.left > fDataWidth)
250				fPageWidth = ViewBounds.right;
251			if(ViewBounds.bottom-ViewBounds.top > fDataHeight)
252				fPageHeight = ViewBounds.bottom;
253		}while(DeltaX != 0.0 || DeltaY != 0.0);
254
255		//Figure out the ratio of the bounds rectangle width or height to the page rectangle width or height
256		float WidthProp = (ViewBounds.right-ViewBounds.left)/fPageWidth;
257		float HeightProp = (ViewBounds.bottom-ViewBounds.top)/fPageHeight;
258
259		BScrollBar* HScrollBar = fScrollView->ScrollBar(B_HORIZONTAL);
260		BScrollBar* VScrollBar = fScrollView->ScrollBar(B_VERTICAL);
261		//Set the scroll bar ranges and proportions.  If the whole document is visible, inactivate the
262		//slider
263		if(HScrollBar)
264		{
265			if(WidthProp >= 1.0 && ViewBounds.left == 0.0)
266				HScrollBar->SetRange(0.0,0.0);
267			else
268				HScrollBar->SetRange(0.0,fPageWidth-(ViewBounds.right-ViewBounds.left));
269			HScrollBar->SetProportion(WidthProp);
270			//Set the step values
271			HScrollBar->SetSteps(20.0,ViewBounds.right-ViewBounds.left);
272		}
273		if(VScrollBar)
274		{
275			if(HeightProp >= 1.0 && ViewBounds.top == 0.0)
276			{
277				VScrollBar->SetRange(0.0,0.0);
278				if(fFillerView)
279					fFillerView->SetViewColor(BeInactiveControlGrey);
280			}
281			else
282			{
283				VScrollBar->SetRange(0.0,fPageHeight-(ViewBounds.bottom-ViewBounds.top));
284				if(fFillerView)
285					fFillerView->SetViewColor(BeBackgroundGrey);
286			}
287			VScrollBar->SetProportion(HeightProp);
288		}
289	}
290}
291
292
293void ColumnListView::ColumnsChanged()
294{
295	//Any previous column dragging/resizing will get corrupted, so deselect
296	if(fColumnLabelView->fColumnClicked)
297		fColumnLabelView->fColumnClicked = NULL;
298
299	//Update the internal sizes and grouping of the columns and sizes of drag groups
300	UpdateColumnSizesDataRectSizeScrollBars();
301	fColumnLabelView->UpdateDragGroups();
302	fColumnLabelView->Invalidate();
303	Invalidate();
304}
305
306
307bool ColumnListView::AddColumn(CLVColumn* Column)
308//Adds a column to the ColumnListView at the end of the list.  Returns true if successful.
309{
310	int32 NumberOfColumns = fColumnList.CountItems();
311	int32 DisplayIndex = NumberOfColumns;
312
313	//Make sure a second Expander is not being added
314	if(Column->fFlags & CLV_EXPANDER)
315	{
316		if(!fHierarchical)
317			return false;
318		for(int32 Counter = 0; Counter < NumberOfColumns; Counter++)
319			if(((CLVColumn*)fColumnList.ItemAt(Counter))->fFlags & CLV_EXPANDER)
320				return false;
321		if(Column->IsShown())
322			fExpanderColumn = NumberOfColumns;
323	}
324
325	//Make sure this column hasn't already been added to another ColumnListView
326	if(Column->fParent != NULL)
327		return false;
328
329	BWindow* ParentWindow = Window();
330	if(ParentWindow)
331		ParentWindow->Lock();
332	//Check if this should be locked at the beginning or end, and adjust its position if necessary
333	if(!Column->Flags() & CLV_LOCK_AT_END)
334	{
335		bool Repeat;
336		if(Column->Flags() & CLV_LOCK_AT_BEGINNING)
337		{
338			//Move it to the beginning, after the last CLV_LOCK_AT_BEGINNING item
339			DisplayIndex = 0;
340			Repeat = true;
341			while(Repeat && DisplayIndex < NumberOfColumns)
342			{
343				Repeat = false;
344				CLVColumn* LastColumn = (CLVColumn*)fColumnDisplayList.ItemAt(DisplayIndex);
345				if(LastColumn->Flags() & CLV_LOCK_AT_BEGINNING)
346				{
347					DisplayIndex++;
348					Repeat = true;
349				}
350			}
351		}
352		else
353		{
354			//Make sure it isn't after a CLV_LOCK_AT_END item
355			Repeat = true;
356			while(Repeat && DisplayIndex > 0)
357			{
358				Repeat = false;
359				CLVColumn* LastColumn = (CLVColumn*)fColumnDisplayList.ItemAt(DisplayIndex-1);
360				if(LastColumn->Flags() & CLV_LOCK_AT_END)
361				{
362					DisplayIndex--;
363					Repeat = true;
364				}
365			}
366		}
367	}
368
369	//Add the column to the display list in the appropriate position
370	fColumnDisplayList.AddItem(Column, DisplayIndex);
371
372	//Add the column to the end of the column list
373	fColumnList.AddItem(Column);
374
375	//Tell the column it belongs to me now
376	Column->fParent = this;
377
378	//Set the scroll bars and tell views to update
379	ColumnsChanged();
380	if(ParentWindow)
381		ParentWindow->Unlock();
382	return true;
383}
384
385
386bool ColumnListView::AddColumnList(BList* NewColumns)
387//Adds a BList of CLVColumn's to the ColumnListView at the position specified, or at the end of the list
388//if AtIndex == -1.  Returns true if successful.
389{
390	int32 NumberOfColumns = int32(fColumnList.CountItems());
391	int32 NumberOfColumnsToAdd = int32(NewColumns->CountItems());
392
393	//Make sure a second CLVExpander is not being added
394	int32 Counter;
395	int32 NumberOfExpanders = 0;
396	for(Counter = 0; Counter < NumberOfColumns; Counter++)
397		if(((CLVColumn*)fColumnList.ItemAt(Counter))->fFlags & CLV_EXPANDER)
398			NumberOfExpanders++;
399	int32 SetfExpanderColumnTo = -1;
400	for(Counter = 0; Counter < NumberOfColumnsToAdd; Counter++)
401	{
402		CLVColumn* ThisColumn = (CLVColumn*)NewColumns->ItemAt(Counter);
403		if(ThisColumn->fFlags & CLV_EXPANDER)
404		{
405			NumberOfExpanders++;
406			if(ThisColumn->IsShown())
407				SetfExpanderColumnTo = NumberOfColumns + Counter;
408		}
409	}
410	if(NumberOfExpanders != 0 && !fHierarchical)
411		return false;
412	if(NumberOfExpanders > 1)
413		return false;
414	if(SetfExpanderColumnTo != -1)
415		fExpanderColumn = SetfExpanderColumnTo;
416
417	//Make sure none of these columns have already been added to a ColumnListView
418	for(Counter = 0; Counter < NumberOfColumnsToAdd; Counter++)
419		if(((CLVColumn*)NewColumns->ItemAt(Counter))->fParent != NULL)
420			return false;
421	//Make sure none of these columns are being added twice
422	for(Counter = 0; Counter < NumberOfColumnsToAdd-1; Counter++)
423		for(int32 Counter2 = Counter+1; Counter2 < NumberOfColumnsToAdd; Counter2++)
424			if(NewColumns->ItemAt(Counter) == NewColumns->ItemAt(Counter2))
425				return false;
426
427	BWindow* ParentWindow = Window();
428	if(ParentWindow)
429		ParentWindow->Lock();
430	for(Counter = 0; Counter < NumberOfColumnsToAdd; Counter++)
431	{
432		CLVColumn* Column = (CLVColumn*)NewColumns->ItemAt(Counter);
433		//Check if this should be locked at the beginning or end, and adjust its position if necessary
434		int32 DisplayIndex = NumberOfColumns;
435		if(!Column->Flags() & CLV_LOCK_AT_END)
436		{
437			bool Repeat;
438			if(Column->Flags() & CLV_LOCK_AT_BEGINNING)
439			{
440				//Move it to the beginning, after the last CLV_LOCK_AT_BEGINNING item
441				DisplayIndex = 0;
442				Repeat = true;
443				while(Repeat && DisplayIndex < NumberOfColumns)
444				{
445					Repeat = false;
446					CLVColumn* LastColumn = (CLVColumn*)fColumnDisplayList.ItemAt(DisplayIndex);
447					if(LastColumn->Flags() & CLV_LOCK_AT_BEGINNING)
448					{
449						DisplayIndex++;
450						Repeat = true;
451					}
452				}
453			}
454			else
455			{
456				//Make sure it isn't after a CLV_LOCK_AT_END item
457				Repeat = true;
458				while(Repeat && DisplayIndex > 0)
459				{
460					Repeat = false;
461					CLVColumn* LastColumn = (CLVColumn*)fColumnDisplayList.ItemAt(DisplayIndex-1);
462					if(LastColumn->Flags() & CLV_LOCK_AT_END)
463					{
464						DisplayIndex--;
465						Repeat = true;
466					}
467				}
468			}
469		}
470
471		//Add the column to the display list in the appropriate position
472		fColumnDisplayList.AddItem(Column, DisplayIndex);
473
474		//Tell the column it belongs to me now
475		Column->fParent = this;
476
477		NumberOfColumns++;
478	}
479
480	//Add the columns to the end of the column list
481	fColumnList.AddList(NewColumns);
482
483	//Set the scroll bars and tell views to update
484	ColumnsChanged();
485	if(ParentWindow)
486		ParentWindow->Unlock();
487	return true;
488}
489
490
491bool ColumnListView::RemoveColumn(CLVColumn* Column)
492//Removes a CLVColumn from the ColumnListView.  Returns true if successful.
493{
494	if(!fColumnList.HasItem(Column))
495		return false;
496	int32 ColumnIndex = fSortKeyList.IndexOf(Column);
497	if(ColumnIndex >= 0)
498		fSortKeyList.RemoveItem(ColumnIndex);
499
500	if(Column->fFlags & CLV_EXPANDER)
501		fExpanderColumn = -1;
502
503	BWindow* ParentWindow = Window();
504	if(ParentWindow)
505		ParentWindow->Lock();
506	//Remove Column from the column and display lists
507	fColumnDisplayList.RemoveItem(Column);
508	fColumnList.RemoveItem(Column);
509
510	//Tell the column it has been removed
511	Column->fParent = NULL;
512
513	//Set the scroll bars and tell views to update
514	ColumnsChanged();
515	if(ParentWindow)
516		ParentWindow->Unlock();
517	return true;
518}
519
520
521bool ColumnListView::RemoveColumns(CLVColumn* Column, int32 Count)
522//Finds Column in ColumnList and removes Count columns and their data from the view and its items
523{
524	BWindow* ParentWindow = Window();
525	if(ParentWindow)
526		ParentWindow->Lock();
527	int32 ColumnIndex = fColumnList.IndexOf(Column);
528	if(ColumnIndex < 0)
529	{
530		if(ParentWindow)
531			ParentWindow->Unlock();
532		return false;
533	}
534	if(ColumnIndex + Count >= fColumnList.CountItems())
535	{
536		if(ParentWindow)
537			ParentWindow->Unlock();
538		return false;
539	}
540
541	//Remove columns from the column and display lists
542	for(int32 Counter = ColumnIndex; Counter < ColumnIndex+Count; Counter++)
543	{
544		CLVColumn* ThisColumn = (CLVColumn*)fColumnList.ItemAt(Counter);
545		fColumnDisplayList.RemoveItem(ThisColumn);
546
547		int32 SortIndex = fSortKeyList.IndexOf(Column);
548		if(SortIndex >= 0)
549			fSortKeyList.RemoveItem(SortIndex);
550
551		if(ThisColumn->fFlags & CLV_EXPANDER)
552			fExpanderColumn = -1;
553
554		//Tell the column it has been removed
555		ThisColumn->fParent = NULL;
556	}
557	fColumnList.RemoveItems(ColumnIndex,Count);
558
559	//Set the scroll bars and tell views to update
560	ColumnsChanged();
561	if(ParentWindow)
562		ParentWindow->Unlock();
563	return true;
564}
565
566void ColumnListView :: SetEditMessage(BMessage * newMsg, BMessenger target)
567{
568   delete _editMessage;
569   _editMessage = newMsg;
570   _editTarget = target;
571}
572
573void ColumnListView :: KeyDown(const char * bytes, int32 numBytes)
574{
575   int colDiff = 0;
576   bool metaKeysPressed = false;
577
578   // Find out if any meta-keys are pressed
579   int32 q;
580   if (Window()->CurrentMessage()->FindInt32("modifiers", &q) == B_NO_ERROR)
581   {
582      metaKeysPressed = ((q & (B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY)) != 0);
583   }
584
585   if (numBytes > 0)
586   {
587      switch (*bytes)
588      {
589         case B_LEFT_ARROW:
590            if (metaKeysPressed == false)
591            {
592               colDiff = -1;
593               break;
594            }
595
596         case B_RIGHT_ARROW:
597            if (metaKeysPressed == false)
598            {
599               colDiff = 1;
600               break;
601            }
602
603         case B_UP_ARROW:
604         case B_DOWN_ARROW:
605            if (metaKeysPressed == false)
606            {
607               BListView::KeyDown(bytes, numBytes);
608               break;
609            }
610
611         default:
612            if (_editMessage != NULL)
613            {
614               BMessage temp(*_editMessage);
615               temp.AddInt32("column", _selectedColumn);
616               temp.AddInt32("row", CurrentSelection());
617               temp.AddString("bytes", bytes);
618
619               int32 key;
620               if (Window()->CurrentMessage()->FindInt32("key", &key) == B_NO_ERROR) temp.AddInt32("key", key);
621
622               _editTarget.SendMessage(&temp);
623            }
624         break;
625      }
626   }
627
628   if (colDiff != 0)
629   {
630      // We need to move the highlighted column by (colDiff) columns, if possible.
631      int numDisplayColumns = fColumnDisplayList.CountItems();
632
633      int curColumn = _selectedColumn;  // curColumn is an ACTUAL index.
634      if (curColumn == -1)  // no current column selected?
635      {
636         curColumn = (colDiff > 0) ? GetActualIndexOf(0) : GetActualIndexOf(numDisplayColumns-1);   // go to an edge
637      }
638      else
639      {
640         // Go to the display column adjacent to the current column's display column.
641         int32 currentDisplayIndex = GetDisplayIndexOf(curColumn);
642         if (currentDisplayIndex < 0) currentDisplayIndex = 0;
643         currentDisplayIndex += colDiff;
644
645         if (currentDisplayIndex < 0) currentDisplayIndex = numDisplayColumns - 1;
646         if (currentDisplayIndex >= numDisplayColumns) currentDisplayIndex = 0;
647         curColumn = GetActualIndexOf(currentDisplayIndex);
648      }
649      SetSelectedColumnIndex(curColumn);
650   }
651}
652
653int32 ColumnListView::CountColumns() const
654{
655	return fColumnList.CountItems();
656}
657
658
659int32 ColumnListView::IndexOfColumn(CLVColumn* column) const
660{
661	return fColumnList.IndexOf(column);
662}
663
664
665CLVColumn* ColumnListView::ColumnAt(int32 column_index) const
666{
667    return (CLVColumn*)fColumnList.ItemAt(column_index);
668}
669
670CLVColumn* ColumnListView::ColumnAt(BPoint point) const
671{
672    for (int i=0; i<fColumnList.CountItems(); i++)
673    {
674       CLVColumn * col = (CLVColumn *) fColumnList.ItemAt(i);
675       if ((point.x >= col->fColumnBegin)&&(point.x <= col->fColumnEnd)) return col;
676    }
677    return NULL;
678}
679
680bool ColumnListView::SetDisplayOrder(const int32* ColumnOrder)
681//Sets the display order using a BList of CLVColumn's
682{
683	BWindow* ParentWindow = Window();
684	if(ParentWindow)
685		ParentWindow->Lock();
686	//Add the items to the display list in order
687	fColumnDisplayList.MakeEmpty();
688	int32 ColumnsToSet = fColumnList.CountItems();
689	for(int32 Counter = 0; Counter < ColumnsToSet; Counter++)
690	{
691		if(ColumnOrder[Counter] >= ColumnsToSet)
692		{
693			if(ParentWindow)
694				ParentWindow->Unlock();
695			return false;
696		}
697		for(int32 Counter2 = 0; Counter2 < Counter; Counter2++)
698			if(ColumnOrder[Counter] == ColumnOrder[Counter2])
699			{
700				if(ParentWindow)
701					ParentWindow->Unlock();
702				return false;
703			}
704		fColumnDisplayList.AddItem(fColumnList.ItemAt(ColumnOrder[Counter]));
705	}
706
707	//Update everything about the columns
708	ColumnsChanged();
709
710	//Let the program know that the display order changed.
711	if(ParentWindow)
712		ParentWindow->Unlock();
713	DisplayOrderChanged(ColumnOrder);
714	return true;
715}
716
717
718void ColumnListView::ColumnWidthChanged(int32 ColumnIndex, float NewWidth)
719{
720    Invalidate();
721}
722
723
724void ColumnListView::DisplayOrderChanged(const int32* order)
725{ }
726
727
728int32* ColumnListView::DisplayOrder() const
729{
730	int32 ColumnsInList = fColumnList.CountItems();
731	int32* ReturnList = new int32[ColumnsInList];
732	BWindow* ParentWindow = Window();
733	if(ParentWindow)
734		ParentWindow->Lock();
735	for(int32 Counter = 0; Counter < ColumnsInList; Counter++)
736		ReturnList[Counter] = int32(fColumnList.IndexOf(fColumnDisplayList.ItemAt(Counter)));
737	if(ParentWindow)
738		ParentWindow->Unlock();
739	return ReturnList;
740}
741
742
743void ColumnListView::SetSortKey(int32 ColumnIndex)
744{
745	CLVColumn* Column;
746	if(ColumnIndex >= 0)
747	{
748		Column = (CLVColumn*)fColumnList.ItemAt(ColumnIndex);
749		if(!(Column->Flags()&CLV_SORT_KEYABLE))
750			return;
751	}
752	else
753		Column = NULL;
754	if(fSortKeyList.ItemAt(0) != Column || Column == NULL)
755	{
756		BWindow* ParentWindow = Window();
757		if(ParentWindow)
758			ParentWindow->Lock();
759		BRect LabelBounds = fColumnLabelView->Bounds();
760		//Need to remove old sort keys and erase all the old underlines
761		int32 SortKeyCount = fSortKeyList.CountItems();
762		for(int32 Counter = 0; Counter < SortKeyCount; Counter++)
763		{
764			CLVColumn* UnderlineColumn = (CLVColumn*)fSortKeyList.ItemAt(Counter);
765			if(UnderlineColumn->fSortMode != NoSort)
766				fColumnLabelView->Invalidate(BRect(UnderlineColumn->fColumnBegin,LabelBounds.top,
767					UnderlineColumn->fColumnEnd,LabelBounds.bottom));
768		}
769		fSortKeyList.MakeEmpty();
770
771		if(Column)
772		{
773			fSortKeyList.AddItem(Column);
774			if(Column->fSortMode == NoSort)
775				SetSortMode(ColumnIndex,Ascending);
776			SortItems();
777			//Need to draw new underline
778			fColumnLabelView->Invalidate(BRect(Column->fColumnBegin,LabelBounds.top,Column->fColumnEnd,
779				LabelBounds.bottom));
780		}
781		if(ParentWindow)
782			ParentWindow->Unlock();
783	}
784}
785
786
787void ColumnListView::AddSortKey(int32 ColumnIndex)
788{
789	CLVColumn* Column;
790	if(ColumnIndex >= 0)
791	{
792		Column = (CLVColumn*)fColumnList.ItemAt(ColumnIndex);
793		if(!(Column->Flags()&CLV_SORT_KEYABLE))
794			return;
795	}
796	else
797		Column = NULL;
798	if(Column && !fSortKeyList.HasItem(Column))
799	{
800		BWindow* ParentWindow = Window();
801		if(ParentWindow)
802			ParentWindow->Lock();
803		BRect LabelBounds = fColumnLabelView->Bounds();
804		fSortKeyList.AddItem(Column);
805		if(Column->fSortMode == NoSort)
806			SetSortMode(ColumnIndex,Ascending);
807		SortItems();
808		//Need to draw new underline
809		fColumnLabelView->Invalidate(BRect(Column->fColumnBegin,LabelBounds.top,Column->fColumnEnd,
810			LabelBounds.bottom));
811		if(ParentWindow)
812			ParentWindow->Unlock();
813	}
814}
815
816
817void ColumnListView::SetSortMode(int32 ColumnIndex,CLVSortMode Mode)
818{
819	CLVColumn* Column;
820	if(ColumnIndex >= 0)
821	{
822		Column = (CLVColumn*)fColumnList.ItemAt(ColumnIndex);
823		if(!(Column->Flags()&CLV_SORT_KEYABLE))
824			return;
825	}
826	else
827		return;
828	if(Column->fSortMode != Mode)
829	{
830		BWindow* ParentWindow = Window();
831		if(ParentWindow)
832			ParentWindow->Lock();
833		BRect LabelBounds = fColumnLabelView->Bounds();
834		Column->fSortMode = Mode;
835		if(Mode == NoSort && fSortKeyList.HasItem(Column))
836			fSortKeyList.RemoveItem(Column);
837		SortItems();
838		//Need to draw or erase underline
839		fColumnLabelView->Invalidate(BRect(Column->fColumnBegin,LabelBounds.top,Column->fColumnEnd,
840			LabelBounds.bottom));
841		if(ParentWindow)
842			ParentWindow->Unlock();
843	}
844}
845
846
847void ColumnListView::ReverseSortMode(int32 ColumnIndex)
848{
849	CLVColumn* Column;
850	if(ColumnIndex >= 0)
851	{
852		Column = (CLVColumn*)fColumnList.ItemAt(ColumnIndex);
853		if(!(Column->Flags()&CLV_SORT_KEYABLE))
854			return;
855	}
856	else
857		return;
858	if(Column->fSortMode == Ascending)
859		SetSortMode(ColumnIndex,Descending);
860	else if(Column->fSortMode == Descending)
861		SetSortMode(ColumnIndex,NoSort);
862	else if(Column->fSortMode == NoSort)
863		SetSortMode(ColumnIndex,Ascending);
864}
865
866
867int32 ColumnListView::Sorting(int32* SortKeys, CLVSortMode* SortModes) const
868{
869	BWindow* ParentWindow = Window();
870	if(ParentWindow)
871		ParentWindow->Lock();
872	int32 NumberOfKeys = fSortKeyList.CountItems();
873	for(int32 Counter = 0; Counter < NumberOfKeys; Counter++)
874	{
875		CLVColumn* Column = (CLVColumn*)fSortKeyList.ItemAt(Counter);
876		SortKeys[Counter] = IndexOfColumn(Column);
877		SortModes[Counter] = Column->SortMode();
878	}
879	if(ParentWindow)
880		ParentWindow->Unlock();
881	return NumberOfKeys;
882}
883
884void ColumnListView :: Pulse()
885{
886   int32 curSel = CurrentSelection();
887   if (curSel >= 0)
888   {
889      CLVListItem * item = (CLVListItem *) ItemAt(curSel);
890      item->Pulse(this);
891   }
892}
893
894void ColumnListView::SetSorting(int32 NumberOfKeys, int32* SortKeys, CLVSortMode* SortModes)
895{
896	BWindow* ParentWindow = Window();
897	if(ParentWindow)
898		ParentWindow->Lock();
899
900	//Need to remove old sort keys and erase all the old underlines
901	BRect LabelBounds = fColumnLabelView->Bounds();
902	int32 SortKeyCount = fSortKeyList.CountItems();
903	for(int32 Counter = 0; Counter < SortKeyCount; Counter++)
904	{
905		CLVColumn* UnderlineColumn = (CLVColumn*)fSortKeyList.ItemAt(Counter);
906		if(UnderlineColumn->fSortMode != NoSort)
907			fColumnLabelView->Invalidate(BRect(UnderlineColumn->fColumnBegin,LabelBounds.top,
908				UnderlineColumn->fColumnEnd,LabelBounds.bottom));
909	}
910	fSortKeyList.MakeEmpty();
911
912	for(int32 Counter = 0; Counter < NumberOfKeys; Counter++)
913	{
914		if(Counter == 0)
915			SetSortKey(SortKeys[0]);
916		else
917			AddSortKey(SortKeys[Counter]);
918		SetSortMode(SortKeys[Counter],SortModes[Counter]);
919	}
920
921	if(ParentWindow)
922		ParentWindow->Unlock();
923}
924
925void ColumnListView::FrameResized(float width, float height)
926{
927	UpdateColumnSizesDataRectSizeScrollBars();
928	uint32 NumberOfItems = CountItems();
929	BFont Font;
930	GetFont(&Font);
931	for(uint32 Counter = 0; Counter < NumberOfItems; Counter++)
932		ItemAt(Counter)->Update(this,&Font);
933	BListView::FrameResized(width,height);
934}
935
936
937void ColumnListView::AttachedToWindow()
938//Hack to work around app_server bug
939{
940	BListView::AttachedToWindow();
941	UpdateColumnSizesDataRectSizeScrollBars();
942}
943
944
945void ColumnListView::ScrollTo(BPoint point)
946{
947	BListView::ScrollTo(point);
948	fColumnLabelView->ScrollTo(BPoint(point.x,0.0));
949}
950
951int32 ColumnListView::GetActualIndexOf(int32 displayIndex) const
952{
953   if ((displayIndex < 0)||(displayIndex >= fColumnDisplayList.CountItems())) return -1;
954   return (int32) fColumnList.IndexOf(fColumnDisplayList.ItemAt(displayIndex));
955}
956
957int32 ColumnListView::GetDisplayIndexOf(int32 realIndex) const
958{
959   if ((realIndex < 0)||(realIndex >= fColumnList.CountItems())) return -1;
960   return (int32) fColumnDisplayList.IndexOf(fColumnList.ItemAt(realIndex));
961}
962
963// Set a new (actual) column index as the selected index.  Call with arg -1 to unselect all.
964// Gotta change the _selectedColumn on all entries.  There is
965// undoubtedly a more efficient way to do this!  --jaf
966void ColumnListView :: SetSelectedColumnIndex(int32 col)
967{
968   if (_selectedColumn != col)
969   {
970      _selectedColumn = col;
971
972      int numRows = fFullItemList.CountItems();
973      for (int j=0; j<numRows; j++) ((CLVListItem *)fFullItemList.ItemAt(j))->_selectedColumn = _selectedColumn;
974
975      // Update current row if necessary.
976      int32 selectedIndex = CurrentSelection();
977      if (selectedIndex != -1) InvalidateItem(selectedIndex);
978   }
979}
980
981
982void ColumnListView::MouseDown(BPoint point)
983{
984    int prevColumn = _selectedColumn;
985	int32 numberOfColumns = fColumnDisplayList.CountItems();
986	float xleft = point.x;
987	for(int32 Counter = 0; Counter < numberOfColumns; Counter++)
988	{
989		CLVColumn* Column = (CLVColumn*)fColumnDisplayList.ItemAt(Counter);
990		if(Column->IsShown())
991		{
992			if (xleft > 0)
993			{
994			   xleft -= Column->Width();
995			   if (xleft <= 0)
996			   {
997			      SetSelectedColumnIndex(GetActualIndexOf(Counter));
998			      break;
999			   }
1000			}
1001		}
1002	}
1003	int32 ItemIndex = IndexOf(point);
1004	if(ItemIndex >= 0)
1005	{
1006		CLVListItem* ClickedItem = (CLVListItem*)BListView::ItemAt(ItemIndex);
1007		if(ClickedItem->fSuperItem)
1008			if(ClickedItem->fExpanderButtonRect.Contains(point))
1009			{
1010				if(ClickedItem->IsExpanded())
1011					Collapse(ClickedItem);
1012				else
1013					Expand(ClickedItem);
1014				return;
1015			}
1016	}
1017
1018
1019	// If it's a right-click, hoist up the popup-menu
1020	const char * selectedText = NULL;
1021	CLVColumn * col = ColumnAt(_selectedColumn);
1022	if (col)
1023	{
1024	   BPopUpMenu * popup = col->GetPopup();
1025	   if (popup)
1026	   {
1027	      BMessage * msg = Window()->CurrentMessage();
1028	      int32 buttons;
1029	      if ((msg->FindInt32("buttons", &buttons) == B_NO_ERROR)&&(buttons == B_SECONDARY_MOUSE_BUTTON))
1030	      {
1031	         BPoint where(point);
1032	         Select(IndexOf(where));
1033	         ConvertToScreen(&where);
1034	         BMenuItem * result = popup->Go(where, false);
1035	         if (result) selectedText = result->Label();
1036	      }
1037	   }
1038	}
1039
1040	int prevRow = CurrentSelection();
1041	BListView::MouseDown(point);
1042
1043	int curRow = CurrentSelection();
1044	if ((_editMessage != NULL)&&((selectedText)||((_selectedColumn == prevColumn)&&(curRow == prevRow))))
1045	{
1046	   // Send mouse message...
1047	   BMessage temp(*_editMessage);
1048	   temp.AddInt32("column", _selectedColumn);
1049	   temp.AddInt32("row", CurrentSelection());
1050	   if (selectedText) temp.AddString("text", selectedText);
1051	                else temp.AddInt32("mouseClick", 0);
1052	   _editTarget.SendMessage(&temp);
1053	}
1054}
1055
1056bool ColumnListView::AddUnder(CLVListItem* item, CLVListItem* superitem)
1057{
1058	if(!fHierarchical)
1059		return false;
1060
1061	//Find the superitem in the full list and display list (if shown)
1062	int32 SuperItemPos = fFullItemList.IndexOf(superitem);
1063	if(SuperItemPos < 0)
1064		return false;
1065	uint32 SuperItemLevel = superitem->fOutlineLevel;
1066
1067	//Add the item under the superitem in the full list
1068	int32 ItemPos = SuperItemPos + 1;
1069	item->fOutlineLevel = SuperItemLevel + 1;
1070	while(true)
1071	{
1072		CLVListItem* Temp = (CLVListItem*)fFullItemList.ItemAt(ItemPos);
1073		if(Temp)
1074		{
1075			if(Temp->fOutlineLevel > SuperItemLevel)
1076				ItemPos++;
1077			else
1078				break;
1079		}
1080		else
1081			break;
1082	}
1083	return AddItemPrivate(item,ItemPos);
1084}
1085
1086
1087bool ColumnListView::AddItem(CLVListItem* item, int32 fullListIndex)
1088{
1089	return AddItemPrivate(item,fullListIndex);
1090}
1091
1092
1093bool ColumnListView::AddItem(CLVListItem* item)
1094{
1095	if(fHierarchical)
1096		return AddItemPrivate(item,fFullItemList.CountItems());
1097	else
1098		return AddItemPrivate(item,CountItems());
1099}
1100
1101
1102bool ColumnListView::AddItem(BListItem* item, int32 fullListIndex)
1103{
1104	return BListView::AddItem(item, fullListIndex);
1105}
1106
1107
1108bool ColumnListView::AddItem(BListItem* item)
1109{
1110	return BListView::AddItem(item);
1111}
1112
1113
1114bool ColumnListView::AddItemPrivate(CLVListItem* item, int32 fullListIndex)
1115{
1116    item->_selectedColumn = _selectedColumn;
1117
1118	if(fHierarchical)
1119	{
1120		uint32 ItemLevel = item->OutlineLevel();
1121
1122		//Figure out whether it is visible (should it be added to visible list)
1123		bool Visible = true;
1124
1125		//Find the item that contains it in the full list
1126		int32 SuperItemPos;
1127		if(ItemLevel == 0)
1128			SuperItemPos = -1;
1129		else
1130			SuperItemPos = fullListIndex - 1;
1131		CLVListItem* SuperItem;
1132		while(SuperItemPos >= 0)
1133		{
1134			SuperItem = (CLVListItem*)fFullItemList.ItemAt(SuperItemPos);
1135			if(SuperItem)
1136			{
1137				if(SuperItem->fOutlineLevel >= ItemLevel)
1138					SuperItemPos--;
1139				else
1140					break;
1141			}
1142			else
1143				return false;
1144		}
1145		if(SuperItemPos >= 0 && SuperItem)
1146		{
1147			if(!SuperItem->IsExpanded())
1148				//SuperItem's contents aren't visible
1149				Visible = false;
1150			if(!HasItem(SuperItem))
1151				//SuperItem itself isn't showing
1152				Visible = false;
1153		}
1154
1155		//Add the item to the full list
1156		if(!fFullItemList.AddItem(item,fullListIndex))
1157			return false;
1158		else
1159		{
1160			//Add the item to the display list
1161			if(Visible)
1162			{
1163				//Find the previous item, or -1 if the item I'm adding will be the first one
1164				int32 PreviousItemPos = fullListIndex - 1;
1165				CLVListItem* PreviousItem;
1166				while(PreviousItemPos >= 0)
1167				{
1168					PreviousItem = (CLVListItem*)fFullItemList.ItemAt(PreviousItemPos);
1169					if(PreviousItem && HasItem(PreviousItem))
1170						break;
1171					else
1172						PreviousItemPos--;
1173				}
1174
1175				//Add the item after the previous item, or first on the list
1176				bool Result;
1177				if(PreviousItemPos >= 0)
1178					Result = BListView::AddItem((BListItem*)item,IndexOf(PreviousItem)+1);
1179				else
1180					Result = BListView::AddItem((BListItem*)item,0);
1181				if(Result == false)
1182					fFullItemList.RemoveItem(item);
1183				return Result;
1184			}
1185			return true;
1186		}
1187	}
1188	else
1189		return BListView::AddItem(item,fullListIndex);
1190}
1191
1192
1193bool ColumnListView::AddList(BList* newItems)
1194{
1195	if(fHierarchical)
1196		return AddListPrivate(newItems,fFullItemList.CountItems());
1197	else
1198		return AddListPrivate(newItems,CountItems());
1199}
1200
1201
1202bool ColumnListView::AddList(BList* newItems, int32 fullListIndex)
1203{
1204	return AddListPrivate(newItems,fullListIndex);
1205}
1206
1207
1208bool ColumnListView::AddListPrivate(BList* newItems, int32 fullListIndex)
1209{
1210	int32 NumberOfItems = newItems->CountItems();
1211	for(int32 count = 0; count < NumberOfItems; count++)
1212		if(!AddItemPrivate((CLVListItem*)newItems->ItemAt(count),fullListIndex+count))
1213			return false;
1214	return true;
1215}
1216
1217
1218bool ColumnListView::RemoveItem(CLVListItem* item)
1219{
1220	if(item == NULL || !fFullItemList.HasItem(item))
1221		return false;
1222	if(fHierarchical)
1223	{
1224		int32 ItemsToRemove = 1 + FullListNumberOfSubitems(item);
1225		return RemoveItems(fFullItemList.IndexOf(item),ItemsToRemove);
1226	}
1227	else
1228		return BListView::RemoveItem((BListItem*)item);
1229}
1230
1231
1232BListItem* ColumnListView::RemoveItem(int32 fullListIndex)
1233{
1234	if(fHierarchical)
1235	{
1236		CLVListItem* TheItem = (CLVListItem*)fFullItemList.ItemAt(fullListIndex);
1237		if(TheItem)
1238		{
1239			int32 ItemsToRemove = 1 + FullListNumberOfSubitems(TheItem);
1240			if(RemoveItems(fullListIndex,ItemsToRemove))
1241				return TheItem;
1242			else
1243				return NULL;
1244		}
1245		else
1246			return NULL;
1247	}
1248	else
1249		return BListView::RemoveItem(fullListIndex);
1250}
1251
1252
1253bool ColumnListView::RemoveItems(int32 fullListIndex, int32 count)
1254{
1255	CLVListItem* TheItem;
1256	if(fHierarchical)
1257	{
1258		uint32 LastSuperItemLevel = ULONG_MAX;
1259		int32 Counter;
1260		int32 DisplayItemsToRemove = 0;
1261		int32 FirstDisplayItemToRemove = -1;
1262		for(Counter = fullListIndex; Counter < fullListIndex+count; Counter++)
1263		{
1264			TheItem = FullListItemAt(Counter);
1265			if(TheItem->fOutlineLevel < LastSuperItemLevel)
1266				LastSuperItemLevel = TheItem->fOutlineLevel;
1267			if(BListView::HasItem((BListItem*)TheItem))
1268			{
1269				DisplayItemsToRemove++;
1270				if(FirstDisplayItemToRemove == -1)
1271					FirstDisplayItemToRemove = BListView::IndexOf(TheItem);
1272			}
1273		}
1274		while(true)
1275		{
1276			TheItem = FullListItemAt(Counter);
1277			if(TheItem && TheItem->fOutlineLevel > LastSuperItemLevel)
1278			{
1279				count++;
1280				Counter++;
1281				if(BListView::HasItem((BListItem*)TheItem))
1282				{
1283					DisplayItemsToRemove++;
1284					if(FirstDisplayItemToRemove == -1)
1285						FirstDisplayItemToRemove = BListView::IndexOf((BListItem*)TheItem);
1286				}
1287			}
1288			else
1289				break;
1290		}
1291		while(DisplayItemsToRemove > 0)
1292		{
1293			if(BListView::RemoveItem(FirstDisplayItemToRemove) == NULL)
1294				return false;
1295			DisplayItemsToRemove--;
1296		}
1297		return fFullItemList.RemoveItems(fullListIndex,count);
1298	}
1299	else
1300		return BListView::RemoveItems(fullListIndex,count);
1301}
1302
1303
1304bool ColumnListView::RemoveItem(BListItem* item)
1305{
1306	return BListView::RemoveItem(item);
1307}
1308
1309
1310CLVListItem* ColumnListView::FullListItemAt(int32 fullListIndex) const
1311{
1312	return (CLVListItem*)fFullItemList.ItemAt(fullListIndex);
1313}
1314
1315
1316int32 ColumnListView::FullListIndexOf(const CLVListItem* item) const
1317{
1318	return fFullItemList.IndexOf((CLVListItem*)item);
1319}
1320
1321
1322int32 ColumnListView::FullListIndexOf(BPoint point) const
1323{
1324	int32 DisplayListIndex = IndexOf(point);
1325	CLVListItem* TheItem = (CLVListItem*)ItemAt(DisplayListIndex);
1326	if(TheItem)
1327		return FullListIndexOf(TheItem);
1328	else
1329		return -1;
1330}
1331
1332
1333CLVListItem* ColumnListView::FullListFirstItem() const
1334{
1335	return (CLVListItem*)fFullItemList.FirstItem();
1336}
1337
1338
1339CLVListItem* ColumnListView::FullListLastItem() const
1340{
1341	return (CLVListItem*)fFullItemList.LastItem();
1342}
1343
1344
1345bool ColumnListView::FullListHasItem(const CLVListItem* item) const
1346{
1347	return fFullItemList.HasItem((CLVListItem*)item);
1348}
1349
1350
1351int32 ColumnListView::FullListCountItems() const
1352{
1353	return fFullItemList.CountItems();
1354}
1355
1356
1357void ColumnListView::MakeEmpty()
1358{
1359	fFullItemList.MakeEmpty();
1360	BListView::MakeEmpty();
1361}
1362
1363
1364void ColumnListView::MakeEmptyPrivate()
1365{
1366	fFullItemList.MakeEmpty();
1367	BListView::MakeEmpty();
1368}
1369
1370
1371bool ColumnListView::FullListIsEmpty() const
1372{
1373	return fFullItemList.IsEmpty();
1374}
1375
1376
1377int32 ColumnListView::FullListCurrentSelection(int32 index) const
1378{
1379	int32 Selection = CurrentSelection(index);
1380	CLVListItem* SelectedItem = (CLVListItem*)ItemAt(Selection);
1381	return FullListIndexOf(SelectedItem);
1382}
1383
1384
1385void ColumnListView::FullListDoForEach(bool (*func)(CLVListItem*))
1386{
1387	int32 NumberOfItems = fFullItemList.CountItems();
1388	for(int32 Counter = 0; Counter < NumberOfItems; Counter++)
1389		if(func((CLVListItem*)fFullItemList.ItemAt(Counter)) == true)
1390			return;
1391}
1392
1393
1394void ColumnListView::FullListDoForEach(bool (*func)(CLVListItem*, void*), void* arg2)
1395{
1396	int32 NumberOfItems = fFullItemList.CountItems();
1397	for(int32 Counter = 0; Counter < NumberOfItems; Counter++)
1398		if(func((CLVListItem*)fFullItemList.ItemAt(Counter),arg2) == true)
1399			return;
1400}
1401
1402
1403CLVListItem* ColumnListView::Superitem(const CLVListItem* item) const
1404{
1405	int32 SuperItemPos;
1406	uint32 ItemLevel = item->fOutlineLevel;
1407	if(ItemLevel == 0)
1408		SuperItemPos = -1;
1409	else
1410		SuperItemPos = fFullItemList.IndexOf((CLVListItem*)item) - 1;
1411	CLVListItem* SuperItem;
1412	while(SuperItemPos >= 0)
1413	{
1414		SuperItem = (CLVListItem*)fFullItemList.ItemAt(SuperItemPos);
1415		if(SuperItem)
1416		{
1417			if(SuperItem->fOutlineLevel >= ItemLevel)
1418				SuperItemPos--;
1419			else
1420				break;
1421		}
1422		else
1423			return NULL;
1424	}
1425	if(SuperItemPos >= 0)
1426		return SuperItem;
1427	else
1428		return NULL;
1429}
1430
1431
1432int32 ColumnListView::FullListNumberOfSubitems(const CLVListItem* item) const
1433{
1434	if(!fHierarchical)
1435		return 0;
1436	int32 ItemPos = FullListIndexOf(item);
1437	int32 SubItemPos;
1438	uint32 SuperItemLevel = item->fOutlineLevel;
1439	if(ItemPos >= 0)
1440	{
1441		for(SubItemPos = ItemPos + 1; SubItemPos >= 1; SubItemPos++)
1442		{
1443			CLVListItem* TheItem = FullListItemAt(SubItemPos);
1444			if(TheItem == NULL || TheItem->fOutlineLevel <= SuperItemLevel)
1445				break;
1446		}
1447	}
1448	else
1449		return 0;
1450	return SubItemPos-ItemPos-1;
1451}
1452
1453
1454void ColumnListView::Expand(CLVListItem* item)
1455{
1456	BWindow* ParentWindow = Window();
1457	if(ParentWindow)
1458		ParentWindow->Lock();
1459	if(!(item->fSuperItem))
1460		item->fSuperItem = true;
1461	if(item->IsExpanded())
1462	{
1463		if(ParentWindow)
1464			ParentWindow->Unlock();
1465		return;
1466	}
1467	item->SetExpanded(true);
1468	if(!fHierarchical)
1469	{
1470		if(ParentWindow)
1471			ParentWindow->Unlock();
1472		return;
1473	}
1474
1475	int32 DisplayIndex = IndexOf(item);
1476	if(DisplayIndex >= 0)
1477	{
1478		if(fExpanderColumn >= 0)
1479		{
1480			//Change the state of the arrow
1481			item->DrawItemColumn(this,item->fExpanderColumnRect,fExpanderColumn, (fExpanderColumn == _selectedColumn), true);
1482			SetDrawingMode(B_OP_OVER);
1483			DrawBitmap(&fDownArrow, BRect(0.0,0.0,item->fExpanderButtonRect.right-
1484				item->fExpanderButtonRect.left,10.0),item->fExpanderButtonRect);
1485			SetDrawingMode(B_OP_COPY);
1486		}
1487
1488		//Add the items under it
1489		int32 FullListIndex = fFullItemList.IndexOf(item);
1490		uint32 ItemLevel = item->fOutlineLevel;
1491		int32 Counter = FullListIndex + 1;
1492		int32 AddPos = DisplayIndex + 1;
1493		while(true)
1494		{
1495			CLVListItem* NextItem = (CLVListItem*)fFullItemList.ItemAt(Counter);
1496			if(NextItem == NULL)
1497				break;
1498			if(NextItem->fOutlineLevel > ItemLevel)
1499			{
1500				BListView::AddItem((BListItem*)NextItem,AddPos++);
1501				if(NextItem->fSuperItem && !NextItem->IsExpanded())
1502				{
1503					//The item I just added is collapsed, so skip all its children
1504					uint32 SkipLevel = NextItem->fOutlineLevel + 1;
1505					while(true)
1506					{
1507						Counter++;
1508						NextItem = (CLVListItem*)fFullItemList.ItemAt(Counter);
1509						if(NextItem == NULL)
1510							break;
1511						if(NextItem->fOutlineLevel < SkipLevel)
1512							break;
1513					}
1514				}
1515				else
1516					Counter++;
1517			}
1518			else
1519				break;
1520		}
1521	}
1522	if(ParentWindow)
1523		ParentWindow->Unlock();
1524}
1525
1526
1527void ColumnListView::Collapse(CLVListItem* item)
1528{
1529	BWindow* ParentWindow = Window();
1530	if(ParentWindow)
1531		ParentWindow->Lock();
1532	if(!(item->fSuperItem))
1533		item->fSuperItem = true;
1534	if(!(item->IsExpanded()))
1535	{
1536		if(ParentWindow)
1537			ParentWindow->Unlock();
1538		return;
1539	}
1540	item->SetExpanded(false);
1541	if(!fHierarchical)
1542	{
1543		if(ParentWindow)
1544			ParentWindow->Unlock();
1545		return;
1546	}
1547
1548	int32 DisplayIndex = IndexOf((BListItem*)item);
1549	if(DisplayIndex >= 0)
1550	{
1551		if(fExpanderColumn >= 0)
1552		{
1553			//Change the state of the arrow
1554			item->DrawItemColumn(this,item->fExpanderColumnRect,fExpanderColumn,(fExpanderColumn == _selectedColumn), true);
1555			SetDrawingMode(B_OP_OVER);
1556			DrawBitmap(&fRightArrow, BRect(0.0,0.0,item->fExpanderButtonRect.right-
1557				item->fExpanderButtonRect.left,10.0),item->fExpanderButtonRect);
1558			SetDrawingMode(B_OP_COPY);
1559		}
1560
1561		//Remove the items under it
1562		uint32 ItemLevel = item->fOutlineLevel;
1563		int32 NextItemIndex = DisplayIndex+1;
1564		while(true)
1565		{
1566			CLVListItem* NextItem = (CLVListItem*)ItemAt(NextItemIndex);
1567			if(NextItem)
1568			{
1569				if(NextItem->fOutlineLevel > ItemLevel)
1570					BListView::RemoveItem(NextItemIndex);
1571				else
1572					break;
1573			}
1574			else
1575				break;
1576		}
1577	}
1578	if(ParentWindow)
1579		ParentWindow->Unlock();
1580}
1581
1582
1583bool ColumnListView::IsExpanded(int32 fullListIndex) const
1584{
1585	BListItem* TheItem = (BListItem*)fFullItemList.ItemAt(fullListIndex);
1586	if(TheItem)
1587		return TheItem->IsExpanded();
1588	else
1589		return false;
1590}
1591
1592
1593void ColumnListView::SetSortFunction(CLVCompareFuncPtr compare)
1594{
1595	fCompare = compare;
1596}
1597
1598
1599void ColumnListView::SortItems()
1600{
1601	BWindow* ParentWindow = Window();
1602	if(ParentWindow)
1603		ParentWindow->Lock();
1604
1605	BList NewList;
1606	int32 NumberOfItems;
1607	if(!fHierarchical)
1608		NumberOfItems = CountItems();
1609	else
1610		NumberOfItems = fFullItemList.CountItems();
1611	if(NumberOfItems == 0)
1612	{
1613		if(ParentWindow)
1614			ParentWindow->Unlock();
1615		return;
1616	}
1617	int32 Counter;
1618	if(!fHierarchical)
1619	{
1620		//Plain sort
1621		//Remember the list context for each item
1622		for(Counter = 0; Counter < NumberOfItems; Counter++)
1623			((CLVListItem*)ItemAt(Counter))->fSortingContextCLV = this;
1624		//Do the actual sort
1625		BListView::SortItems((int (*)(const void*, const void*))ColumnListView::PlainBListSortFunc);
1626	}
1627	else
1628	{
1629		//Block-by-block sort
1630		SortFullListSegment(0,0,&NewList);
1631		fFullItemList = NewList;
1632		//Remember the list context for each item
1633		for(Counter = 0; Counter < NumberOfItems; Counter++)
1634			((CLVListItem*)fFullItemList.ItemAt(Counter))->fSortingContextBList = &NewList;
1635		//Do the actual sort
1636		BListView::SortItems((int (*)(const void*, const void*))ColumnListView::HierarchicalBListSortFunc);
1637	}
1638
1639	if(ParentWindow)
1640		ParentWindow->Unlock();
1641}
1642
1643
1644int ColumnListView::PlainBListSortFunc(BListItem** a_item1, BListItem** a_item2)
1645{
1646	CLVListItem* item1 = (CLVListItem*)*a_item1;
1647	CLVListItem* item2 = (CLVListItem*)*a_item2;
1648	ColumnListView* SortingContext = item1->fSortingContextCLV;
1649	int32 SortDepth = SortingContext->fSortKeyList.CountItems();
1650	int CompareResult = 0;
1651	if(SortingContext->fCompare)
1652		for(int32 SortIteration = 0; SortIteration < SortDepth && CompareResult == 0; SortIteration++)
1653		{
1654			CLVColumn* Column = (CLVColumn*)SortingContext->fSortKeyList.ItemAt(SortIteration);
1655			CompareResult = SortingContext->fCompare(item1,item2,SortingContext->fColumnList.IndexOf(Column));
1656			if(Column->fSortMode == Descending)
1657				CompareResult = 0-CompareResult;
1658		}
1659	return CompareResult;
1660}
1661
1662
1663int ColumnListView::HierarchicalBListSortFunc(BListItem** a_item1, BListItem** a_item2)
1664{
1665	CLVListItem* item1 = (CLVListItem*)*a_item1;
1666	CLVListItem* item2 = (CLVListItem*)*a_item2;
1667	if(item1->fSortingContextBList->IndexOf(item1) < item1->fSortingContextBList->IndexOf(item2))
1668		return -1;
1669	else
1670		return 1;
1671}
1672
1673
1674void ColumnListView::SortFullListSegment(int32 OriginalListStartIndex, int32 InsertionPoint,
1675	BList* NewList)
1676{
1677	//Identify and sort the items at this level
1678	BList* ItemsInThisLevel = SortItemsInThisLevel(OriginalListStartIndex);
1679	int32 NewItemsStopIndex = InsertionPoint + ItemsInThisLevel->CountItems();
1680	NewList->AddList(ItemsInThisLevel,InsertionPoint);
1681	delete ItemsInThisLevel;
1682
1683	//Identify and sort the subitems
1684	for(int32 Counter = InsertionPoint; Counter < NewItemsStopIndex; Counter++)
1685	{
1686		CLVListItem* ThisItem = (CLVListItem*)NewList->ItemAt(Counter);
1687		CLVListItem* NextItem = (CLVListItem*)fFullItemList.ItemAt(fFullItemList.IndexOf(ThisItem)+1);
1688		if(ThisItem->IsSuperItem() && NextItem && ThisItem->fOutlineLevel < NextItem->fOutlineLevel)
1689		{
1690			int32 OldListSize = NewList->CountItems();
1691			SortFullListSegment(fFullItemList.IndexOf(ThisItem)+1,Counter+1,NewList);
1692			int32 NewListSize = NewList->CountItems();
1693			NewItemsStopIndex += NewListSize - OldListSize;
1694			Counter += NewListSize - OldListSize;
1695		}
1696	}
1697}
1698
1699
1700BList* ColumnListView::SortItemsInThisLevel(int32 OriginalListStartIndex)
1701{
1702	uint32 ThisLevel = ((CLVListItem*)fFullItemList.ItemAt(OriginalListStartIndex))->fOutlineLevel;
1703
1704	//Create a new BList of the items in this level
1705	int32 Counter = OriginalListStartIndex;
1706	int32 ItemsInThisLevel = 0;
1707	BList* ThisLevelItems = new BList(16);
1708	while(true)
1709	{
1710		CLVListItem* ThisItem = (CLVListItem*)fFullItemList.ItemAt(Counter);
1711		if(ThisItem == NULL)
1712			break;
1713		uint32 ThisItemLevel = ThisItem->fOutlineLevel;
1714		if(ThisItemLevel == ThisLevel)
1715		{
1716			ThisLevelItems->AddItem(ThisItem);
1717			ItemsInThisLevel++;
1718		}
1719		else if(ThisItemLevel < ThisLevel)
1720			break;
1721		Counter++;
1722	}
1723
1724	//Sort the BList of the items in this level
1725	CLVListItem** SortArray = new CLVListItem*[ItemsInThisLevel];
1726	CLVListItem** ListItems = (CLVListItem**)ThisLevelItems->Items();
1727	for(Counter = 0; Counter < ItemsInThisLevel; Counter++)
1728		SortArray[Counter] = ListItems[Counter];
1729	ThisLevelItems->MakeEmpty();
1730	SortListArray(SortArray,ItemsInThisLevel);
1731	for(Counter = 0; Counter < ItemsInThisLevel; Counter++)
1732		ThisLevelItems->AddItem(SortArray[Counter]);
1733	delete [] SortArray;
1734	return ThisLevelItems;
1735}
1736
1737
1738void ColumnListView::SortListArray(CLVListItem** SortArray, int32 NumberOfItems)
1739{
1740	int32 SortDepth = fSortKeyList.CountItems();
1741	for(int32 Counter1 = 0; Counter1 < NumberOfItems-1; Counter1++)
1742		for(int32 Counter2 = Counter1+1; Counter2 < NumberOfItems; Counter2++)
1743		{
1744			int CompareResult = 0;
1745			if(fCompare)
1746				for(int32 SortIteration = 0; SortIteration < SortDepth && CompareResult == 0; SortIteration++)
1747				{
1748					CLVColumn* Column = (CLVColumn*)fSortKeyList.ItemAt(SortIteration);
1749					CompareResult = fCompare(SortArray[Counter1],SortArray[Counter2],fColumnList.IndexOf(Column));
1750					if(Column->fSortMode == Descending)
1751						CompareResult = 0-CompareResult;
1752				}
1753			if(CompareResult == 1)
1754			{
1755				CLVListItem* Temp = SortArray[Counter1];
1756				SortArray[Counter1] = SortArray[Counter2];
1757				SortArray[Counter2] = Temp;
1758			}
1759		}
1760}
1761
1762
1763void ColumnListView :: MessageReceived(BMessage * msg)
1764{
1765   switch(msg->what)
1766   {
1767      case B_UNMAPPED_KEY_DOWN:
1768         if (_editMessage != NULL)
1769         {
1770            BMessage temp(*_editMessage);
1771            temp.AddInt32("column", _selectedColumn);
1772            temp.AddInt32("row", CurrentSelection());
1773
1774            int32 key;
1775            if (msg->FindInt32("key", &key) == B_NO_ERROR) temp.AddInt32("unmappedkey", key);
1776            _editTarget.SendMessage(&temp);
1777         }
1778      break;
1779
1780      default:
1781         BListView::MessageReceived(msg);
1782      break;
1783   }
1784}
1785