Added the ability to disable character spin when the librarian window is open.

Jayden Platell [09-05-14 - 02:00]
Added the ability to disable character spin when the librarian window is open.
Added an options button to the top right of the window.
Made compatibility changes for the 1.4 patch.
Filename
Librarian.lua
Librarian.xml
LibrarianSettings.lua
diff --git a/Librarian.lua b/Librarian.lua
index 9faffd1..85c6a82 100644
--- a/Librarian.lua
+++ b/Librarian.lua
@@ -10,7 +10,7 @@ ZO_CreateStringId("SI_LIBRARIAN_SORT_TYPE_TITLE", "Title")
 ZO_CreateStringId("SI_LIBRARIAN_SORT_TYPE_WORD_COUNT", "Words")
 ZO_CreateStringId("SI_LIBRARIAN_MARK_UNREAD", "Mark as Unread")
 ZO_CreateStringId("SI_LIBRARIAN_MARK_READ", "Mark as Read")
-ZO_CreateStringId("SI_LIBRARIAN_CREDIT", "Librarian 1.2.5 by Flamage")
+ZO_CreateStringId("SI_LIBRARIAN_CREDIT", "Librarian 1.2.6 by Flamage")
 ZO_CreateStringId("SI_LIBRARIAN_BOOK_COUNT", "%d Books")
 ZO_CreateStringId("SI_LIBRARIAN_UNREAD_COUNT", "%s (%d Unread)")
 ZO_CreateStringId("SI_LIBRARIAN_SHOW_ALL_BOOKS", "Show books for all characters")
@@ -24,7 +24,7 @@ ZO_CreateStringId("SI_LIBRARIAN_EMPTY_LIBRARY_IMPORT_PROMPT", [[It appears your
 Patch 1.3 fixed a bug in the storage of addon data which now needs to be moved back to the correct place.
 Please click the "Import from before patch" button in the Librarian setting menu to perform this migration.
 It is recommended that you backup your Librarian SavedVariables before performing this step as a precaution.
-Librarian settings can be found by pressing Escape to open the main menu, and selecting Settings, Addon Settings, then Librarian.]])
+Librarian settings can be opened by clicking the settings button in the top right of the window.]])

 local SORT_ARROW_UP = "EsoUI/Art/Miscellaneous/list_sortUp.dds"
 local SORT_ARROW_DOWN = "EsoUI/Art/Miscellaneous/list_sortDown.dds"
@@ -40,64 +40,71 @@ local ENTRY_SORT_KEYS =
 }

 function Librarian:New()
-	local librarian = ZO_SortFilterList.New(self, LibrarianFrame)
-	librarian:Initialise()
-	return librarian
+    local librarian = ZO_SortFilterList.New(self, LibrarianFrame)
+    librarian:Initialise()
+    return librarian
 end

 function Librarian:Initialise()
- 	self.masterList = {}
- 	self.newBookCount = 0
+    self.masterList = {}
+    self.newBookCount = 0
     self.sortHeaderGroup:SelectHeaderByKey("timeStamp")

- 	ZO_ScrollList_AddDataType(self.list, LIBRARIAN_DATA, "LibrarianBookRow", 30, function(control, data) self:SetupBookRow(control, data) end)
- 	ZO_ScrollList_EnableHighlight(self.list, "ZO_ThinListHighlight")
+    ZO_ScrollList_AddDataType(self.list, LIBRARIAN_DATA, "LibrarianBookRow", 30, function(control, data) self:SetupBookRow(control, data) end)
+    ZO_ScrollList_EnableHighlight(self.list, "ZO_ThinListHighlight")

-	self.localSavedVars = ZO_SavedVars:New("Librarian_SavedVariables", 1, nil, self.defaults, nil)
-	self.globalSavedVars = ZO_SavedVars:NewAccountWide("Librarian_SavedVariables", 1, nil, self.defaults, nil)
+    self.localSavedVars = ZO_SavedVars:New("Librarian_SavedVariables", 1, nil, self.defaults, nil)
+    self.globalSavedVars = ZO_SavedVars:NewAccountWide("Librarian_SavedVariables", 1, nil, self.defaults, nil)

-	if not self.globalSavedVars.settings then self.globalSavedVars.settings = {} end
-	self.settings = self.globalSavedVars.settings
+    if not self.globalSavedVars.settings then self.globalSavedVars.settings = {} end
+    self.settings = self.globalSavedVars.settings

-	if not self.globalSavedVars.books then self.globalSavedVars.books = {} end
-	self.books = self.globalSavedVars.books
+    if not self.globalSavedVars.books then self.globalSavedVars.books = {} end
+    self.books = self.globalSavedVars.books

