Objective Edit : Chapter 6 Customization : Working with the Gutter
Working with the Gutter
In Objective Edit version 6.x and later, the gutter is more customizable than it was in earlier versions. The gutter area is potentially divided into two regions, the index region and the mark region. In the index region, a zero-based line number for each text line is displayed; the mark region is comparable to the old gutter, where bookmarks, breakpoints, or other mark types are displayed. For backward compatibility, the default setting only displays the mark region. Use the SetGutterAlignment() function to customize the gutter display.
NOTE >> “Methods Related to the Gutter” contains two reference tables that list all of the relevant gutter-related methods in the SECEditController and SECEditViewport classes.
SECEditController::SetGutterAlignment(UINT nAlign)
This function sets the gutter format according to the nAlign value. The available nAlign values are:
ID_SECEDIT_GUTTER_MARKONLY
Display the mark region only. This is the default (and the old) behavior.
ID_SECEDIT_GUTTER_INDEXONLY
Display the index region only.
ID_SECEDIT_GUTTER_MARKINDEX
Display the mark region first, then the index region.
ID_SECEDIT_GUTTER_INDEXMARK
Display the index region first, then the mark region.
ID_SECEDIT_GUTTER_NOGUTTER
Do not display the gutter area.
Customizing the Index Region
As described above, the SetGutterAlignment() function can be used with the following nAlign values to display the index area:
ID_SECEDIT_GUTTER_INDEXONLY
ID_SECEDIT_GUTTER_MARKINDEX
ID_SECEDIT_GUTTER_INDEXMARK
If you choose to make the index area visible, the following functions can be used to further customize the index region:
SECEditController::SetGutterIndexDigits(int nDigits)
This function sets the number of digits that will be displayed in the index region. By default, or for nDigits = -1, Objective Edit uses the value for the maximum number of index digits, which changes as the value for the maximum number of line numbers changes.
SECEditController::SetGutterIndexWidth(int nWidth)
This function sets the width for the index region. By default, Objective Edit uses the value for the maximum number of index digits times the character width.
SECEditController::SetGutterIndexColor(COLORREF rgb)
This function sets the index text color.
SECEditController::GetGutterIndexFont()
GetGutterIndexFont() returns a pointer to the SECEditFontInfo object, which defines font information for the index region. The parameters you can change in SECEditFontInfo are m_nCharWidth, m_nLineHeight, and m_strFaceName.
SECEditController::UpdateGutterIndexFont()
After you modify the SECEditInfo object, you must call UpdateGutterIndexFont() to apply the changes.
For example, if pCtlr is a pointer to an SECEditController object, the following code modifies the index text font:
 
SECEditFontInfo* pInfo = pCtlr->GetGutterIndexFont();
pInfo->m_nCharWidth = 6;
pInfo->m_nLineHeight = 12;
pInfo->m_strFaceName = _T("Arial");
pCtlr->UpdateGutterIndexFont();
To change additional font information, you must override the virtual function UpdateGutterIndexFont().
Changing the Step Value and Format
The numbers on the gutter bar usually advance by one:
 
00001
00002
It is possible to change the step to any value, for example:
 
00010
00020
00030
It is also possible to change the format of the numbers to:
 
0x010
0x020
Accomplish both of these tasks by overriding the same virtual function:
 
CString SECEditController::GetGutterIndexText(int nLine) const
{
CString s;
s.Format(_T("%d"), nLine);
//Determine Digits
int nDigit = 0;
for(int n = nLine;
n > 0; n/=10)
nDigit++;
 
//Line Number of 0 has 1 digit
if (nDigit == 0)
nDigit = 1;
 
int nMaxDigit = GetGutterIndexDigits();
//ASSERT(nDigit <= nMaxDigit)
 
//Pad Zero
for (int i = 0; i &lt; (nMaxDigit - nDigit); i++)
s = _T("0") + s;
 
return s;
}
You can change the ''//PadZero'' routine to:
 
//Pad Zero
s = _T(''0x'');
for (int i = 0; i &lt; (nMaxDigit - nDigit-3); i++)
s = _T("0") + s;
s = s + _T(''0'');
To override GetGutterIndexText() in an SECEditCtrl-derived class:
1. Create a class derived from SECEditController. Let’s call it CMyController.
2. In the SECEditCtrl-derived class you can override the CreateController() function:
 
