File size: 17,909 Bytes
18d1597
 
 
 
 
 
 
 
 
 
 
 
 
38fdd3d
18d1597
 
38fdd3d
 
18d1597
 
 
 
38fdd3d
18d1597
 
 
 
 
 
 
 
 
 
 
 
 
 
38fdd3d
18d1597
 
 
 
38fdd3d
 
 
 
 
 
 
 
 
 
 
 
 
 
18d1597
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38fdd3d
 
 
 
 
18d1597
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38fdd3d
 
18d1597
 
 
 
 
 
 
 
38fdd3d
 
 
18d1597
 
 
38fdd3d
 
18d1597
 
 
 
 
 
 
 
 
 
38fdd3d
 
18d1597
38fdd3d
 
18d1597
38fdd3d
18d1597
38fdd3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18d1597
 
 
 
 
 
38fdd3d
 
 
18d1597
38fdd3d
 
18d1597
 
 
38fdd3d
 
18d1597
 
 
38fdd3d
18d1597
38fdd3d
 
18d1597
38fdd3d
18d1597
38fdd3d
 
 
 
 
 
18d1597
38fdd3d
18d1597
38fdd3d
 
 
 
 
 
 
18d1597
38fdd3d
 
 
 
 
 
 
18d1597
38fdd3d
 
 
 
 
 
18d1597
38fdd3d
 
 
 
18d1597
38fdd3d
18d1597
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38fdd3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18d1597
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38fdd3d
 
 
 
 
 
 
18d1597
38fdd3d
18d1597
 
 
 
 
 
38fdd3d
18d1597
38fdd3d
 
18d1597
 
 
 
 
38fdd3d
 
18d1597
 
 
 
 
 
 
 
 
 
 
38fdd3d
 
 
 
 
 
18d1597
 
 
 
38fdd3d
 
18d1597
 
 
 
 
38fdd3d
 
 
 
 
 
18d1597
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38fdd3d
 
18d1597
38fdd3d
 
 
 
18d1597
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
Attribute VB_Name = "DuplicateImageReview"
Option Explicit

' Worksheet layout
Private Const FIRST_DATA_ROW As Long = 2
Private Const FIRST_IMAGE_COL As Long = 2
Private Const LAST_IMAGE_COL As Long = 10

Private Const MANUAL_SHEET As String = "Manual"
Private Const WRITEBUFFER_SHEET As String = "WriteBuffer"
Private Const HIGHLIGHT_MARKER As String = "DUPLICATE_IMAGE_LINK_HIGHLIGHT"
Private Const HIGHLIGHT_BATCH_SIZE As Long = 100

' Button 1: detect column + row duplicate URLs in B:J.
Public Sub DetectDuplicateImageLinks()
    Dim targetSheet As Worksheet
    Dim columnGroupCount As Long
    Dim rowGroupCount As Long
    Dim previousCalculation As XlCalculation
    Dim previousScreenUpdating As Boolean
    Dim previousEnableEvents As Boolean
    Dim settingsSaved As Boolean
    Dim summary As String

    On Error GoTo ErrorHandler

    Set targetSheet = GetTargetWorksheet()
    If targetSheet Is Nothing Then
        MsgBox "This macro can only run on the Manual or WriteBuffer worksheet.", _
               vbExclamation, "Duplicate Image Links"
        Exit Sub
    End If

    SaveExcelSettings previousCalculation, previousScreenUpdating, previousEnableEvents
    settingsSaved = True
    EnablePerformanceMode

    RunDuplicateDetection targetSheet, columnGroupCount, rowGroupCount

    RestoreExcelSettings previousCalculation, previousScreenUpdating, previousEnableEvents
    settingsSaved = False

    If columnGroupCount + rowGroupCount = 0 Then
        summary = "Duplicate scan completed." & vbCrLf & vbCrLf & _
                  "Column duplicate groups: 0" & vbCrLf & _
                  "Row duplicate groups: 0" & vbCrLf & vbCrLf & _
                  "No duplicates detected."
    Else
        summary = "Duplicate scan completed." & vbCrLf & vbCrLf & _
                  "Column duplicate groups: " & Format$(columnGroupCount, "#,##0") & vbCrLf & _
                  "Row duplicate groups: " & Format$(rowGroupCount, "#,##0") & vbCrLf & vbCrLf & _
                  "Total duplicate groups: " & _
                  Format$(columnGroupCount + rowGroupCount, "#,##0")
    End If

    MsgBox summary, vbInformation, "Duplicate Image Links"
    Exit Sub