-	if not self.localSavedVars.characterBooks then self.localSavedVars.characterBooks = {} end
-	self.characterBooks = self.localSavedVars.characterBooks
+    if not self.localSavedVars.characterBooks then self.localSavedVars.characterBooks = {} end
+    self.characterBooks = self.localSavedVars.characterBooks

-	self.searchBox = GetControl(LibrarianFrame, "SearchBox")
+    self.searchBox = GetControl(LibrarianFrame, "SearchBox")
     self.searchBox:SetHandler("OnTextChanged", function() self:OnSearchTextChanged() end)
     self.search = ZO_StringSearch:New()
     self.search:AddProcessor(LIBRARIAN_SEARCH, function(stringSearch, data, searchTerm, cache) return self:ProcessBookEntry(stringSearch, data, searchTerm, cache) end)

-	self.sortFunction = function(listEntry1, listEntry2) return ZO_TableOrderingFunction(listEntry1.data, listEntry2.data, self.currentSortKey, ENTRY_SORT_KEYS, self.currentSortOrder) end
+    self.sortFunction = function(listEntry1, listEntry2) return ZO_TableOrderingFunction(listEntry1.data, listEntry2.data, self.currentSortKey, ENTRY_SORT_KEYS, self.currentSortOrder) end

-	self:UpdateSavedVariables()
+    self:UpdateSavedVariables()

-	local settings = LibrarianSettings:New(self.settings)
+    local settings = LibrarianSettings:New(self.settings)

-	local function OnShowAllBooksClicked(checkButton, isChecked)
+    local function OnShowAllBooksClicked(checkButton, isChecked)
         self.settings.showAllBooks = isChecked
         self:RefreshFilters()
     end

     local function GetShowAllBooks()
-		return self.settings.showAllBooks
+        return self.settings.showAllBooks
     end

-	local showAllBooks = LibrarianFrameShowAllBooks
-	ZO_CheckButton_SetToggleFunction(showAllBooks, OnShowAllBooksClicked)
+    local showAllBooks = LibrarianFrameShowAllBooks
+    ZO_CheckButton_SetToggleFunction(showAllBooks, OnShowAllBooksClicked)
     ZO_CheckButton_SetCheckState(showAllBooks, GetShowAllBooks())

     --self:ImportFromLoreLibrary()
-	self:RefreshData()
-	self:InitializeKeybindStripDescriptors()
-	self:InitializeScene()
-	self:AddLoreReaderUnreadToggle()
+    self:RefreshData()
+    self:InitializeKeybindStripDescriptors()
+    self:InitializeScene()
+    self:AddLoreReaderUnreadToggle()
 end

 function Librarian:AddLoreReaderUnreadToggle()
-	if LORE_READER.keybindStripDescriptor then
+    local readerKeybinds
+    if LORE_READER.keybindStripDescriptor then
+        readerKeybinds = LORE_READER.keybindStripDescriptor
+    else
+        readerKeybinds = LORE_READER.PCKeybindStripDescriptor
+    end
+
+    if readerKeybinds then
         -- Special exit button
         local exitKeybind =
         {
@@ -106,35 +113,35 @@ function Librarian:AddLoreReaderUnreadToggle()
             keybind = "UI_SHORTCUT_EXIT",
             callback = function() SCENE_MANAGER:HideCurrentScene() end,
         }
-        table.insert(LORE_READER.keybindStripDescriptor, exitKeybind)
+        table.insert(readerKeybinds, exitKeybind)