BOOL CMySECEditCtrl::CreateController()
{
m_pCtlr = new CMyController;
m_bAutoDelCtlr = TRUE;
 
return (m_pCtlr != NULL);
}
3. Override GetGutterIndexText() in CMyController.
Allow Toggling Between Normal Gutter and Line Numbers
When overriding the DrawMarks() function, you need to keep track of the gutter width if you are going to resize it to draw other things.
If you want to allow the user to toggle between a normal gutter and line numbers, take a look at this sample code from the JEDPlus freeware application, which does just that:
NOTE >> Java is not one of the languages supported by Objective Edit. Support for C++, C#, HTML, XML, VB, VBScript, and JavaScript is standard. Other languages (including Java) require customization.
 
int CJavaEditView::InitView()
{
SECEditView::InitView();
//Keep track of the default size of the gutter area
nCacheMarkSpace = m_iMarkSpace;
return 0;
}
void CJavaEditView::DrawMarks(CPaintDC *dc, RECT *rect, PLINEDESC pLine)
{
if(!m_bViewLines)
{
//Do the stock bookmark painting
SECEditView::DrawMarks(dc, rect, pLine);
return;
}
//Paint line numbers in the gutter
CString strLineNum = "";
strLineNum.Format("%04d", pLine->iLineNo);
//Use the same font in the gutter as the view
CFont* pEditorFont = GetFont();
CFont* pOldFont = dc->SelectObject(pEditorFont);
int nBkMode = dc->SetBkMode(TRANSPARENT);
if (pLine->fBookMark)
{
//Draw line number as blue if there
//is a bookmark on this line
COLORREF cr = dc->SetTextColor(RGB(0,0,255));
dc->TextOut(rect->left, rect->top, strLineNum);
dc->SetTextColor(cr);
}
else
{
dc->TextOut(rect->left, rect->top, strLineNum);
}
dc->SelectObject(pOldFont);
dc->SetBkMode(nBkMode);
}
void CJavaEditView::OnViewLineNumbers()
{
m_bViewLines = (!m_bViewLines);
//Resize the gutter area to accomodate line numbers
//or back to normal gutter size.
if(m_bViewLines)
{
m_iMarkSpace = (nCacheMarkSpace * 2) + (nCacheMarkSpace / 2);
}
else
{
m_iMarkSpace = nCacheMarkSpace;
}
//Force repaint to refresh gutter
Invalidate(TRUE);
//reposition cursor
MakeCursorVisible();
}
void CJavaEditView::OnUpdateViewLineNumbers(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_bViewLines);
}
Changing the Width of the Gutter
SECEditController::SetGutterWidth(int nWidth)
Use this function to change the mark region width to nWidth. The default value is 20.
SECEditController::GetGutterWidth()
Returns the width (in logical units) of the gutter on the left side of the viewport.
Changing Gutter Painting
If you want to change the way the gutter paints, you'll need to reinitialize the gutter brush. Remember that the gutter is actually painted by the CBrush m_pGutterBrush. It is initialized in SECEditViewport::CreateGutterBrush() and it defaults to using a patterned brush using the IDB_SECEDIT_GUTTER bitmap resource. The overridden function can be added in your SECEditView-derived class.
NOTE >> Since CreateGutterBrush() is protected and not a virtual function, you will need to add a copy of OnCreate() in your View class as well.
Here is a example of how to use a custom defined IDB_CUSTOM_PATTERN bitmap to replace the default bitmap. We are using the SDI sample implementation for this demonstration:
 
BOOL CSdiView::OnCreate()
{
if(!MvcViewport::OnCreate())
{
TRACE0("MvcViewport::OnCreate failed\n");
return FALSE;
}
 
// Set up the Gutter Bitmap
CreateGutterBrush();
 
return TRUE;
}
 