ErrorHandler:
    If settingsSaved Then
        RestoreExcelSettings previousCalculation, previousScreenUpdating, previousEnableEvents
    End If
    MsgBox "Duplicate detection could not be completed." & vbCrLf & _
           Err.Description, vbCritical, "Duplicate Image Links"
End Sub

' Button 2: remove duplicates only within each individual row.
Public Sub RemoveRowDuplicates()
    Dim targetSheet As Worksheet
    Dim imageData As Variant
    Dim lastDataRow As Long
    Dim dataRowIndex As Long
    Dim duplicateRowsRemoved As Long
    Dim rowGroups As Long
    Dim rowLinks As Long
    Dim columnGroupCount As Long
    Dim rowGroupCount As Long
    Dim previousCalculation As XlCalculation
    Dim previousScreenUpdating As Boolean
    Dim previousEnableEvents As Boolean
    Dim settingsSaved As Boolean

    On Error GoTo ErrorHandler

    Set targetSheet = GetTargetWorksheet()
    If targetSheet Is Nothing Then
        MsgBox "This macro can only run on the Manual or WriteBuffer worksheet.", _
               vbExclamation, "Remove Row Duplicates"
        Exit Sub
    End If

    SaveExcelSettings previousCalculation, previousScreenUpdating, previousEnableEvents
    settingsSaved = True
    EnablePerformanceMode

    lastDataRow = GetLastRow(targetSheet)
    If lastDataRow >= FIRST_DATA_ROW Then
        imageData = targetSheet.Range( _
            targetSheet.Cells(FIRST_DATA_ROW, FIRST_IMAGE_COL), _
            targetSheet.Cells(lastDataRow, LAST_IMAGE_COL)).Value2

        For dataRowIndex = 1 To UBound(imageData, 1)
            CompressRow imageData, dataRowIndex, rowGroups, rowLinks
            If rowLinks > 0 Then duplicateRowsRemoved = duplicateRowsRemoved + 1
        Next dataRowIndex

        ' Assigning values preserves all cell formatting.
        targetSheet.Range( _
            targetSheet.Cells(FIRST_DATA_ROW, FIRST_IMAGE_COL), _
            targetSheet.Cells(lastDataRow, LAST_IMAGE_COL)).Value2 = imageData
    End If

    ' Refresh highlights without showing the Detect Duplicates popup.
    RunDuplicateDetection targetSheet, columnGroupCount, rowGroupCount

    RestoreExcelSettings previousCalculation, previousScreenUpdating, previousEnableEvents
    settingsSaved = False

    MsgBox "Duplicate rows removed: " & Format$(duplicateRowsRemoved, "#,##0"), _
           vbInformation, "Remove Row Duplicates"
    Exit Sub

ErrorHandler:
    If settingsSaved Then
        RestoreExcelSettings previousCalculation, previousScreenUpdating, previousEnableEvents
    End If
    MsgBox "Row duplicates could not be removed." & vbCrLf & _
           Err.Description, vbCritical, "Remove Row Duplicates"
End Sub

' Clear previous highlights, then scan and highlight column + row duplicate groups.
Private Sub RunDuplicateDetection( _
    ByVal targetSheet As Worksheet, _
    ByRef columnGroupCount As Long, _
    ByRef rowGroupCount As Long)

    Dim lastDataRow As Long
    Dim imageData As Variant
    Dim highlightGroups As Object
    Dim groupOrder As Collection
    Dim usedColors As Object
    Dim groupKey As Variant
    Dim occurrenceList As Collection
    Dim fillColor As Long
    Dim highlightIndex As Long

    columnGroupCount = 0
    rowGroupCount = 0

    ClearDuplicateHighlights targetSheet

    lastDataRow = GetLastRow(targetSheet)
    If lastDataRow < FIRST_DATA_ROW Then Exit Sub

    imageData = targetSheet.Range( _
        targetSheet.Cells(FIRST_DATA_ROW, FIRST_IMAGE_COL), _
        targetSheet.Cells(lastDataRow, LAST_IMAGE_COL)).Value2

    Set highlightGroups = CreateObject("Scripting.Dictionary")
    highlightGroups.CompareMode = vbTextCompare
    Set groupOrder = New Collection

    CountAndCollectDuplicates imageData, highlightGroups, groupOrder, _
                              columnGroupCount, rowGroupCount

    Set usedColors = CreateObject("Scripting.Dictionary")

    For Each groupKey In groupOrder
        Set occurrenceList = highlightGroups.Item(CStr(groupKey))
        If occurrenceList.Count > 1 Then
            highlightIndex = highlightIndex + 1
            fillColor = GetGroupColor(highlightIndex, usedColors)
            HighlightOccurrences targetSheet, occurrenceList, fillColor, highlightIndex
        End If
    Next groupKey