-		local toggleKeybind =
-		{
+        local toggleKeybind =
+        {
             alignment = KEYBIND_STRIP_ALIGN_RIGHT,
             name = function()
-            	local book = self:FindBook(LORE_READER.titleText)
-            	if not book or book.unread then
-            		if self.settings.showUnreadIndicatorInReader then
-            			self.unreadIndicator:SetHidden(false)
-            		else
-            			self.unreadIndicator:SetHidden(true)
-            		end
-            		return GetString(SI_LIBRARIAN_MARK_READ)
-            	else
-            		self.unreadIndicator:SetHidden(true)
-            		return GetString(SI_LIBRARIAN_MARK_UNREAD)
-            	end
+                local book = self:FindBook(LORE_READER.titleText)
+                if not book or book.unread then
+                    if self.settings.showUnreadIndicatorInReader then
+                        self.unreadIndicator:SetHidden(false)
+                    else
+                        self.unreadIndicator:SetHidden(true)
+                    end
+                    return GetString(SI_LIBRARIAN_MARK_READ)
+                else
+                    self.unreadIndicator:SetHidden(true)
+                    return GetString(SI_LIBRARIAN_MARK_UNREAD)
+                end
             end,
             keybind = "UI_SHORTCUT_SECONDARY",
             callback = function()
-            	local book = self:FindBook(LORE_READER.titleText)
-            	if not book then return end
+                local book = self:FindBook(LORE_READER.titleText)
+                if not book then return end
                 book.unread = not book.unread
-                KEYBIND_STRIP:UpdateKeybindButtonGroup(LORE_READER.keybindStripDescriptor)
+                KEYBIND_STRIP:UpdateKeybindButtonGroup(readerKeybinds)
                 self:RefreshData()
             end
         }
-        table.insert(LORE_READER.keybindStripDescriptor, toggleKeybind)
+        table.insert(readerKeybinds, toggleKeybind)
     end

     self.unreadIndicator = WINDOW_MANAGER:CreateControl("LibrarianUnreadIndicator", ZO_LoreReaderBookContainer, CT_TEXTURE)
@@ -145,11 +152,19 @@ function Librarian:AddLoreReaderUnreadToggle()

     local function OnSceneStateChange(oldState, newState)
         if(newState == SCENE_SHOWING) then
-            KEYBIND_STRIP:RemoveKeybindButton(KEYBIND_STRIP.defaultExit)
-            KEYBIND_STRIP:AddKeybindButtonGroup(LORE_READER.keybindStripDescriptor)
+            if KEYBIND_STRIP.defaultExit then
+                KEYBIND_STRIP:RemoveKeybindButton(KEYBIND_STRIP.defaultExit)
+            else
+                KEYBIND_STRIP:RemoveDefaultExit()
+            end
+            KEYBIND_STRIP:AddKeybindButtonGroup(readerKeybinds)
         elseif(newState == SCENE_HIDDEN) then
-            KEYBIND_STRIP:RemoveKeybindButtonGroup(LORE_READER.keybindStripDescriptor)
-            KEYBIND_STRIP:AddKeybindButton(KEYBIND_STRIP.defaultExit)
+            KEYBIND_STRIP:RemoveKeybindButtonGroup(readerKeybinds)
+            if KEYBIND_STRIP.defaultExit then
+                KEYBIND_STRIP:AddKeybindButton(KEYBIND_STRIP.defaultExit)
+            else
+                KEYBIND_STRIP:RestoreDefaultExit()
+            end
         end
     end

@@ -167,55 +182,55 @@ function Librarian:AddLoreReaderUnreadToggle()
 end

 function Librarian:UpdateSavedVariables()
-	-- Version 1.0.4 - Settings moved to global variables.
-	if self.localSavedVars.setting_time_format then
-		self.globalSavedVars.settings.time_format = self.localSavedVars.setting_time_format
-		self.localSavedVars.setting_time_format = nil
-	end
-
-	-- Version 1.0.4 - Book data moved to global variables
-	if self.localSavedVars.books then
-		for _,book in ipairs(self.localSavedVars.books) do
-			local timeStamp = book.timeStamp
-			local unread = book.unread
-			self:AddBook(book)
-			local characterBook = self:FindCharacterBook(book.title)
-			if characterBook then characterBook.timeStamp = timeStamp end
-			local globalBook = self:FindBook(book.title)
-			if globalBook then
-				globalBook.timeStamp = timeStamp
-				globalBook.unread = unread
-			end
-		end
-		self.localSavedVars.books = nil
-		self:RefreshData()
-	end
-
-	-- Version 1.0.16 - Fixed a couple of settings names.
-	if self.globalSavedVars.settings.alert_style then
-		self.globalSavedVars.settings.alertStyle = self.globalSavedVars.settings.alert_style
-		self.globalSavedVars.settings.alert_style = nil
-	end
-
-	if self.globalSavedVars.settings.time_format then
-		self.globalSavedVars.settings.timeFormat = self.globalSavedVars.settings.time_format
-		self.globalSavedVars.settings.time_format = nil
-	end
-
-	-- Version 1.1.3 - SavedVariable hell!
-	for _,account in pairs(Librarian_SavedVariables["Default"]) do
-		for _,book in pairs(account["$AccountWide"].books) do
-			if type(book.body) == "string" then
-				local newBody = book.body
-				book.body = {}
-				while string.len(newBody) > 1024 do
-					table.insert(book.body, string.sub(newBody, 0, 1024))
-					newBody = string.sub(newBody, 1025)
-				end
-				table.insert(book.body, newBody)
-			end
-		end
-	end
+    -- Version 1.0.4 - Settings moved to global variables.
+    if self.localSavedVars.setting_time_format then
+        self.globalSavedVars.settings.time_format = self.localSavedVars.setting_time_format
+        self.localSavedVars.setting_time_format = nil
+    end
+
+    -- Version 1.0.4 - Book data moved to global variables
+    if self.localSavedVars.books then
+        for _,book in ipairs(self.localSavedVars.books) do
+            local timeStamp = book.timeStamp
+            local unread = book.unread
+            self:AddBook(book)
+            local characterBook = self:FindCharacterBook(book.title)
+            if characterBook then characterBook.timeStamp = timeStamp end
+            local globalBook = self:FindBook(book.title)
+            if globalBook then
+                globalBook.timeStamp = timeStamp
+                globalBook.unread = unread
+            end
+        end
+        self.localSavedVars.books = nil
+        self:RefreshData()
+    end
+
+    -- Version 1.0.16 - Fixed a couple of settings names.
+    if self.globalSavedVars.settings.alert_style then
+        self.globalSavedVars.settings.alertStyle = self.globalSavedVars.settings.alert_style
+        self.globalSavedVars.settings.alert_style = nil
+    end
+
+    if self.globalSavedVars.settings.time_format then
+        self.globalSavedVars.settings.timeFormat = self.globalSavedVars.settings.time_format
+        self.globalSavedVars.settings.time_format = nil
+    end
+
+    -- Version 1.1.3 - SavedVariable hell!
+    for _,account in pairs(Librarian_SavedVariables["Default"]) do
+        for _,book in pairs(account["$AccountWide"].books) do
+            if type(book.body) == "string" then
+                local newBody = book.body
+                book.body = {}
+                while string.len(newBody) > 1024 do
+                    table.insert(book.body, string.sub(newBody, 0, 1024))
+                    newBody = string.sub(newBody, 1025)
+                end
+                table.insert(book.body, newBody)
+            end
+        end
+    end
 end

 function Librarian:InitializeKeybindStripDescriptors()
@@ -226,7 +241,7 @@ function Librarian:InitializeKeybindStripDescriptors()
             name = GetString(SI_LORE_LIBRARY_READ),
             keybind = "UI_SHORTCUT_PRIMARY",
             visible = function()
-            	return self.mouseOverRow
+                return self.mouseOverRow
             end,
             callback = function()
                 self:ReadBook(self.mouseOverRow.data.title)
@@ -235,20 +250,20 @@ function Librarian:InitializeKeybindStripDescriptors()
         {
             alignment = KEYBIND_STRIP_ALIGN_RIGHT,
             name = function()
-            	if not self.mouseOverRow then return nil end
-            	local book = self:FindBook(self.mouseOverRow.data.title)
-            	if book.unread then
-            		return GetString(SI_LIBRARIAN_MARK_READ)
-            	else
-            		return GetString(SI_LIBRARIAN_MARK_UNREAD)
-            	end
+                if not self.mouseOverRow then return nil end
+                local book = self:FindBook(self.mouseOverRow.data.title)
+                if book.unread then
+                    return GetString(SI_LIBRARIAN_MARK_READ)
+                else
+                    return GetString(SI_LIBRARIAN_MARK_UNREAD)
+                end
             end,
             keybind = "UI_SHORTCUT_SECONDARY",
             visible = function()
-            	return self.mouseOverRow
+                return self.mouseOverRow
             end,
             callback = function()
-            	local book = self:FindBook(self.mouseOverRow.data.title)
+                local book = self:FindBook(self.mouseOverRow.data.title)
                 book.unread = not book.unread
                 self:RefreshData()
             end,
@@ -257,92 +272,94 @@ function Librarian:InitializeKeybindStripDescriptors()
 end

 function Librarian:InitializeScene()
-	if not LIBRARIAN_SCENE then
-		LIBRARIAN_TITLE_FRAGMENT = ZO_SetTitleFragment:New(SI_WINDOW_TITLE_LIBRARIAN)
-		LIBRARIAN_SCENE = ZO_Scene:New("librarian", SCENE_MANAGER)
-		LIBRARIAN_SCENE:AddFragmentGroup(FRAGMENT_GROUP.MOUSE_DRIVEN_UI_WINDOW)
-		LIBRARIAN_SCENE:AddFragmentGroup(FRAGMENT_GROUP.FRAME_TARGET_STANDARD_RIGHT_PANEL)
-		LIBRARIAN_SCENE:AddFragment(ZO_FadeSceneFragment:New(LibrarianFrame))
-		LIBRARIAN_SCENE:AddFragment(RIGHT_BG_FRAGMENT)
-		LIBRARIAN_SCENE:AddFragment(TITLE_FRAGMENT)
-		LIBRARIAN_SCENE:AddFragment(LIBRARIAN_TITLE_FRAGMENT)
-		LIBRARIAN_SCENE:AddFragment(CODEX_WINDOW_SOUNDS)
-
-		LIBRARIAN_SCENE:RegisterCallback("StateChange",
-			function(oldState, newState)
-				if(newState == SCENE_SHOWING) then
+    if not LIBRARIAN_SCENE then
+        LIBRARIAN_TITLE_FRAGMENT = ZO_SetTitleFragment:New(SI_WINDOW_TITLE_LIBRARIAN)
+        LIBRARIAN_SCENE = ZO_Scene:New("librarian", SCENE_MANAGER)
+        LIBRARIAN_SCENE:AddFragmentGroup(FRAGMENT_GROUP.MOUSE_DRIVEN_UI_WINDOW)
+        if self.settings.enableCharacterSpin then
+            LIBRARIAN_SCENE:AddFragmentGroup(FRAGMENT_GROUP.FRAME_TARGET_STANDARD_RIGHT_PANEL)
+        end
+        LIBRARIAN_SCENE:AddFragment(ZO_FadeSceneFragment:New(LibrarianFrame))
+        LIBRARIAN_SCENE:AddFragment(RIGHT_BG_FRAGMENT)
+        LIBRARIAN_SCENE:AddFragment(TITLE_FRAGMENT)
+        LIBRARIAN_SCENE:AddFragment(LIBRARIAN_TITLE_FRAGMENT)
+        LIBRARIAN_SCENE:AddFragment(CODEX_WINDOW_SOUNDS)
+
+        LIBRARIAN_SCENE:RegisterCallback("StateChange",
+            function(oldState, newState)
+                if(newState == SCENE_SHOWING) then
                     KEYBIND_STRIP:AddKeybindButtonGroup(self.keybindStripDescriptor)
                 elseif(newState == SCENE_HIDDEN) then
                     KEYBIND_STRIP:RemoveKeybindButtonGroup(self.keybindStripDescriptor)
                 end
             end)
-	end
+    end
 end

 function Librarian:ImportFromLoreLibrary()
-	local hasImportedBooks = false
-	local chatEnabled = self.settings.chatEnabled
-	local alertEnabled = self.settings.alertEnabled
-	self.settings.chatEnabled = true
-	self.settings.alertEnabled = false
-
-	for categoryIndex = 1, GetNumLoreCategories() do
-		local categoryName, numCollections = GetLoreCategoryInfo(categoryIndex)
-		for collectionIndex = 1, numCollections do
+    local hasImportedBooks = false
+    local chatEnabled = self.settings.chatEnabled
+    local alertEnabled = self.settings.alertEnabled
+    self.settings.chatEnabled = true
+    self.settings.alertEnabled = false
+
+    for categoryIndex = 1, GetNumLoreCategories() do
+        local categoryName, numCollections = GetLoreCategoryInfo(categoryIndex)
+        for collectionIndex = 1, numCollections do
             local collectionName, description, numKnownBooks, totalBooks, hidden = GetLoreCollectionInfo(categoryIndex, collectionIndex)
             if not hidden then
-            	for bookIndex = 1, totalBooks do
-            		local title, icon, known = GetLoreBookInfo(categoryIndex, collectionIndex, bookIndex)
-            		if known then
-            			if not self:FindCharacterBook(title) then
-            				local body, medium, showTitle = ReadLoreBook(categoryIndex, collectionIndex, bookIndex)
-            				local book = {title = title, body = body, medium = medium, showTitle = showTitle}
-            				self:AddBook(book)
-            			end
-            		end
-            	end
+                for bookIndex = 1, totalBooks do
+                    local title, icon, known = GetLoreBookInfo(categoryIndex, collectionIndex, bookIndex)
+                    if known then
+                        if not self:FindCharacterBook(title) then
+                            local body, medium, showTitle = ReadLoreBook(categoryIndex, collectionIndex, bookIndex)
+                            local book = {title = title, body = body, medium = medium, showTitle = showTitle}
+                            self:AddBook(book)
+                        end
+                    end
+                end
             end
         end
-	end
+    end

-	self.settings.chatEnabled = chatEnabled
-	self.settings.alertEnabled = alertEnabled
+    self.settings.chatEnabled = chatEnabled
+    self.settings.alertEnabled = alertEnabled
 end

 function Librarian:CanImportFromEmptyAccount()
-	return Librarian_SavedVariables["Default"][""] ~= nil
+    return Librarian_SavedVariables["Default"][""] ~= nil
 end

 function Librarian:ImportFromEmptyAccount()
-	if Librarian_SavedVariables["Default"][""] ~= nil then
-		Librarian_SavedVariables["Default"][GetDisplayName()] = Librarian_SavedVariables["Default"][""]
-	end
-	Librarian_SavedVariables["Default"][""] = nil
-	SLASH_COMMANDS["/reloadui"]()
+    if Librarian_SavedVariables["Default"][""] ~= nil then
+        Librarian_SavedVariables["Default"][GetDisplayName()] = Librarian_SavedVariables["Default"][""]
+    end
+    Librarian_SavedVariables["Default"][""] = nil
+    SLASH_COMMANDS["/reloadui"]()
 end

 function Librarian:BuildMasterList()
-	if self:CanImportFromEmptyAccount() and #self.books == 0 then LibrarianFrameMessage:SetHidden(false) end
+    if self:CanImportFromEmptyAccount() and #self.books == 0 then LibrarianFrameMessage:SetHidden(false) end

     for i, book in ipairs(self.books) do
-		local data = {}
-		for k,v in pairs(book) do
-			if k == "body" then
-				data[k] = table.concat(book.body)
-			else
-				data[k] = v
-			end
-  		end
-  		data.type = LIBRARIAN_SEARCH
-  		local characterBook = self:FindCharacterBook(book.title)
-  		if characterBook then
-			data.seenByCurrentCharacter = true
-			data.timeStamp = characterBook.timeStamp
-		else
-			data.seenByCurrentCharacter = false
-			data.timeStamp = book.timeStamp
-		end
-  		self.masterList[i] = data
+        local data = {}
+        for k,v in pairs(book) do
+            if k == "body" then
+                data[k] = table.concat(book.body)
+            else
+                data[k] = v
+            end
+        end
+        data.type = LIBRARIAN_SEARCH
+        local characterBook = self:FindCharacterBook(book.title)
+        if characterBook then
+            data.seenByCurrentCharacter = true
+            data.timeStamp = characterBook.timeStamp
+        else
+            data.seenByCurrentCharacter = false
+            data.timeStamp = book.timeStamp
+        end
+        self.masterList[i] = data
     end
 end

@@ -356,17 +373,17 @@ function Librarian:FilterScrollList()
     for i = 1, #self.masterList do
         local data = self.masterList[i]
         if self.settings.showAllBooks or data.seenByCurrentCharacter then
-        	if(searchTerm == "" or self.search:IsMatch(searchTerm, data)) then
-            	table.insert(scrollData, ZO_ScrollList_CreateDataEntry(LIBRARIAN_DATA, data))
-            	bookCount = bookCount + 1
-            	if data.unread then unreadCount = unreadCount + 1 end
+            if(searchTerm == "" or self.search:IsMatch(searchTerm, data)) then
+                table.insert(scrollData, ZO_ScrollList_CreateDataEntry(LIBRARIAN_DATA, data))
+                bookCount = bookCount + 1
+                if data.unread then unreadCount = unreadCount + 1 end
             end
         end
     end

     local message = string.format(GetString(SI_LIBRARIAN_BOOK_COUNT), bookCount)
     if unreadCount > 0 then message = string.format(GetString(SI_LIBRARIAN_UNREAD_COUNT), message, unreadCount) end
-	LibrarianFrameBookCount:SetText(message)
+    LibrarianFrameBookCount:SetText(message)
 end

 function Librarian:SortScrollList()
@@ -375,25 +392,25 @@ function Librarian:SortScrollList()
 end

 function Librarian:SetupBookRow(control, data)
-	control.data = data
-	control.unread = GetControl(control, "Unread")
-	control.found = GetControl(control, "Found")
-	control.title = GetControl(control, "Title")
-	control.wordCount = GetControl(control, "WordCount")
-
-	control.unread.nonRecolorable = true
-	if data.unread then control.unread:SetAlpha(1) else control.unread:SetAlpha(0) end
+    control.data = data
+    control.unread = GetControl(control, "Unread")
+    control.found = GetControl(control, "Found")
+    control.title = GetControl(control, "Title")
+    control.wordCount = GetControl(control, "WordCount")
+
+    control.unread.nonRecolorable = true
+    if data.unread then control.unread:SetAlpha(1) else control.unread:SetAlpha(0) end

-	control.found.normalColor = ZO_NORMAL_TEXT
-	control.found:SetText(self:FormatClockTime(data.timeStamp))
+    control.found.normalColor = ZO_NORMAL_TEXT
+    control.found:SetText(self:FormatClockTime(data.timeStamp))

-	control.title.normalColor = ZO_NORMAL_TEXT
-	control.title:SetText(data.title)
+    control.title.normalColor = ZO_NORMAL_TEXT
+    control.title:SetText(data.title)

-	control.wordCount.normalColor = ZO_NORMAL_TEXT
-	control.wordCount:SetText(data.wordCount)
+    control.wordCount.normalColor = ZO_NORMAL_TEXT
+    control.wordCount:SetText(data.wordCount)

-	ZO_SortFilterList.SetupRow(self, control, data)
+    ZO_SortFilterList.SetupRow(self, control, data)
 end

 function Librarian:ProcessBookEntry(stringSearch, data, searchTerm, cache)
@@ -411,67 +428,67 @@ function Librarian:ProcessBookEntry(stringSearch, data, searchTerm, cache)
 end

 function Librarian:FindCharacterBook(title)
-	if not self.characterBooks then return nil end
-	for _,book in pairs(self.characterBooks) do
-		if book.title == title then return book end
-	end
+    if not self.characterBooks then return nil end
+    for _,book in pairs(self.characterBooks) do
+        if book.title == title then return book end
+    end
 end

 function Librarian:FindBook(title)
-	for _,book in pairs(self.books) do
-		if book.title == title then return book end
-	end
+    for _,book in pairs(self.books) do
+        if book.title == title then return book end
+    end
 end

 function Librarian:AddBook(book)
-	if not self:FindCharacterBook(book.title) then
-		if not self:FindBook(book.title) then
-			book.timeStamp = GetTimeStamp()
-			book.unread = true
-			local wordCount = 0
-			for w in book.body:gmatch("%S+") do wordCount = wordCount + 1 end
-			book.wordCount = wordCount
-
-			local newBody = book.body
-			book.body = {}
-			while string.len(newBody) > 1024 do
-				table.insert(book.body, string.sub(newBody, 0, 1024))
-				newBody = string.sub(newBody, 1025)
-			end
-			table.insert(book.body, newBody)
-
-			table.insert(self.books, book)
-		end
-
-		local characterBook = { title = book.title, timeStamp = GetTimeStamp() }
-		table.insert(self.characterBooks, characterBook)
-
-		self:RefreshData()
-		if self.settings.alertEnabled then
-			CENTER_SCREEN_ANNOUNCE:AddMessage(EVENT_SKILL_RANK_UPDATE, CSA_EVENT_LARGE_TEXT, SOUNDS.BOOK_ACQUIRED, GetString(SI_LIBRARIAN_NEW_BOOK_FOUND))
-		end
-		if self.settings.chatEnabled then
-			d(string.format(GetString(SI_LIBRARIAN_NEW_BOOK_FOUND_WITH_TITLE), book.title))
-		end
-
-		self.newBookCount = self.newBookCount + 1
-		if self.settings.reloadReminderBookCount and self.settings.reloadReminderBookCount > 0 and self.settings.reloadReminderBookCount <= self.newBookCount then
-			d(GetString(SI_LIBRARIAN_RELOAD_REMINDER))
-		end
-	end
+    if not self:FindCharacterBook(book.title) then
+        if not self:FindBook(book.title) then
+            book.timeStamp = GetTimeStamp()
+            book.unread = true
+            local wordCount = 0
+            for w in book.body:gmatch("%S+") do wordCount = wordCount + 1 end
+            book.wordCount = wordCount
+
+            local newBody = book.body
+            book.body = {}
+            while string.len(newBody) > 1024 do
+                table.insert(book.body, string.sub(newBody, 0, 1024))
+                newBody = string.sub(newBody, 1025)
+            end
+            table.insert(book.body, newBody)
+
+            table.insert(self.books, book)
+        end
+
+        local characterBook = { title = book.title, timeStamp = GetTimeStamp() }
+        table.insert(self.characterBooks, characterBook)
+
+        self:RefreshData()
+        if self.settings.alertEnabled then
+            CENTER_SCREEN_ANNOUNCE:AddMessage(EVENT_SKILL_RANK_UPDATE, CSA_EVENT_LARGE_TEXT, SOUNDS.BOOK_ACQUIRED, GetString(SI_LIBRARIAN_NEW_BOOK_FOUND))
+        end
+        if self.settings.chatEnabled then
+            d(string.format(GetString(SI_LIBRARIAN_NEW_BOOK_FOUND_WITH_TITLE), book.title))
+        end
+
+        self.newBookCount = self.newBookCount + 1
+        if self.settings.reloadReminderBookCount and self.settings.reloadReminderBookCount > 0 and self.settings.reloadReminderBookCount <= self.newBookCount then
+            d(GetString(SI_LIBRARIAN_RELOAD_REMINDER))
+        end
+    end
 end

 function Librarian:Toggle()
-	if LibrarianFrame:IsControlHidden() then
-		SCENE_MANAGER:Show("librarian")
-	else
-		SCENE_MANAGER:Hide("librarian")
-	end
+    if LibrarianFrame:IsControlHidden() then
+        SCENE_MANAGER:Show("librarian")
+    else
+        SCENE_MANAGER:Hide("librarian")
+    end
 end

 function Librarian:ReadBook(title)
-	local book = self:FindBook(title)
-	LORE_READER:SetupBook(book.title, book.body and table.concat(book.body), book.medium, book.showTitle)
+    local book = self:FindBook(title)
+    LORE_READER:SetupBook(book.title, book.body and table.concat(book.body), book.medium, book.showTitle)
     SCENE_MANAGER:Push("loreReaderInteraction")
     PlaySound(LORE_READER.OpenSound)
 end
@@ -481,12 +498,12 @@ function Librarian:FormatClockTime(time)
     local utcSeconds = GetTimeStamp() % 86400
     local offset = midnightSeconds - utcSeconds
     if offset < -43200 then
-    	offset = offset + 86400
+        offset = offset + 86400
     end

     local dateString = GetDateStringFromTimestamp(time)
     local timeString = ZO_FormatTime((time + offset) % 86400, TIME_FORMAT_STYLE_CLOCK_TIME, self.settings.timeFormat)
-	return string.format("%s %s", dateString, timeString)
+    return string.format("%s %s", dateString, timeString)
 end

 function Librarian:OnSearchTextChanged()
@@ -495,18 +512,18 @@ function Librarian:OnSearchTextChanged()
 end

 local function SlashCommand(args)
-	Librarian:Toggle()
+    Librarian:Toggle()
 end

 local function OnAddonLoaded(event, addon)
-	if addon == "Librarian" then
-		LIBRARIAN = Librarian:New()
-	end
+    if addon == "Librarian" then
+        LIBRARIAN = Librarian:New()
+    end
 end

 local function OnShowBook(eventCode, title, body, medium, showTitle)
-	local book = { title = title, body = body, medium = medium, showTitle = showTitle }
-	LIBRARIAN:AddBook(book)
+    local book = { title = title, body = body, medium = medium, showTitle = showTitle }
+    LIBRARIAN:AddBook(book)
 end

 function LibrarianRow_OnMouseEnter(control)
diff --git a/Librarian.xml b/Librarian.xml
index 11f4a96..3069771 100644
--- a/Librarian.xml
+++ b/Librarian.xml
@@ -30,6 +30,18 @@
         </Button>
         <TopLevelControl name="LibrarianFrame" inherits="ZO_RightPanelFootPrint" hidden="true">
             <Controls>
+                <Button name="$(parent)Options" mouseOverBlendMode="ADD" inherits="ZO_ButtonBehaviorClickSound">
+                    <Dimensions x="48" y="48" />
+                    <Anchor point="BOTTOMRIGHT" relativePoint="TOPRIGHT" />
+                    <Textures
+                        normal="EsoUI/Art/ChatWindow/chat_options_up.dds"
+                        pressed="EsoUI/Art/ChatWindow/chat_options_down.dds"
+                        mouseOver="EsoUI/Art/ChatWindow/chat_options_over.dds"
+                    />
+                    <OnClicked>
+                        SLASH_COMMANDS["/librarianOptions"]()
+                    </OnClicked>
+                </Button>
                 <Label name="$(parent)BookCount" font="ZoFontHeader3" color="INTERFACE_COLOR_TYPE_TEXT_COLORS:INTERFACE_TEXT_COLOR_NORMAL">
                     <Anchor point="TOPLEFT" offsetY="10" />
                 </Label>
diff --git a/LibrarianSettings.lua b/LibrarianSettings.lua
index 244b0a6..a7c6fc0 100644
--- a/LibrarianSettings.lua
+++ b/LibrarianSettings.lua
@@ -69,14 +69,18 @@ function LibrarianSettings:Initialise(settings)
 	if self.settings.reloadReminderBookCount == nil then
 		self.settings.reloadReminderBookCount = 5
 	end
+
+  if self.settings.enableCharacterSpin == nil then
+    self.settings.enableCharacterSpin = true
+  end

   local panelData = {
     type = "panel",
     name = "Librarian",
     displayName = "Librarian Book Manager",
     author = "Flamage",
-    version = "1.2.5",
-    slashCommand = "/lo"
+    version = "1.2.6",
+    slashCommand = "/librarianOptions"
   }

   local optionsTable = {
@@ -123,6 +127,17 @@ function LibrarianSettings:Initialise(settings)
       setFunc = function(value) self.settings.showUnreadIndicatorInReader = value end
     },
     [5] = {
+      type = "checkbox",
+      name = "Character Spin",
+      tooltip = "Allow the character to spin and face the camera when Librarian is open.",
+      getFunc = function() return self.settings.enableCharacterSpin end,
+      setFunc = function(value)
+        self.settings.enableCharacterSpin = value
+        SLASH_COMMANDS["/reloadui"]()
+      end,
+      warning = "UI will be reloaded automatically."
+    },
+    [6] = {
       type = "button",
       name = "Import from Lore Library",
       tooltip = "Import any missing books from the Lore Library.  Works with all books once Eidetic Memory is unlocked.",