void CSdiView::CreateGutterBrush()
{
if(m_pGutterBrush)
{
delete m_pGutterBrush;
}
 
m_pGutterBrush = new CBrush;
 
// Load the gutter bitmap
HANDLE hBitmap = ::LoadImage(AfxFindResourceHandle
(MAKEINTRESOURCE(IDB_CUSTOM_PATTERN), RT_BITMAP),
MAKEINTRESOURCE(IDB_CUSTOM_PATTERN),
IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
 
// If this ASSERT fires the most common cause is not properly
// including the Objective Edit resources into your project.
// To do this, open the view menu, and select View->Resource
// Includes. In the Read-only symbol directives edit control,
// add: #include "seceditres.h" at the bottom.
// In the Compile-time directives, add #include "seceditres.rc"
// at the bottom.
 
ASSERT (hBitmap != NULL);
CBitmap bm;
bm.Attach(hBitmap);
 
m_pGutterBrush->CreatePatternBrush(&bm);
}
Changing the Background Color of the Gutter Area
The background color for text appearing in the gutter area is not derived from the gutter text font. Instead, it is derived from the background color specified for the gutter area. To customize the gutter background color, override the SECEditViewport::RenderGutter() method.
SECEditViewport::RenderGutter()
Fills the background of the gutter area to the left of the editing window in preparation for drawing any gutter marks. When called by the Objective Edit framework, pDC (see code below) is a pointer to an SECMemDC that is a wrapper for a back buffer DC.
The following code segment is taken from the Gutter sample project. It demonstrates how to change the gutter background color by overriding the RenderGutter() method. Figure 20 shows the result of the following code, which specifies a white background and draws a line down the right side of the gutter area.
 
void CGutterView::RenderGutter(CDC* pDC, const CRect& rcGutter) const
{
pDC->SetBkColor(RGB(255, 255, 255));
pDC->FillSolidRect(&rcGutter, RGB(255,255,255));
// Draw a solid line down the right
pDC->SetTextColor(RGB(0, 0, 0));
pDC->MoveTo(rcGutter.right - 1, rcGutter.top);
pDC->LineTo(rcGutter.right - 1, rcGutter.bottom);
}
Figure 20 – Index Region on Left of Gutter and Mark Region (with Breakpoints) on Right
Drawing Custom Breakpoints and Bookmarks
Figure 21 shows how to identify the default appearance of breakpoints and bookmarks.
Figure 21 – Breakpoints (circles) and Bookmarks (rounded rectangles) in the Gutter
 
When it comes to drawing the gutter (and items in the gutter), the SECEditViewport methods involved include:
DrawGutter()
Draws the gutter area to the left of the editing window and then calls the DrawMarks() function to draw any marks that may be in the gutter.
 
/* Draws the gutter area and any gutter marks */
virtual void DrawGutter(CRect rcInvalid) const;
DrawMarks()
Called to draw any items (such as breakpoints) in the gutter. Override it to do custom processing.
Table 5 defines the arguments associated with the DrawMarks() method.
Table 5 – Arguments for SECEditViewport::DrawMarks() and Related Methods 
Argument
Purpose
nLine
The line index for which to draw the marks.
pDC
A pointer to an SECMemDC that is a wrapper for a back buffer DC. (The Device Context on which to draw the marks.)
rcMark
The CRect in which we should render the mark.
By default, DrawMarks() calls the following functions in this order:
PreDrawMarks()
DrawBookmark()
DrawBreakpoint()
PostDrawMarks()
 
/* Called to draw the items in the gutter */
virtual void DrawMarks(int nLine, CDC* pDC, const CRect& rcMark) const;
PreDrawMarks()
Is called before any of the predefined gutter marks are drawn. Override it to provide custom drawing of marks in the gutter.
 
/* Called before any items are drawn in the gutter */
virtual void PreDrawMarks(int nLine, CDC* pDC, CRect rcMark) const;
DrawBookmark()
Draws a bookmark in the gutter for lines that have a bookmark. Override it to draw a custom bookmark.
 
/* Draws a Bookmark in the gutter */
virtual void DrawBookmark(int nLine, CDC* pDC, CRect rcMark) const;
DrawBreakpoint()
Draws a breakpoint in the gutter for lines that have a breakpoint. Override it to draw a custom breakpoint.
 
/* Draws a Breakpoint in the gutter */
virtual void DrawBreakpoint(int nLine, CDC* pDC, CRect rcMark) const;
PostDrawMarks()
Called after the predefined items in the gutter (such as breakpoints) are drawn. Override it to provide custom drawing of marks in the gutter.
 
/* Called after the items have been drawn in the gutter */
virtual void PostDrawMarks(int nLine, CDC* pDC, CRect rcMark) const;
Figure 22 summarizes the SECEditViewport methods involved in drawing the gutter and gutter objects.
Figure 22 – How the Gutter Gets Drawn
Implementing Three Breakpoint States
Ordinarily you can toggle the breakpoint and clear it. In some cases you might want a third state—deactivating it.
You can set three states (even more in fact) for a break point. To implement three state breakpoints (no breakpoint, breakpoint set, breakpoint set but not active), use another DataFlag value (other than 16 - 18) to define a new data flag. Once you check the breakpoint status, check both flags. With the combination of two booleans, you can have four states from which to choose.
To place a proper indicator in the gutter on the left side of SECEditView-derived views (where the bookmarks and breakpoints indicators appear), define your own Data Item Flag type (with a value 0-15) and override SECEditViewport::DrawMarks(). For example,
1. Define the bookmark:
 
#define ID_ITEMDATA_MYMARK // needs an int value
2. Override DrawMarks():
 
void CMyViewport::DrawMarks(int nLine, CDC* pDC, const CRect& rcMark) const
{
// called before gutter items are drawn
PreDrawMarks(nLine, pDC, rcMark);
// draws bookmark in gutter
DrawBookmark(nLine, pDC, rcMark);
//draws breakpoint in the gutter
DrawBreakpoint(nLine, pDC, rcMark);
DrawMyMark(nLine, pDC, rcMark);
// called after gutter items have been drawn
PostDrawMarks(nLine, pDC, rcMark);
}
3. Add a function implementation for DrawMyMark().
4. From then on, you can add this new mark to any line by calling:
 
SECEdit::SetItemDataFlag(int nLine, unsigned int nFlag, BOOL bAdd);
Inserting Bookmarks vs. Marking Found Text
Suppose that by pressing <Ctrl>+<F2> you can insert a bookmark into a file and by choosing Mark All in the Find in Files dialog, you can mark all occurrences of the given text. The problem is that both procedures set the line flag to ID_SECEDIT_ITEMDATA_BOOKMARK.
You can customize this to enable differentiating between inserting bookmarks and marking found text occurrences.
1. Register new resource, say ID_SECEDIT_ITEMDATA_MULTIFIND.
2. Override virtualBOOL SECEditController::OnMarkAll(_SEC_FIND_REPLACE_STATE* pState), replacing ID_SECEDIT_ITEMDATA_BOOKMARK with ID_SECEDIT_ITEMDATA_MULTIFIND in call pEdit->SetItemDataFlag(nResultLine, ID_SECEDIT_ITEMDATA_BOOKMARK, TRUE);
3. Override virtual void SECEditViewport::DrawBookmark(int nLine, CDC* pDC, CRect rcMark) const, replacing resource ID and action. For instance, you can change the color of bookmarks.
Methods Related to the Gutter
Table 6 and Table 7 provide a handy reference for functions that can be used to manipulate the gutter. For additional information about these methods, please refer to the Objective Edit Class Reference.
Table 6 – SECEditController Methods Related to the Gutter 
Method
Definition
SetGutterAlignment()
Sets the gutter format according to the nAlign value (e.g. mark region only, index only, or both).
GetGutterAlignment()
Retrieves the alignment style for the mark and index regions of the gutter.
SetGutterWidth()
Sets the width of the gutter’s mark region (in pixels). The mark region is where bookmarks and breakpoints are displayed.
GetGutterWidth()
Returns the width (in logical units) of the gutter on the left side of the viewport.
GetGutterMarkWidth()
Retrieves the width of the mark region of the gutter (in pixels). The mark region is where breakpoints and bookmarks are displayed.
SetGutterIndexWidth()
Sets the index region of the gutter (in pixels). The index region is where line numbers are displayed.
GetGutterIndexWidth()
Retrieves the index region of the gutter (in pixels). The index region is where line numbers are displayed. The value -1 indicates that the width is being managed automatically.
SetGutterIndexDigits()
Sets the number of numerical digits the index area of the gutter can display. A value of -1 indicates that this number will be managed automatically.
GetGutterIndexDigits()
Retrieves the number of numerical digits the index area of the gutter can display.
SetGutterIndexColor()
Sets the index text color.
GetGutterIndexColor()
Retrieves the index text color.
GetGutterIndexFont()
Returns a pointer to the SECEditFontInfo object, which defines font information for the index region.
UpdateGutterIndexFont()
This function must be called to update the font for the gutter region after making changes to the m_pGutterIndexFont member.
GetGutterIndexText()
Retrieves the number displayed in the index area of gutter as text.
Table 7 – SECEditViewport Methods Related to the Gutter 
Method
Definition
CreateGutterBrush()
Sets up the gutter bitmap.
GetGutterBrush()
Retrieves the brush used to paint the gutter.
DrawGutter()
Draws the gutter area to the left of the editing window and then calls the DrawMarks() function to draw any marks that may be in the gutter.
RenderGutter()
Fills the background of the gutter area.
DrawMarks()
Is called to draw any items in the gutter (such as breakpoints). The default implementation calls the next four functions in the order they appear.
PreDrawMarks()
Is called before any items in the gutter (such as breakpoints) are drawn.
DrawBookmark()
Draws a bookmark in the gutter for lines that have a bookmark.
DrawBreakpoint()
Draws a breakpoint in the gutter for lines that have a breakpoint.
PostDrawMarks()
Is called after the predefined items in the gutter (such as breakpoints) are drawn.