API

리스트뷰 아이템 스왑, 이동, 정렬 관련 API 소스

디버그정 2009. 9. 3. 20:51


// 컬럼 수는 외부에서 입력한다. 이 함수는 빈번하게 사용하므로 Header_GetItemCount 호출은 낭비.
void __stdcall ListItemSwap(HWND hList, int One, int Two, int iColCount)
{
 // 텍스트 제외 속성 교환
 LVITEM liOne={0}, liTwo={0};
 liOne.mask = liTwo.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
 liOne.stateMask = liTwo.stateMask = LVIS_CUT | LVIS_DROPHILITED | LVIS_FOCUSED | LVIS_SELECTED | LVIS_OVERLAYMASK | LVIS_STATEIMAGEMASK;
 liOne.iSubItem = liTwo.iSubItem = 0;
 
 liOne.iItem = One;
 liTwo.iItem = Two;
 ListView_GetItem(hList, &liOne);
 ListView_GetItem(hList, &liTwo);

 liOne.iItem = Two;
 liTwo.iItem = One;
 ListView_SetItem(hList, &liOne);
 ListView_SetItem(hList, &liTwo);


 // 텍스트 교환
 int i;
 TCHAR szTextOne[1024]={0}, szTextTwo[1024]={0};
 liOne.mask = liTwo.mask = LVIF_TEXT;
 liOne.cchTextMax = liTwo.cchTextMax = sizeof(szTextOne);
 
 liOne.iItem = One;
 liTwo.iItem = Two;
 liOne.pszText = szTextOne;
 liTwo.pszText = szTextTwo;

 for(i=0; i<iColCount; i++){
  liOne.iSubItem = liTwo.iSubItem = i;
  SendMessage(hList, LVM_GETITEMTEXT, One, (LPARAM)&liOne);
  SendMessage(hList, LVM_GETITEMTEXT, Two, (LPARAM)&liTwo);

  SendMessage(hList, LVM_SETITEMTEXT, Two, (LPARAM)&liOne);
  SendMessage(hList, LVM_SETITEMTEXT, One, (LPARAM)&liTwo);
 } 

 /* 참고) 텍스트 교환 매크로 함수 사용시
          (함수 실행시마다 LVITEM 변수를 각각 선언하므로 좀 더 느리다.)
 int i;
 TCHAR szTextOne[1024]={0}, szTextTwo[1024]={0};

 for(i=0; i<iColCount; i++){
  ListView_GetItemText(hList, One, i, szTextOne, sizeof(szTextOne));
  ListView_GetItemText(hList, Two, i, szTextTwo, sizeof(szTextTwo));
  ListView_SetItemText(hList, Two, i, szTextOne);
  ListView_SetItemText(hList, One, i, szTextTwo);
 }*/
}

 

int __stdcall ListItemUp(HWND hList)
{
 int iCount = ListView_GetItemCount(hList);
 if(!iCount) return 0;
 int iSelCount = ListView_GetSelectedCount(hList);
 if(!iSelCount) return 0;

 int iColCount=Header_GetItemCount(GetDlgItem(hList,0));

 // iPre는 이전 처리항목의 인덱스, 비교해서 불필요한/엉뚱한 이동 방지
 int i, iPre=-1, index=-1, iUp, iSwapCount=0;
 for(i=0 ; i<iSelCount ; i++){
  index = ListView_GetNextItem(hList, index, LVNI_ALL|LVNI_SELECTED);
  if(index==0){ // 맨 첫라인인 경우 이동은 불필요하다.
   iPre=0;
   continue;
  }

  iUp = index-1;  // 이동할 인덱스
  if(iUp == iPre){ // 이전 처리항목의 인덱스와 같으면 스왑하면 안된다.
   iPre=index;
  } else {
   ListItemSwap(hList, index, iUp, iColCount);
   iPre=iUp;
   ++iSwapCount;
  }
 }
 return iSwapCount;
}

 

int __stdcall ListItemFirst(HWND hList)
{
 int iCount = ListView_GetItemCount(hList);
 if(!iCount) return 0;
 int iSelCount = ListView_GetSelectedCount(hList);
 if(!iSelCount) return 0;

 int iColCount=Header_GetItemCount(GetDlgItem(hList,0));

 int i, j, index=-1, iInsertCount=0;
 LVITEM li={0};
 li.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
 li.stateMask = LVIS_CUT | LVIS_DROPHILITED | LVIS_FOCUSED | LVIS_SELECTED | LVIS_OVERLAYMASK | LVIS_STATEIMAGEMASK;
 li.iSubItem = 0;
 for(i=0 ; i<iSelCount ; i++){
  index = ListView_GetNextItem(hList, index, LVNI_ALL|LVNI_SELECTED);
  if(i == index) continue; // 이동할 필요가 없는 경우

  // 속성을 같게하면서 아이템 삽입 
  li.iItem = index;
  ListView_GetItem(hList, &li);
  
  li.iItem = i;
  ListView_InsertItem(hList, &li);

  // 이제 텍스트 복사
  TCHAR szText[1024]={0};
  for(j=0; j<iColCount; j++){ // 아이템 삽입해서 한 줄 밀리므로 +1을 해줘야 한다.
   ListView_GetItemText(hList, index+1, j, szText, sizeof(szText));
   ListView_SetItemText(hList, i, j, szText);
  }

  ++iInsertCount;
  
  // 복사가 끝났으므로 소스행 삭제
  ListView_DeleteItem(hList, index+1);

 }
 return iInsertCount;
}

 