End Sub

' Column group = same URL in 2+ distinct rows.
' Row group = same URL 2+ times in one row.
' Highlight map is keyed by URL so every occurrence shares one colour.
Private Sub CountAndCollectDuplicates( _
    ByVal imageData As Variant, _
    ByVal highlightGroups As Object, _
    ByVal groupOrder As Collection, _
    ByRef columnGroupCount As Long, _
    ByRef rowGroupCount As Long)

    Dim urlRows As Object
    Dim urlRowSet As Object
    Dim rowUrlCounts As Object
    Dim dataRowIndex As Long
    Dim dataColumnIndex As Long
    Dim worksheetRow As Long
    Dim worksheetColumn As Long
    Dim normalizedUrl As String
    Dim cellValue As Variant
    Dim occurrenceList As Collection
    Dim urlKey As Variant
    Dim countKey As Variant

    Set urlRows = CreateObject("Scripting.Dictionary")
    urlRows.CompareMode = vbTextCompare

    For dataRowIndex = 1 To UBound(imageData, 1)
        worksheetRow = dataRowIndex + FIRST_DATA_ROW - 1
        Set rowUrlCounts = CreateObject("Scripting.Dictionary")
        rowUrlCounts.CompareMode = vbTextCompare

        For dataColumnIndex = 1 To UBound(imageData, 2)
            cellValue = imageData(dataRowIndex, dataColumnIndex)
            If IsError(cellValue) Then GoTo NextCell

            normalizedUrl = NormalizeUrl(cellValue)
            If Len(normalizedUrl) = 0 Then GoTo NextCell

            worksheetColumn = dataColumnIndex + FIRST_IMAGE_COL - 1

            If Not highlightGroups.Exists(normalizedUrl) Then
                Set occurrenceList = New Collection
                highlightGroups.Add normalizedUrl, occurrenceList
                groupOrder.Add normalizedUrl
            Else
                Set occurrenceList = highlightGroups.Item(normalizedUrl)
            End If
            occurrenceList.Add Array(worksheetRow, worksheetColumn)

            If Not urlRows.Exists(normalizedUrl) Then
                Set urlRowSet = CreateObject("Scripting.Dictionary")
                urlRows.Add normalizedUrl, urlRowSet
            Else
                Set urlRowSet = urlRows.Item(normalizedUrl)
            End If
            urlRowSet(CStr(worksheetRow)) = True

            If rowUrlCounts.Exists(normalizedUrl) Then
                rowUrlCounts(normalizedUrl) = CLng(rowUrlCounts(normalizedUrl)) + 1
            Else
                rowUrlCounts.Add normalizedUrl, 1
            End If
NextCell:
        Next dataColumnIndex

        For Each countKey In rowUrlCounts.Keys
            If CLng(rowUrlCounts(countKey)) > 1 Then
                rowGroupCount = rowGroupCount + 1
            End If
        Next countKey
    Next dataRowIndex

    For Each urlKey In urlRows.Keys
        Set urlRowSet = urlRows.Item(urlKey)
        If urlRowSet.Count > 1 Then
            columnGroupCount = columnGroupCount + 1
        End If
    Next urlKey
End Sub

' Batch noncontiguous cells to avoid one rule per duplicate cell.
Private Sub HighlightOccurrences( _
    ByVal targetSheet As Worksheet, _
    ByVal occurrenceList As Collection, _
    ByVal fillColor As Long, _
    ByVal groupNumber As Long)

    Dim occurrence As Variant
    Dim batchRange As Range
    Dim occurrenceCell As Range
    Dim batchCount As Long

    For Each occurrence In occurrenceList
        Set occurrenceCell = targetSheet.Cells( _
            CLng(occurrence(LBound(occurrence))), _
            CLng(occurrence(UBound(occurrence))))

        If batchRange Is Nothing Then
            Set batchRange = occurrenceCell
        Else
            Set batchRange = Application.Union(batchRange, occurrenceCell)
        End If

        batchCount = batchCount + 1
        If batchCount >= HIGHLIGHT_BATCH_SIZE Then
            AddHighlightRule batchRange, fillColor, groupNumber
            Set batchRange = Nothing
            batchCount = 0
        End If
    Next occurrence

    If Not batchRange Is Nothing Then
        AddHighlightRule batchRange, fillColor, groupNumber
    End If
