OSDN Git Service

Avoid an assertion failure when loading settings from winmerge.ini
[winmerge-jp/winmerge-jp.git] / Src / SubeditList.cpp
1 // SubeditList.cpp : implementation file
2 //
3
4 #include "stdafx.h"
5 #include "SubeditList.h"
6 #include "Win_VersionHelper.h"
7 #include "WildcardDropList.h"
8
9 #ifdef _DEBUG
10 #define new DEBUG_NEW
11 #undef THIS_FILE
12 static char THIS_FILE[] = __FILE__;
13 #endif
14
15 #define IDC_IPEDIT 1000
16
17 /// Some stuff is from https://www.codeguru.com/cpp/controls/listview/editingitemsandsubitem/article.php/c923/Editable-subitems.htm
18
19 /////////////////////////////////////////////////////////////////////////////
20 // CSubeditList
21
22 CSubeditList::CSubeditList()
23 {
24 }
25
26 CSubeditList::~CSubeditList()
27 {
28 }
29
30 void CSubeditList::SetItemBooleanValue(int nItem, int nSubItem, bool value)
31 {
32         if (IsWin7_OrGreater())
33                 SetItemText(nItem, nSubItem, value ? _T("\u2611") : _T("\u2610"));
34         else
35                 SetItemText(nItem, nSubItem, value ? _T("true") : _T("false"));
36 }
37
38 bool CSubeditList::GetItemBooleanValue(int nItem, int nSubItem) const
39 {
40         CString text = GetItemText(nItem, nSubItem);
41         return (text.Compare(_T("true")) == 0 || text.Compare(_T("\u2611")) == 0);
42 }
43
44 /**
45  * @brief Get the edit style for the specified column.
46  * @param [in] nCol Column to get edit style
47  * @return\81@Edit style for the specified column
48  * @remarks Returns EditStyle::EDIT_BOX; as default if a column with no edit style is specified.
49  */
50 CSubeditList::EditStyle CSubeditList::GetEditStyle(int nCol) const
51 {
52         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
53         int nColumnCount = pHeader->GetItemCount();
54
55         if (nCol < 0 || nCol >= nColumnCount)
56                 return EditStyle::EDIT_BOX;
57
58         if (nCol >= m_editStyle.size())
59                 return EditStyle::EDIT_BOX;
60
61         return m_editStyle.at(nCol);
62 }
63
64 /**
65  * @brief Set the edit style for the specified column.
66  * @param [in] nCol Column to set the edit style
67  * @param [in] style Edit style to set
68  */
69 void CSubeditList::SetEditStyle(int nCol, EditStyle style)
70 {
71         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
72         int nColumnCount = pHeader->GetItemCount();
73         if (nCol < 0 || nCol >= nColumnCount)
74                 return;
75
76         for (size_t i = m_editStyle.size(); i <= nCol; i++)
77                 m_editStyle.push_back(EditStyle::EDIT_BOX);
78
79         m_editStyle[nCol] = style;
80 }
81
82 /**
83  * @brief Get the character limit for the specified column.
84  * @param [in] nCol Column to get character limit
85  * @return\81@Character limit for the specified column
86  * @remarks Currently, this setting is valid only for columns whose edit style is EditStyle::WILDCARD_DROPLIST.
87  */
88 int CSubeditList::GetLimitTextSize(int nCol) const
89 {
90         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
91         int nColumnCount = pHeader->GetItemCount();
92
93         if (nCol < 0 || nCol >= nColumnCount)
94                 return 0;
95
96         // Currently, this setting is valid only for columns whose edit style is EditStyle::WILDCARD_DROPLIST.
97         if (GetEditStyle(nCol) != EditStyle::WILDCARD_DROP_LIST)
98                 return 0;
99
100         if (nCol >= m_limitTextSize.size())
101                 return 0;
102
103         return m_limitTextSize.at(nCol);
104 }
105
106 /**
107  * @brief Set the character limit for the specified column.
108  * @param [in] nCol Column to set the character limit
109  * @param [in] nLimitTextSize Character limit to set
110  * @remarks Currently, this setting is valid only for columns whose edit style is EditStyle::WILDCARD_DROPLIST.
111  */
112 void CSubeditList::SetLimitTextSize(int nCol, int nLimitTextSize)
113 {
114         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
115         int nColumnCount = pHeader->GetItemCount();
116         if (nCol < 0 || nCol >= nColumnCount)
117                 return;
118
119         // Currently, this setting is valid only for columns whose edit style is EditStyle::WILDCARD_DROPLIST.
120         if (GetEditStyle(nCol) != EditStyle::WILDCARD_DROP_LIST)
121                 return;
122
123         for (size_t i = m_limitTextSize.size(); i <= nCol; i++)
124                 m_limitTextSize.push_back(0);
125
126         m_limitTextSize[nCol] = nLimitTextSize;
127 }
128
129 /**
130  * @brief Get the wildcard drop list fixed pattern for the specified cell.
131  * @param [in] nItem Th row index to get wildcard drop list fixed pattern
132  * @param [in] nSubItem The column to get wildcard drop list fixed pattern
133  * @return Wildcard drop list fixed pattern for the specified cell
134  */
135 String CSubeditList::GetDropListFixedPattern(int nItem, int nSubItem) const
136 {
137         int nItemCount = GetItemCount();
138         if (nItem < 0 || nItem >= nItemCount)
139                 return _T("");
140
141         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
142         int nColumnCount = pHeader->GetItemCount();
143         if (nSubItem < 0 || nSubItem >= nColumnCount)
144                 return _T("");
145
146         // This setting is valid only for columns whose edit style is EditStyle::WILDCARD_DROPLIST.
147         if (GetEditStyle(nSubItem) != EditStyle::WILDCARD_DROP_LIST)
148                 return _T("");
149
150         if (nItem < m_dropListFixedPattern.size())
151                 if (nSubItem < m_dropListFixedPattern[nItem].size())
152                         return m_dropListFixedPattern[nItem][nSubItem];
153
154         return _T("");
155 }
156
157 /**
158  * @brief Set the wildcard drop list fixed pattern for the specified cell.
159  * @param [in] nItem The row index to set wildcard drop list fixed pattern
160  * @param [in] nSubItem The column to set wildcard drop list fixed pattern
161  * @param [in] fixedPattern Wildcard drop list fixed pattern to set
162  */
163 void CSubeditList::SetDropListFixedPattern(int nItem, int nSubItem, const String& fixedPattern)
164 {
165         int nItemCount = GetItemCount();
166         if (nItem < 0 || nItem >= nItemCount)
167                 return;
168
169         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
170         int nColumnCount = pHeader->GetItemCount();
171         if (nSubItem < 0 || nSubItem >= nColumnCount)
172                 return;
173
174         // This setting is valid only for columns whose edit style is EditStyle::WILDCARD_DROPLIST.
175         if (GetEditStyle(nSubItem) != EditStyle::WILDCARD_DROP_LIST)
176                 return;
177
178         for (size_t i = m_dropListFixedPattern.size(); i <= nItem; i++)
179                 m_dropListFixedPattern.push_back(std::vector<String>());
180
181         for (size_t i = m_dropListFixedPattern[nItem].size(); i <= nSubItem; i++)
182                 m_dropListFixedPattern[nItem].push_back(_T(""));
183
184         m_dropListFixedPattern[nItem][nSubItem] = fixedPattern;
185 }
186
187 // HitTestEx    - Determine the row index and column index for a point
188 // Returns      - the row index or -1 if point is not over a row
189 // point        - point to be tested.
190 // col          - to hold the column index
191 int CSubeditList::HitTestEx(CPoint &point, int *col) const
192 {
193         int colnum = 0;
194         int row = HitTest( point, NULL );
195         
196         if( col ) *col = 0;
197  
198         // Make sure that the ListView is in LVS_REPORT
199         if( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
200                 return row;
201  
202         // Get the top and bottom row visible
203         row = GetTopIndex();
204         int bottom = row + GetCountPerPage();
205         if( bottom > GetItemCount() )
206                 bottom = GetItemCount();
207         
208         // Get the number of columns
209         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
210         int nColumnCount = pHeader->GetItemCount();
211  
212         // Loop through the visible rows
213         for( ;row <=bottom;row++)
214         {
215                 // Get bounding rect of item and check whether point falls in it.
216                 CRect rect;
217                 GetItemRect( row, &rect, LVIR_BOUNDS );
218                 if( rect.PtInRect(point) )
219                 {
220                         // Now find the column
221                         for( colnum = 0; colnum < nColumnCount; colnum++ )
222                         {
223                                 int colwidth = GetColumnWidth(colnum);
224                                 if( point.x >= rect.left 
225                                         && point.x <= (rect.left + colwidth ) )
226                                 {
227                                         if( col ) *col = colnum;
228                                         return row;
229                                 }
230                                 rect.left += colwidth;
231                         }
232                 }
233         }
234         return -1;
235 }
236
237
238 BEGIN_MESSAGE_MAP(CSubeditList, CListCtrl)
239         //{{AFX_MSG_MAP(CSubeditList)
240                 // NOTE - the ClassWizard will add and remove mapping macros here.
241         ON_NOTIFY(LVN_ENDLABELEDIT, IDC_SUBSTITUTION_FILTERS, OnEndLabelEdit)
242         ON_WM_LBUTTONDOWN()
243         //}}AFX_MSG_MAP
244 END_MESSAGE_MAP()
245
246 /////////////////////////////////////////////////////////////////////////////
247 // CSubeditList message handlers
248
249
250 // EditSubLabel         - Start edit of a sub item label
251 // Returns              - Temporary pointer to the new edit control
252 // nItem                - The row index of the item to edit
253 // nCol                 - The column of the sub item.
254 //CEdit* CSubeditList::EditSubLabel(int nItem, int nCol)
255 CInPlaceEdit* CSubeditList::EditSubLabel( int nItem, int nCol )
256 {
257         // The returned pointer should not be saved
258          
259         // Make sure that the item is visible
260         if( !EnsureVisible( nItem, TRUE ) ) return NULL;
261          
262         // Make sure that nCol is valid
263         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
264         int nColumnCount = pHeader->GetItemCount();
265         if( nCol >= nColumnCount || GetColumnWidth(nCol) < 5 )
266                 return NULL;
267          
268         if (GetEditStyle(nCol) != EditStyle::EDIT_BOX)
269                 return NULL;
270         // Get the column offset
271         int offset = 0;
272         for( int i = 0; i < nCol; i++ )
273                 offset += GetColumnWidth( i );
274          
275         CRect rect;
276         GetItemRect( nItem, &rect, LVIR_BOUNDS );
277          
278         // Now scroll if we need to expose the column
279         CRect rcClient;
280         GetClientRect( &rcClient );
281         if( offset + rect.left < 0 || offset + rect.left > rcClient.right )
282         {
283                 CSize size;
284                 size.cx = offset + rect.left;
285                 size.cy = 0;
286                 Scroll( size );
287                 rect.left -= size.cx;
288         }
289
290         // Get Column alignment
291         LV_COLUMN lvcol;
292         lvcol.mask = LVCF_FMT;
293         GetColumn( nCol, &lvcol );
294         DWORD dwStyle ;
295         if((lvcol.fmt&LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
296                 dwStyle = ES_LEFT;
297         else if((lvcol.fmt&LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
298                 dwStyle = ES_RIGHT;
299         else dwStyle = ES_CENTER;
300
301         rect.left += offset+4;
302         rect.right = rect.left + GetColumnWidth( nCol ) - 3 ;
303         if( rect.right > rcClient.right) rect.right = rcClient.right;
304
305         dwStyle |= WS_BORDER|WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL;
306         CInPlaceEdit *pEdit = new CInPlaceEdit(nItem, nCol, GetItemText( nItem, nCol ));
307         pEdit->Create( dwStyle, rect, this, IDC_IPEDIT );
308
309         return pEdit;
310 }
311
312 /**
313  * @brief Start edit of a sub item label with wilcard drop list.
314  * @param [in] nItem The row index of the item to edit
315  * @param [in] nCol The column of the sub item
316  */
317 void CSubeditList::EditSubLabelWildcardDropList( int nItem, int nCol )
318 {
319         // Make sure that the item is visible
320         if( !EnsureVisible( nItem, TRUE ) ) return;
321
322         // Make sure that nCol is valid
323         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
324         int nColumnCount = pHeader->GetItemCount();
325         if( nCol >= nColumnCount || GetColumnWidth(nCol) < 5 )
326                 return;
327
328         if (GetEditStyle(nCol) != EditStyle::WILDCARD_DROP_LIST)
329                 return;
330
331         CString pattern = GetDropListFixedPattern(nItem, nCol).c_str();
332         int nLimitTextSize = GetLimitTextSize(nCol);
333         WildcardDropList::OnItemActivate(m_hWnd, nItem, nCol, 4, pattern, true, nLimitTextSize);
334 }
335
336 void CSubeditList::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
337 {
338         if( GetFocus() != this ) SetFocus();
339         CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
340 }
341
342 void CSubeditList::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
343 {
344         if( GetFocus() != this ) SetFocus();
345         CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
346 }
347
348 void CSubeditList::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
349 {
350         LV_DISPINFO *plvDispInfo = (LV_DISPINFO *)pNMHDR;
351         LV_ITEM *plvItem = &plvDispInfo->item;
352          
353         if (plvItem->pszText != NULL)
354         {
355                 SetItemText(plvItem->iItem, plvItem->iSubItem, plvItem->pszText);
356         }
357         *pResult = FALSE;
358 }
359
360 void CSubeditList::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
361 {
362         LV_DISPINFO* plvDispInfo = (LV_DISPINFO*)pNMHDR;
363         LV_ITEM* plvItem = &plvDispInfo->item;
364         plvItem->iSubItem = 1;
365
366 //      if (plvItem->pszText != NULL)
367 //      {
368 //              SetItemText(plvItem->iItem, plvItem->iSubItem, plvItem->pszText);
369 //      }
370         *pResult = FALSE;
371 }
372
373 void CSubeditList::OnLButtonDown(UINT nFlags, CPoint point)
374 {
375         int index;
376         int colnum;
377         CListCtrl::OnLButtonDown(nFlags, point);
378         if( ( index = HitTestEx( point, &colnum )) != -1 )
379         {
380                 UINT flag = LVIS_FOCUSED;
381                 //if ((GetItemState(index, flag) & flag) == flag && colnum > 0)
382                 if ((GetItemState(index, flag) & flag) == flag)
383                 {
384                         // Add check for LVS_EDITLABELS
385                         if (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_EDITLABELS)
386                         {
387                                 if (m_readOnlyColumns.find(colnum) == m_readOnlyColumns.end())
388                                         if (m_binaryValueColumns.find(colnum) != m_binaryValueColumns.end())
389                                         {
390                                                 CString text = GetItemText(index, colnum);
391                                                 if (IsWin7_OrGreater())
392                                                 {
393                                                         SetItemText(index, colnum, text.Compare(_T("\u2611")) == 0 ?
394                                                                 _T("\u2610") : _T("\u2611"));
395                                                 }
396                                                 else
397                                                 {
398                                                         SetItemText(index, colnum, text.Compare(_T("true")) == 0 ?
399                                                                 _T("false") : _T("true"));
400                                                 }
401                                         }
402                                         else
403                                         {
404                                                 switch (GetEditStyle(colnum))
405                                                 {
406                                                 case EditStyle::EDIT_BOX:
407                                                         EditSubLabel(index, colnum);
408                                                         break;
409                                                 case EditStyle::WILDCARD_DROP_LIST:
410                                                         EditSubLabelWildcardDropList(index, colnum);
411                                                         break;
412                                                 default:
413                                                         break;
414                                                 }
415                                         }
416                         }
417                 }
418                 else
419                 {
420                         SetItemState(index, LVIS_SELECTED | LVIS_FOCUSED,
421                                         LVIS_SELECTED | LVIS_FOCUSED);
422                 }
423         }
424 }
425
426 /////////////////////////////////////////////////////////////////////////////
427 // CInPlaceEdit
428
429 CInPlaceEdit::CInPlaceEdit(int iItem, int iSubItem, CString sInitText)
430 :m_sInitText( sInitText )
431 {
432         m_iItem = iItem;
433         m_iSubItem = iSubItem;
434         m_bESC = FALSE;
435 }
436
437 CInPlaceEdit::~CInPlaceEdit()
438 {
439 }
440
441
442 BEGIN_MESSAGE_MAP(CInPlaceEdit, CEdit)
443         //{{AFX_MSG_MAP(CInPlaceEdit)
444         ON_WM_KILLFOCUS()
445         ON_WM_NCDESTROY()
446         ON_WM_CHAR()
447         ON_WM_CREATE()
448         //}}AFX_MSG_MAP
449         ON_WM_LBUTTONDOWN()
450 END_MESSAGE_MAP()
451
452 /////////////////////////////////////////////////////////////////////////////
453 // CInPlaceEdit message handlers
454          
455 BOOL CInPlaceEdit::PreTranslateMessage(MSG* pMsg)
456 {
457         if( pMsg->message == WM_KEYDOWN )
458         {
459                 if(pMsg->wParam == VK_RETURN
460                                 || pMsg->wParam == VK_DELETE
461                                 || pMsg->wParam == VK_ESCAPE
462                                 || GetKeyState( VK_CONTROL)
463                                 )
464                 {
465                         ::TranslateMessage(pMsg);
466                         ::DispatchMessage(pMsg);
467                         return TRUE;                            // DO NOT process further
468                 }
469         }
470
471         return CEdit::PreTranslateMessage(pMsg);
472 }
473
474
475 void CInPlaceEdit::OnKillFocus(CWnd* pNewWnd)
476 {
477         CEdit::OnKillFocus(pNewWnd);
478
479         CString str;
480         GetWindowText(str);
481
482         // Send Notification to parent of ListView ctrl
483         LV_DISPINFO dispinfo;
484         dispinfo.hdr.hwndFrom = GetParent()->m_hWnd;
485         dispinfo.hdr.idFrom = GetDlgCtrlID();
486         dispinfo.hdr.code = LVN_ENDLABELEDIT;
487
488         dispinfo.item.mask = LVIF_TEXT;
489         dispinfo.item.iItem = m_iItem;
490         dispinfo.item.iSubItem = m_iSubItem;
491         dispinfo.item.pszText = m_bESC ? NULL : LPTSTR((LPCTSTR)str);
492         dispinfo.item.cchTextMax = str.GetLength();
493
494         GetParent()->GetParent()->SendMessage( WM_NOTIFY, GetParent()->GetDlgCtrlID(), 
495                                         (LPARAM)&dispinfo );
496
497         DestroyWindow();
498 }
499          
500 void CInPlaceEdit::OnNcDestroy()
501 {
502         CEdit::OnNcDestroy();
503
504         delete this;
505 }
506
507
508 void CInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
509 {
510         if( nChar == VK_ESCAPE || nChar == VK_RETURN)
511         {
512                 if( nChar == VK_ESCAPE )
513                         m_bESC = TRUE;
514                 GetParent()->SetFocus();
515                 return;
516         }
517
518
519         CEdit::OnChar(nChar, nRepCnt, nFlags);
520
521         // Resize edit control if needed
522
523         // Get text extent
524         CString str;
525
526         GetWindowText( str );
527         CWindowDC dc(this);
528         CFont *pFont = GetParent()->GetFont();
529         CFont *pFontDC = dc.SelectObject( pFont );
530         CSize size = dc.GetTextExtent( str );
531         dc.SelectObject( pFontDC );
532         size.cx += 5;                           // add some extra buffer
533
534         // Get client rect
535         CRect rect, parentrect;
536         GetClientRect( &rect );
537         GetParent()->GetClientRect( &parentrect );
538
539         // Transform rect to parent coordinates
540         ClientToScreen( &rect );
541         GetParent()->ScreenToClient( &rect );
542
543         // Check whether control needs to be resized
544         // and whether there is space to grow
545         if( size.cx > rect.Width() )
546         {
547                 if( size.cx + rect.left < parentrect.right )
548                         rect.right = rect.left + size.cx;
549                 else
550                         rect.right = parentrect.right;
551                 MoveWindow( &rect );
552         }
553 }
554
555 int CInPlaceEdit::OnCreate(LPCREATESTRUCT lpCreateStruct)
556 {
557         if (CEdit::OnCreate(lpCreateStruct) == -1)
558                 return -1;
559
560         // Set the proper font
561         CFont* font = GetParent()->GetFont();
562         SetFont(font);
563          
564         SetWindowText( m_sInitText );
565         SetFocus();
566         SetSel( 0, -1 );
567         return 0;
568 }