int __stdcall ListItemDown(HWND hList)
{
 int iCount = ListView_GetItemCount(hList);
 if(!iCount) return 0;
 int iSelCount = ListView_GetSelectedCount(hList);
 if(!iSelCount) return 0;

 int iColCount=Header_GetItemCount(GetDlgItem(hList,0));

 // iPre는 이전 처리항목의 인덱스, 비교해서 불필요한/엉뚱한 이동 방지
 int i, iPre=iCount, index=iCount-1, iDown, iSwapCount=0;
 for(i=0 ; i<iSelCount ; i++){ // 맨아래 선택항목부터 처리해야 한다.
  // 끝라인이 선택된 경우 인덱스는 아래와 같은 식으로 구해야 된다.
  if(i==0 && LVIS_SELECTED == ListView_GetItemState(hList, iCount-1, LVIS_SELECTED)){
   index=iCount-1;
  } else {
   index = ListView_GetNextItem(hList, index, LVNI_ALL|LVNI_ABOVE|LVNI_SELECTED);
  }
  if(index == iCount-1){ // 맨 끝라인인 경우 이동은 불필요하다.
   iPre=iCount-1;
   continue;
  }
  
  iDown = index+1;  // 이동할 인덱스
  if(iDown == iPre){  // 이전 처리항목의 인덱스와 같으면 스왑하면 안된다.
   iPre=index;
  } else {
   ListItemSwap(hList, index, iDown, iColCount);
   iPre=iDown;
   ++iSwapCount;
  }
 }
 return iSwapCount;
}

 


int __stdcall ListItemLast(HWND hList)
{
 int iCount = ListView_GetItemCount(hList);
 if(!iCount) return 0;
 int iSelCount = ListView_GetSelectedCount(hList);
 if(!iSelCount) return 0;

 int iColCount=Header_GetItemCount(GetDlgItem(hList,0));

 int i, j, index=iCount-1, iInsertCount=0;
 LVITEM li={0};
 li.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
 li.stateMask = LVIS_CUT | LVIS_DROPHILITED | LVIS_FOCUSED | LVIS_SELECTED | LVIS_OVERLAYMASK | LVIS_STATEIMAGEMASK;
 li.iSubItem = 0;
 for(i=0 ; i<iSelCount ; i++){
  // 끝라인이 선택된 경우 인덱스는 아래와 같은 식으로 구해야 된다.
  if(i==0 && LVIS_SELECTED == ListView_GetItemState(hList, iCount-1, LVIS_SELECTED)){
   index=iCount-1;
  } else {
   index = ListView_GetNextItem(hList, index, LVNI_ALL|LVNI_ABOVE|LVNI_SELECTED);
  }
  if(iCount-i-1 == index) continue; // 이동할 필요가 없는 경우

  // 속성을 같게하면서 아이템 삽입
  li.iItem = index;
  ListView_GetItem(hList, &li);
  
  li.iItem = iCount-i;
  ListView_InsertItem(hList, &li);

  // 이제 텍스트 복사
  TCHAR szText[1024]={0};
  for(j=0; j<iColCount; j++){
   ListView_GetItemText(hList, index, j, szText, sizeof(szText));
   ListView_SetItemText(hList, iCount-i, j, szText);
  }
  
  ++iInsertCount;

  // 복사가 끝났으므로 소스행 삭제
  ListView_DeleteItem(hList, index);

 }
 return iInsertCount;
}

 

// return value is swap count.
int __stdcall ListItemArrange(HWND hList, int iColAxis, BOOL bAscending)
{
 int i, j, iItemCount;
 iItemCount=ListView_GetItemCount(hList);
 if(!iItemCount) return 0;

 int iColCount=Header_GetItemCount(GetDlgItem(hList,0));

 // 비교할 때마다 ListView_GetItemText는 낭비이므로
 // 일괄적으로 해당 열의 문자열을 뽑아 놓는다. 메모리 공간 확보: [1024]크기의 문자열 포인터(배열 포인터)
 TCHAR (*apszText)[1024]=(TCHAR(*)[1024])VirtualAlloc(NULL, iItemCount*1024*sizeof(TCHAR), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 if(apszText==NULL) return 0;
 for(i=0 ; i<iItemCount ; i++){
  ListView_GetItemText(hList, i, iColAxis, apszText[i], 1024*sizeof(TCHAR));
 }

 // 선택 정렬
 int index, iSwapCount=0;
 TCHAR szTemp[1024]={0};
 for(i=0 ; i<iItemCount-1 ; i++){
  index=i;
  for(j=i+1 ; j<iItemCount ; j++){  // 문자열 비교 루프
   if(bAscending){  // 오름차순(0,1,2,3~~~ 갈수록 값이 올라간다.)
    if(lstrcmpi(apszText[index], apszText[j])>0) index=j;
   } else {   // 내림차순(10,9,8,7~~~ 갈수록 값이 내려간다.)
    if(lstrcmpi(apszText[index], apszText[j])<0) index=j;
   }
  }

  if(index != i){  // 같으면 스왑할 필요 없으므로
   ListItemSwap(hList, index, i, iColCount);
   ++iSwapCount;

   // 앞의 문자열 메모리도 맞게 스왑
   lstrcpy(szTemp, apszText[index]);
   lstrcpy(apszText[index], apszText[i]);
   lstrcpy(apszText[i], szTemp);
  }
 }
 VirtualFree(apszText, 0, MEM_RELEASE);
 return iSwapCount;
}