I'm trying to get a selection of shapes in order from right to left. I found a routine by John Wilson on vbaexpress on which I based my code.
The sorting works perfectly when I select item by item by clicking on the shapes but it doesn't respect the "visible order" of shapes if I select them by "lassoing" with my mouse.
In case of dragging my mouse over the shapes to select them, the routine should respect the visible order of shapes.
Thanks in advance.
Sub AlignFlush()
Dim oshpR As ShapeRange
Dim oshp As Shape
Dim L As Long
Dim rayPOS() As Single
Set oshpR = ActiveWindow.Selection.ShapeRange
ReDim rayPOS(1 To oshpR.Count)
'add to array
For L = 1 To oshpR.Count
rayPOS(L) = oshpR(L).Left
Next L
'sort
Call sortray(rayPOS)
'apply
For L = 1 To oshpR.Count
If L = 1 Then
Set oshp = Windows(1).Selection.ShapeRange(1)
PosTop = oshp.Top
PosNext = oshp.Left + oshp.Width
Else
Set oshp = Windows(1).Selection.ShapeRange(L)
oshp.Top = PosTop
oshp.Left = PosNext
PosNext = oshp.Left + oshp.Width
End If
Next L
End Sub
Sub sortray(ArrayIn As Variant)
Dim b_Cont As Boolean
Dim lngCount As Long
Dim vSwap As Long
Do
b_Cont = False
For lngCount = LBound(ArrayIn) To UBound(ArrayIn) - 1
If ArrayIn(lngCount) > ArrayIn(lngCount + 1) Then
vSwap = ArrayIn(lngCount)
ArrayIn(lngCount) = ArrayIn(lngCount + 1)
ArrayIn(lngCount + 1) = vSwap
b_Cont = True
End If
Next lngCount
Loop Until Not b_Cont
End Sub
Some comments on your existing code:
Array counts always start at 0 unless you use the Option Base statement to set it to a different number.
When you use ReDim, most of the time, you want to use the Preserve keyword, or the ReDim obliterates the existing array contents. But in this case, we know the array size ahead of time, so Preserve is not necessary.
You call sortray, but didn't include it in your listing. I've added a sorting routine.
But then you make no use of the sorted array in the section where you position the shapes.
Working macro (based on your description of what you mean by "visible order" being the left-to-right sequence):
Since you use the left position of the leftmost shape to apply to the others, here's a simpler way to do that:
Sub AlignFlushLeftWithLeftmostShape()
Dim ShpCount As Long
Dim oshpR As ShapeRange
Dim L As Long
Dim rayPOS() As Single
Set oshpR = ActiveWindow.Selection.ShapeRange
ShpCount = oshpR.Count
ReDim rayPOS(ShpCount - 1)
For L = 0 To ShpCount - 1
rayPOS(L) = oshpR(L + 1).Left
Next L
Call BubbleSort(rayPOS)
For x = 1 To ShpCount
oshpR(x).Left = rayPOS(0)
Next x
End Sub
Sub BubbleSort(arr)
Dim lTemp As Long
Dim i As Long
Dim j As Long
Dim lngMin As Long
Dim lngMax As Long
lngMin = LBound(arr)
lngMax = UBound(arr)
For i = lngMin To lngMax - 1
For j = i + 1 To lngMax
If arr(i) > arr(j) Then
lTemp = arr(i)
arr(i) = arr(j)
arr(j) = lTemp
End If
Next j
Next i
End Sub
I am trying to write some code in VBA and PowerPoint to automatically create a poster (basically a box in box view) however, its proving to be a little more challenging than my skills can cope.
looking to get some guidance on how best to structure the code (and patterns)
at the moment:
Im reading the data (see table) from csv, and placing them into 4 in memory arrays (cat, sub-cat, column, intersection) - this is probably bad - but dont know of anything better.
my code tries to process the spacing logic and adjusts the x-and-y pos of each intersection block (grows the category and sub-category blocks when needed) within the arrays, then finally draws/places the blocks on the powerpoint canvas.
You will see in the graphic "initial selected block" at the moment thats the context from which i run -- the idea is to use that block size as the template for the dimensions
Im not worried about the placement, its more the algorithm to determine the spacing (or block size of each of the components
below is a sample of the data that I read:
and once the logic is generated, this is how the "poster" would look
Im happy to share code, its just getting a bit big now (and I feel im missing a trick in terms of simplicity)
code sample included below:
Const defaultObjectWidth = 95
Const defaultObjectHeight = 50
Const defaultCategoryWidth = 90
Const defaultSubCatWidth = 90
Const defaultBuffer = 3
Const blocksPerCol = 4
Const myYAxisWidth = 100
Sub insertShape()
On Error GoTo Err_Handler
Dim SlideHeight As Long, SlideWidth As Long
Dim ContainerHeight As Long, ContainerWidth As Long
Dim ObjectHeight As Long, ObjectWidth As Long
Dim x As Long, y As Long
Dim shp As Object
Dim colWidth As Integer
Dim blockSize As Integer
Dim IntersectCalcComplete As Boolean
IntersectCalcComplete = False
' create the Arrays for storing the data
Dim CategorysArr() As String
Dim SubCatsArr() As String
Dim YAxissArr() As String
Dim IntersectsArr() As String
Dim tmpIntersects() As String
Call LoadSubCats(SubCatsArr)
Call LoadCategorys(CategorysArr)
Call LoadYAxiss(YAxissArr)
Call LoadIntersects(IntersectsArr)
ReDim tmpIntersects(0 To 2, 0 To 0)
If ActiveWindow.Selection.Type = ppSelectionNone Then
MsgBox "Please select object", vbExclamation, "Make Selection"
Else
Set shp = ActiveWindow.Selection.ShapeRange(1)
SlideHeight = ActivePresentation.PageSetup.SlideHeight 'get slide vertical height
SlideWidth = ActivePresentation.PageSetup.SlideWidth 'get slide horizontal width
'Main Content Container width and height
ContainerHeight = shp.Height
ContainerWidth = shp.Width
' calculate the container column sizes.
'
colWidth = calcColumnSizes(shp.Width, CategorysArr, SubCatsArr, YAxissArr)
' calculate the blockwidth based on the density factor parameter
blockWidth = calcBlockWidth(colWidth, blocksPerCol)
' Iterate through the Categorys, SubCats and Intersects in order to calculate the spacing of the Intersects.
' first sort the matricies by a specific column.
' CategorysArr = QuickSortArray(CategorysArr, -1, -1, 4)
' Call multiSortArray(1, CategorysArr, arrFlds(CategorysArr), "2 Asc 5 Asc")
' -- ------------------------------------------------------------------------------------------------------------------------------------------------
Dim SubCatID As Integer
'Dim myYAxisWidth As Integer
Dim SubCatHeight, CategoryHeight, currentX, currentY, n, t As Integer
SubCatHeight = 0
CategoryHeight = 0
currentX = 0
currentY = 0
currentBlockUsed = 0
For i = LBound(SubCatsArr, 2) To UBound(SubCatsArr, 2) ' For each SubCat in the SubCats Array
t = t + 1
'Debug.Print (" processing SubCat: " & t & " called: " & SubCatsArr(1, i))
' Debug.Print (SubCatsArr(i, 1)) ' print the name
SubCatID = SubCatsArr(0, i)
If Not IsNull(SubCatID) Then
' with each SubCat - find all related Intersects and fit them into the canvas
' 1: Filter the Intersects by SubCat
Call getIntersectBySubCat(SubCatID, IntersectsArr, tmpIntersects)
'printArray (tmpIntersects)
' 2: re-structure the Intersects position to fit into the YAxiss allocated
' myYAxisWidth = 200 ' -- get YAxis width TODO: get the real width
' -- we already know what the blockWidth is (above)
SubCatHeight = defaultObjectHeight + defaultBuffer ' set the SubCatheight on the first pass.
For j = LBound(tmpIntersects, 2) To UBound(tmpIntersects, 2) ' Loop through tmp Intersects to add placement
If LBound(tmpIntersects, 2) = -1 Then ' SubCat has no Intersects
' Debug.Print ("SubCat ID:" & SubCatID & " has no Intersects")
Else
If myYAxisWidth < (currentX + blockWidth + (defaultBuffer * 2)) Then ' If there is not enough remaining space in YAxis
currentY = currentY + defaultBuffer + defaultObjectHeight
currentX = 0
SubCatHeight = SubCatHeight + defaultObjectHeight + defaultBuffer
End If
n = findArrStr(IntersectsArr, 0, str(tmpIntersects(0, j)))
IntersectsArr(6, n) = str(currentX + defaultBuffer) ' X CoOrdinate
IntersectsArr(7, n) = str(currentY + defaultBuffer) ' Y CoOrdinate
currentX = currentX + defaultBuffer + defaultObjectWidth
End If
Next j 'filtered Intersect
Else
' Debug.Print ("empty record in Array Detected")
End If
Call setSubCatHeight(SubCatHeight, SubCatID, SubCatsArr, CategorysArr)
SubCatHeight = 0
Next i 'SubCat
' -- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' -- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' ADDING OF THE SHAPES TO THE VIEW
Call addCategorys(CategorysArr, shp)
Call addSubCats(SubCatsArr, shp)
Call addIntersects(IntersectsArr, shp)
' x = (SlideWidth - ObjectWidth) / 2 'calculate horizontal position
' y = (SlideHeight - ObjectHeight) / 2 'calculate vertical position
' shp.Left = x 'move object horizontal
' shp.Top = y 'move object vertical
End If
Exit_Label:
On Error Resume Next
Set shp = Nothing
Exit Sub
Err_Handler:
MsgBox Err.Description, vbCritical
Resume Exit_Label
End Sub
Function getIntersectBySubCat(SubCatID As Integer, ByRef IntersectsArr As Variant, ByRef tmpIntersects As Variant)
' Filters the Intersects to the specific SubCat
Dim i, j As Integer
j = -1
Erase tmpIntersects
ReDim Preserve tmpIntersects(0 To 2, -1 To 0)
For i = LBound(IntersectsArr, 2) To UBound(IntersectsArr, 2)
If IntersectsArr(2, i) = SubCatID Then 'if the Intersect relates to the current SubCat
j = j + 1
ReDim Preserve tmpIntersects(0 To 2, 0 To j) ' Redimension:
tmpIntersects(0, j) = IntersectsArr(0, i) ' Intersect Code
tmpIntersects(1, j) = IntersectsArr(3, i) ' get the YAxis
tmpIntersects(2, j) = IntersectsArr(9, i) ' get the Order
'Debug.Print ("RESULT ARRAY" & tmpIntersects(i + 1, 0) & "_" & tmpIntersects(i + 1, 1) & "_" & tmpIntersects(i + 1, 2) & "_")
End If
Next i 'Intersect
' sort the array by the Intersect order column (2)
' Call QuickSortArray(tmpIntersects, , , 2)
End Function
Function calcColumnSizes(lenContainer As Integer, ByRef CategorysArr As Variant, ByRef SubCatsArr As Variant, ByRef YAxissArr As Variant) As Integer
Dim lenCategory, lenSubCat, cntQtr As Integer
Dim lenWorkArea, columnSize As Integer
cntQtr = UBound(YAxissArr) + 1
lenWorkArea = lenContainer - defaultCategoryWidth - defaultSubCatWidth - (2 * defaultBuffer)
lenWorkArea = lenWorkArea - ((2 * defaultBuffer) + (cntQtr * defaultBuffer))
columnSize = lenWorkArea / cntQtr
' now propogate the columnSize into the YAxiss data
For i = LBound(YAxissArr, 1) To UBound(YAxissArr, 1)
YAxissArr(2, i) = str(Round(columnSize, 1)) ' Width
Next i
calcColumnSizes = columnSize
End Function
Function calcBlockWidth(columnSize As Integer, density As Integer) As Integer
calcBlockWidth = columnSize / density
End Function
Function setSubCatHeight(SubCatYPos, ID As Integer, ByRef SubCatsArr As Variant, ByRef CategorysArr As Variant)
Dim curSubCat As Integer
curSubCat = findArrStr(SubCatsArr, 0, str(ID))
If SubCatsArr(3, curSubCat) < SubCatYPos Then
SubCatsArr(3, curSubCat) = SubCatYPos
Call setCategoryHeight(SubCatYPos, CInt(SubCatsArr(2, curSubCat)), CategorysArr)
End If
End Function
Function setCategoryHeight(CategoryYPos, ID As Integer, ByRef CategorysArr As Variant)
Dim curCategory As Integer
curCategory = findArrStr(CategorysArr, 0, str(ID))
If CategorysArr(2, curCategory) < CategoryYPos Then
CategorysArr(2, curCategory) = CategoryYPos
End If
End Function
Function addCategorys(ByRef CategorysArr As Variant, masterShp As Shape)
Dim i, j As Integer
For i = LBound(CategorysArr, 2) To UBound(CategorysArr, 2)
Call addBlockContents(CInt(CategorysArr(2, i)), CInt(CategorysArr(3, i)), CStr(CategorysArr(1, i)), masterShp) ' X, Y, Name, Shape
Next i
End Function
Function addSubCats(ByRef SubCatsArr As Variant, masterShp As Shape)
Dim i, j As Integer
For i = LBound(SubCatsArr, 2) To UBound(SubCatsArr, 2)
Call addBlockContents(CInt(SubCatsArr(3, i)), CInt(SubCatsArr(4, i)), CStr(SubCatsArr(1, i)), masterShp)
Next i
End Function
Function addIntersects(ByRef IntersectsArr As Variant, masterShp As Shape)
Dim i, j As Integer
Dim myX, myY As Integer
myX = 0
myY = 0
For i = LBound(IntersectsArr, 2) To UBound(IntersectsArr, 2)
' adjust the placement of the shape relative to the masterShape
myX = masterShp.Left + CInt(IntersectsArr(6, i)) + defaultCategoryWidth + defaultSubCatWidth + (defaultBuffer * 2)
myY = masterShp.Top + CInt(IntersectsArr(7, i))
Call addBlockContents(myX, myY, CStr(IntersectsArr(1, i)), masterShp)
'For j = LBound(COAArray, 2) To UBound(COAArray, 2)
'Next j
Next i
End Function
Function addBlockContents(x, y, IntersectName As String, myShp As Shape)
Dim oSh As Shape
Set oSh = ActiveWindow.Selection.SlideRange.Shapes.AddShape(msoShapeRectangle, x, y, defaultObjectWidth, defaultObjectHeight)
With oSh
With .Fill
.Visible = msoTrue
.ForeColor.ObjectThemeColor = msoThemeColorText2
.ForeColor.TintAndShade = 0
.ForeColor.Brightness = 0.8000000119
.Transparency = 0
.Solid
End With 'fill
With .TextFrame
.MarginLeft = 0
.MarginRight = 0
.MarginTop = 0
.MarginBottom = 0
.AutoSize = False
With .TextRange
.Text = IntersectName
With .Font
.Name = "Ariel"
.Size = 6.4
.Bold = msoFalse
.Italic = msoFalse
.Underline = msoFalse
.Shadow = msoFalse
.Emboss = msoFalse
.BaselineOffset = 0
.AutoRotateNumbers = msoFalse
.Color.SchemeColor = ppForeground
End With ' Font
End With ' TextRange
End With ' TextFrame
End With ' oSh, the shape itself
'Set addBlockContents = oSh
End Function
any help will be awesome!
The easiest way to get started here is to use tables. It will help you with a lot of the layout issues.
As a first step, create a table manually. Insert a table and remove headings and other automatic coloring. Set the border line to maximum width and to match the background color before applying it to all cells.
Now you can merge cells to create larger boxes (like Cars in your example). Deleting cells isn't possible, but you can set the background color.
If this is good enough, then you have something to automate.
If not, then you can perhaps use this to find coordinates for boxes to create.
I have a spreadsheet that involves the user resizing some rectangular shapes, which are set on a background of an Excel grid with column width = row height = 10pixels. The purpose of this background is to give a scale to the plan, which is made by the shapes; in this case, one column or row represents 10cm - there is a thick border after every 10 cells to represent a metre:
When the user resizes the rectangle, I would like the text inside the rectangle to display the dimensions, according to the scale of the plan. I have read many articles about how the shapes dimensions are provided in points, and the columns and rows in pixels (or a unit based on the font), and have found the conversion function between them, but it does not seem to give the results I would expect - the values for the width and height depend on the level of zoom, giving smaller and smaller results as I zoom out, even though the displayed pixel width remains the same.
Is there a way to consistently convert the pixel units of the grid to the points unit of the shapes such that I can essentially count how many column widths and row heights comprise the shape dimensions? Here is the macro I have written so far:
Option Explicit
Dim sh As Shape
Dim dbPx_Per_Unit As Double
Dim strUnit As String
Dim UserSelection As Variant
Dim strText As String
Dim strWidth As String
Dim strHeight As String
Sub LabelShapeSize()
Set UserSelection = ActiveWindow.Selection
'is selection a shape?
On Error GoTo NoShapeSelected
Set sh = ActiveSheet.Shapes(UserSelection.Name)
On Error Resume Next
'pixels are the units for the columns and rows
'dbPx_Per_Unit = InputBox("there are this many pixels per unit:", "Conversion Rate", 10)
dbPx_Per_Unit = 100
'strUnit = InputBox("Unit Name:", "Units", "M")
strUnit = "M"
With sh
'Width and length is measured in points, so we need to convert the points to pixels to get the actual size
strWidth = Format(Application.ActiveWindow.PointsToScreenPixelsX(.Width) / dbPx_Per_Unit, "#,##0.0")
strHeight = Format(Application.ActiveWindow.PointsToScreenPixelsY(.Height) / dbPx_Per_Unit, "#,##0.0")
'this is our message that will be in the shape
strText = strWidth & strUnit & " x " & strHeight & strUnit
With .TextFrame2
.VerticalAnchor = msoAnchorMiddle
With .TextRange.Characters
.ParagraphFormat.FirstLineIndent = 0
.ParagraphFormat.Alignment = msoAlignCenter
.Text = strText
'I'll sort something out for dark shapes at some point, but for now let's just write in black ink
With .Font
.Fill.Visible = msoTrue
.Fill.ForeColor.ObjectThemeColor = msoThemeColorDark1
.Fill.Solid
.Size = 10
End With
End With
End With
End With
Exit Sub
'No shape error
NoShapeSelected:
MsgBox "You must select a shape to calculate dimensions!", vbCritical, "Object not set to an instance of a Nobject"
End Sub
****** for completeness, here is the final script I wrote implementing the solution in the answer below ******
Option Explicit
Dim sh As Shape
Dim db_Cols_Per_Unit As Double
Dim strUnit As String
Dim strText As String
Dim userSelection As Variant
Dim ws As Worksheet
Dim clrBackground As Long
Dim leftCol As Integer
Dim colWidth As Integer
Dim topRow As Integer
Dim rowHeight As Integer
Sub LabelShapeSize()
Set userSelection = ActiveWindow.Selection
Set ws = ActiveSheet
db_Cols_Per_Unit = 10
strUnit = "M"
'is selection a shape?
On Error GoTo NoShapeSelected
Set sh = ActiveSheet.Shapes(userSelection.Name)
On Error Resume Next
topRow = 1
rowHeight = 0
leftCol = 1
colWidth = 0
With sh
While ws.Cells(1, leftCol).Left <= .Left 'Move left until we find the first column the shape lies within
leftCol = leftCol + 1
Wend
While ws.Cells(1, leftCol + colWidth).Left <= .Left + .Width 'Continue moving left until we find the first column the shape does not lie within
colWidth = colWidth + 1
Wend
While ws.Cells(topRow, 1).Top <= .Top 'Move down until we find the first row the shape lies within
topRow = topRow + 1
Wend
While ws.Cells(topRow + rowHeight, 1).Top <= .Top + .Height 'Continue moving down until we find the first row the shape does not lie within
rowHeight = rowHeight + 1
Wend
'this is our message that will be in the shape
strText = Format(colWidth / db_Cols_Per_Unit & strUnit, "#,##0.0") & " x " & rowHeight / Format(db_Cols_Per_Unit, "#,##0.0") & strUnit
clrBackground = .Fill.ForeColor.RGB
With .TextFrame2
.VerticalAnchor = msoAnchorMiddle
With .TextRange.Characters
.ParagraphFormat.FirstLineIndent = 0
.ParagraphFormat.Alignment = msoAlignCenter
.Text = strText
With .Font
.Fill.Visible = msoTrue
.Fill.ForeColor.RGB = ContrastColor(clrBackground)
.Fill.Solid
.Size = 10
End With
End With
End With
End With
Exit Sub
'No shape error
NoShapeSelected:
MsgBox "You must select a shape to calculate dimensions!", vbCritical, "Object not set to an instance of a Nobject"
End Sub
Function ContrastColor(clrBackground As Long) As Long
Dim brightness As Integer
Dim luminance As Double
Dim r As Integer
Dim g As Integer
Dim b As Integer
r = clrBackground Mod 256
g = (clrBackground \ 256) Mod 256
b = (clrBackground \ 65536) Mod 256
luminance = ((0.199 * r) + (0.587 * g) + (0.114 * b)) / 255
If luminance > 0.5 Then
brightness = 0
Else
brightness = 255
End If
ContrastColor = RGB(brightness, brightness, brightness)
End Function
thanks to #Gacek answer in this question for the luminance function.
I believe your best bet would be to make use of the Left, Top, Width, and Height cell properties. They'll tell you the value in Excel's weird format (the same units as used by the shapes), so you won't need to do any conversions.
The downside is that as far as I know of, there's no way to get the row/column that exists at a given top/left value, so you need to search through all the rows/columns until you find the one that matches your shape's boundaries.
Here's a quick example (there's probably an off-by-one error in here somewhere)
Dim UserSelection As Variant
Dim ws As Worksheet
Dim sh As Shape
Dim leftCol As Integer
Dim colWidth As Integer
Dim topRow As Integer
Dim rowHeight As Integer
Set ws = ActiveSheet
Set UserSelection = ActiveWindow.Selection
Set sh = ActiveSheet.Shapes(UserSelection.Name)
leftCol = 1
colWidth = 0
While ws.Cells(1, leftCol).Left <= sh.Left 'Move left until we find the first column the shape lies within
leftCol = leftCol + 1
Wend
While ws.Cells(1, leftCol + colWidth).Left <= sh.Left + sh.width 'Continue moving left until we find the first column the shape does not lie within
colWidth = colWidth + 1
Wend
topRow = 1
rowHeight = 0
While ws.Cells(topRow, 1).Top <= sh.Top 'Move down until we find the first row the shape lies within
topRow = topRow + 1
Wend
While ws.Cells(topRow + rowHeight, 1).Top <= sh.Top + sh.height 'Continue moving down until we find the first row the shape does not lie within
rowHeight = rowHeight + 1
Wend
MsgBox "Shape is " & colWidth & " columns wide by " & rowHeight & " rows high"
I want to write a function where I can select one shape after which a macro aligns all the shapes that are within a 'short range' of the selected shape.
Therefore I wrote the following code that selects all the object within a range:
Sub Shape_Dimensions()
Dim L As Long
Dim T As Long
Dim H As Long
Dim W As Long
With ActiveWindow.Selection
If .Type = ppSelectionShapes Then
L = .ShapeRange.Left
T = .ShapeRange.Top
H = .ShapeRange.Height
W = .ShapeRange.Width
Else
MsgBox "You have not selected an OBJECT in PowerPoint to dimension."
Exit Sub
End If
End With
'Set range for selection
TopRange = L + 30
DownRange = T + H + 20
'Left and right are 0 - 600
End Sub
Now the final step I want to take is select all shapes that are within the top range and down range and align them with the top of the selected box. Any thoughts on how I should proceed?
Sub Shape_Align()
Dim L As Long
Dim T As Long
Dim H As Long, TopRange As Long, DownRange As Long
Dim W As Long, s As Shape, n As String
With ActiveWindow.Selection
If .Type = ppSelectionShapes Then
L = .ShapeRange.Left
T = .ShapeRange.Top
H = .ShapeRange.Height
W = .ShapeRange.Width
n = .ShapeRange.Name
Else
MsgBox "You have not selected an OBJECT in PowerPoint to dimension."
Exit Sub
End If
End With
'Set range for selection
TopRange = L + 30
DownRange = T + H + 20
'Left and right are 0 - 600
For Each s In ActiveWindow.View.Slide.Shapes
If s.Name <> n Then
'in scope for lining up?
If Abs(s.Top - T) < 60 Then
s.Top = T
End If
End If
Next s
End Sub
I have a sheet with several shapes which have text strings, I'd like to color those shapes based on its text. Here is the code I have that for now it doesn't work as expected.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim shp As Shape, r As Long, g As Long, b As Long, NormScale As String
With ActiveSheet
For Each shp In .Shapes
With shp.TextFrame
Select Case NormScale
Case "N"
r = 255
g = 0
b = 0
Case "P"
r = 128
g = 128
b = 128
End Select
End With
shp.Fill.ForeColor.RGB = RGB(r, g, b)
Next shp
End With
End Sub
You just forgot to read the text:
Sub Mike()
Dim shp As Shape, r As Long, g As Long, b As Long, NormScale As String
With ActiveSheet
For Each shp In .Shapes
With shp.TextFrame
NormScale = .Characters.Text
Select Case NormScale
Case "N"
r = 255
g = 0
b = 0
Case "P"
r = 128
g = 128
b = 128
End Select
End With
shp.Fill.ForeColor.RGB = RGB(r, g, b)
Next shp
End With
End Sub
EDIT#1:
To exclude specific Shapes from the process, we must first identify then:
Sub WhatDoWeHave()
Dim shp As Shape
With ActiveSheet
For Each shp In .Shapes
MsgBox shp.Type & vbCrLf & shp.Name
Next shp
End With
End Sub
EDIT#2:
This version will exclude Shapes whose Name begins with "Picture"
Sub Mike()
Dim shp As Shape, r As Long, g As Long, b As Long, NormScale As String
With ActiveSheet
For Each shp In .Shapes
If InStr(shp.Name, "Picture") = 0 Then
With shp.TextFrame
NormScale = .Characters.Text
Select Case NormScale
Case "N"
r = 255
g = 0
b = 0
Case "P"
r = 128
g = 128
b = 128
End Select
End With
shp.Fill.ForeColor.RGB = RGB(r, g, b)
End If
Next shp
End With
End Sub