End Sub

' Conditional formatting preserves the cells' permanent formatting.
Private Sub AddHighlightRule( _
    ByVal targetRange As Range, _
    ByVal fillColor As Long, _
    ByVal groupNumber As Long)

    Dim highlightRule As Object
    Dim markerFormula As String

    markerFormula = "=N(""" & HIGHLIGHT_MARKER & _
                    "_" & CStr(groupNumber) & """)=0"

    Set highlightRule = targetRange.FormatConditions.Add( _
        Type:=xlExpression, Formula1:=markerFormula)

    highlightRule.Interior.Color = fillColor
    highlightRule.StopIfTrue = False
End Sub

' Remove only conditional-format rules created by this module.
Public Sub ClearDuplicateHighlights(ByVal targetSheet As Worksheet)
    Dim formatIndex As Long
    Dim formatRule As Object

    If targetSheet Is Nothing Then Exit Sub

    For formatIndex = targetSheet.Cells.FormatConditions.Count To 1 Step -1
        Set formatRule = targetSheet.Cells.FormatConditions(formatIndex)
        If IsDuplicateHighlightRule(formatRule) Then formatRule.Delete
    Next formatIndex
End Sub

' Drop duplicate highlight rules when product data is edited or cleared.
Public Sub OnDataAreaChanged( _
    ByVal targetSheet As Worksheet, _
    ByVal changedRange As Range)

    Dim dataRange As Range

    If targetSheet Is Nothing Or changedRange Is Nothing Then Exit Sub
    If Not IsTargetWorksheet(targetSheet) Then Exit Sub

    Set dataRange = Intersect( _
        changedRange, _
        targetSheet.Range( _
            targetSheet.Cells(FIRST_DATA_ROW, 1), _
            targetSheet.Cells(targetSheet.Rows.Count, LAST_IMAGE_COL)))

    If Not dataRange Is Nothing Then ClearDuplicateHighlights targetSheet
End Sub

Private Function IsDuplicateHighlightRule( _
    ByVal formatRule As Object) As Boolean

    Dim formulaText As String
    On Error GoTo NotDuplicateRule

    If TypeName(formatRule) <> "FormatCondition" Then Exit Function
    If formatRule.Type <> xlExpression Then Exit Function

    formulaText = CStr(formatRule.Formula1)
    IsDuplicateHighlightRule = _
        (InStr(1, formulaText, HIGHLIGHT_MARKER, vbTextCompare) > 0)
    Exit Function

NotDuplicateRule:
    IsDuplicateHighlightRule = False
End Function

' Deduplicate and compact one row. Returns row-group and link-removal counts.
Private Sub CompressRow( _
    ByRef imageData As Variant, _
    ByVal dataRowIndex As Long, _
    ByRef rowGroupsRemoved As Long, _
    ByRef linksRemoved As Long)

    Dim seenUrls As Object
    Dim urlCounts As Object
    Dim retainedValues() As Variant
    Dim imageColumnCount As Long
    Dim inputColumn As Long
    Dim outputColumn As Long
    Dim cellValue As Variant
    Dim normalizedUrl As String
    Dim countKey As Variant

    rowGroupsRemoved = 0
    linksRemoved = 0
    imageColumnCount = LAST_IMAGE_COL - FIRST_IMAGE_COL + 1
    ReDim retainedValues(1 To imageColumnCount)

    Set seenUrls = CreateObject("Scripting.Dictionary")
    seenUrls.CompareMode = vbTextCompare
    Set urlCounts = CreateObject("Scripting.Dictionary")
    urlCounts.CompareMode = vbTextCompare

    For inputColumn = 1 To imageColumnCount
        cellValue = imageData(dataRowIndex, inputColumn)

        If IsError(cellValue) Then
            outputColumn = outputColumn + 1
            retainedValues(outputColumn) = cellValue
        Else
            normalizedUrl = NormalizeUrl(cellValue)

            If Len(normalizedUrl) > 0 Then
                If urlCounts.Exists(normalizedUrl) Then
                    urlCounts(normalizedUrl) = CLng(urlCounts(normalizedUrl)) + 1
                Else
                    urlCounts.Add normalizedUrl, 1
                End If

                If Not seenUrls.Exists(normalizedUrl) Then
                    seenUrls.Add normalizedUrl, True
                    outputColumn = outputColumn + 1
                    retainedValues(outputColumn) = cellValue
                Else
                    linksRemoved = linksRemoved + 1
                End If
            End If
        End If
    Next inputColumn

    For Each countKey In urlCounts.Keys
        If CLng(urlCounts(countKey)) > 1 Then
            rowGroupsRemoved = rowGroupsRemoved + 1
        End If
    Next countKey

    For inputColumn = 1 To imageColumnCount
        If inputColumn <= outputColumn Then
            imageData(dataRowIndex, inputColumn) = retainedValues(inputColumn)
        Else
            imageData(dataRowIndex, inputColumn) = Empty
        End If
    Next inputColumn
End Sub

Private Function GetGroupColor( _
    ByVal groupNumber As Long, _
    ByVal usedColors As Object) As Long

    Dim colorPalette As Variant
    Dim candidateColor As Long
    Dim calculatedColor As Double
    Dim colorKey As String

    colorPalette = Array( _
        RGB(255, 235, 59), RGB(129, 199, 132), _
        RGB(100, 181, 246), RGB(255, 183, 77), _
        RGB(244, 143, 177), RGB(171, 71, 188), _
        RGB(77, 208, 225), RGB(174, 213, 129), _
        RGB(255, 138, 101), RGB(159, 168, 218), _
        RGB(255, 202, 40), RGB(38, 166, 154))

    If groupNumber <= UBound(colorPalette) + 1 Then
        candidateColor = colorPalette(groupNumber - 1)
    Else
        calculatedColor = groupNumber * 2654435761#
        calculatedColor = calculatedColor - _
            Int(calculatedColor / 16777216#) * 16777216#
        candidateColor = CLng(calculatedColor)
    End If

    colorKey = CStr(candidateColor)
    Do While usedColors.Exists(colorKey)
        candidateColor = candidateColor + 1
        If candidateColor > 16777215 Then candidateColor = 0
        colorKey = CStr(candidateColor)
    Loop

    usedColors.Add colorKey, True
    GetGroupColor = candidateColor
End Function

Private Function GetLastRow(ByVal targetSheet As Worksheet) As Long
    GetLastRow = targetSheet.Cells( _
        targetSheet.Rows.Count, 1).End(xlUp).Row
End Function

Private Function GetTargetWorksheet() As Worksheet
    If TypeName(ActiveSheet) <> "Worksheet" Then Exit Function
    If IsTargetWorksheet(ActiveSheet) Then Set GetTargetWorksheet = ActiveSheet
End Function

Private Function IsTargetWorksheet(ByVal targetSheet As Worksheet) As Boolean
    IsTargetWorksheet = _
        (StrComp(targetSheet.Name, MANUAL_SHEET, vbTextCompare) = 0) Or _
        (StrComp(targetSheet.Name, WRITEBUFFER_SHEET, vbTextCompare) = 0)
End Function

Private Function NormalizeUrl(ByVal cellValue As Variant) As String
    On Error GoTo NormalizeFailed

    If IsError(cellValue) Or IsEmpty(cellValue) Or IsNull(cellValue) Then
        Exit Function
    End If

    If VarType(cellValue) = vbObject Or VarType(cellValue) = vbDataObject Then
        Exit Function
    End If

    NormalizeUrl = LCase$(Trim$(CStr(cellValue)))
    Exit Function

NormalizeFailed:
    NormalizeUrl = vbNullString
End Function

Private Sub SaveExcelSettings( _
    ByRef calculationMode As XlCalculation, _
    ByRef screenUpdating As Boolean, _
    ByRef enableEvents As Boolean)

    calculationMode = Application.Calculation
    screenUpdating = Application.ScreenUpdating
    enableEvents = Application.EnableEvents
End Sub

Private Sub EnablePerformanceMode()
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Application.Calculation = xlCalculationManual
End Sub

Private Sub RestoreExcelSettings( _
    ByVal calculationMode As XlCalculation, _
    ByVal screenUpdating As Boolean, _
    ByVal enableEvents As Boolean)

    Application.Calculation = calculationMode
    Application.ScreenUpdating = screenUpdating
    Application.EnableEvents = enableEvents
End Sub