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