| package li.cil.oc.client |
| |
| import com.google.common.base.Strings |
| import cpw.mods.fml.common.FMLCommonHandler |
| import li.cil.oc.OpenComputers |
| import li.cil.oc.api.detail.ManualAPI |
| import li.cil.oc.api.manual.ContentProvider |
| import li.cil.oc.api.manual.ImageProvider |
| import li.cil.oc.api.manual.ImageRenderer |
| import li.cil.oc.api.manual.PathProvider |
| import li.cil.oc.api.manual.TabIconRenderer |
| import li.cil.oc.common.GuiType |
| import net.minecraft.client.Minecraft |
| import net.minecraft.entity.player.EntityPlayer |
| import net.minecraft.item.ItemStack |
| import net.minecraft.world.World |
| |
| import scala.annotation.tailrec |
| import scala.collection.convert.WrapAsJava._ |
| import scala.collection.convert.WrapAsScala._ |
| import scala.collection.mutable |
| |
| object Manual extends ManualAPI { |
| final val LanguageKey = "%LANGUAGE%" |
| |
| final val FallbackLanguage = "en_US" |
| |
| class History(val path: String, var offset: Int = 0) |
| |
| class Tab(val renderer: TabIconRenderer, val tooltip: Option[String], val path: String) |
| |
| val tabs = mutable.Buffer.empty[Tab] |
| |
| val pathProviders = mutable.Buffer.empty[PathProvider] |
| |
| val contentProviders = mutable.Buffer.empty[ContentProvider] |
| |
| val imageProviders = mutable.Buffer.empty[(String, ImageProvider)] |
| |
| val history = new mutable.Stack[History] |
| |
| reset() |
| |
| override def addTab(renderer: TabIconRenderer, tooltip: String, path: String): Unit = { |
| tabs += new Tab(renderer, Option(tooltip), path) |
| if (tabs.length > 7) { |
| OpenComputers.log.warn("Gosh I'm popular! Too many tabs were added to the OpenComputers in-game manual, so some won't be shown. In case this actually happens, let me know and I'll look into making them scrollable or something...") |
| } |
| } |
| |
| override def addProvider(provider: PathProvider): Unit = { |
| pathProviders += provider |
| } |
| |
| override def addProvider(provider: ContentProvider): Unit = { |
| contentProviders += provider |
| } |
| |
| override def addProvider(prefix: String, provider: ImageProvider): Unit = { |
| imageProviders += (if (Strings.isNullOrEmpty(prefix)) "" else prefix + ":") -> provider |
| } |
| |
| override def pathFor(stack: ItemStack): String = { |
| for (provider <- pathProviders) { |
| val path = try provider.pathFor(stack) catch { |
| case t: Throwable => |
| OpenComputers.log.warn("A path provider threw an error when queried with an item.", t) |
| null |
| } |
| if (path != null) return path |
| } |
| null |
| } |
| |
| override def pathFor(world: World, x: Int, y: Int, z: Int): String = { |
| for (provider <- pathProviders) { |
| val path = try provider.pathFor(world, x, y, z) catch { |
| case t: Throwable => |
| OpenComputers.log.warn("A path provider threw an error when queried with a block.", t) |
| null |
| } |
| if (path != null) return path |
| } |
| null |
| } |
| |
| override def contentFor(path: String): java.lang.Iterable[String] = { |
| val cleanPath = com.google.common.io.Files.simplifyPath(path) |
| val language = FMLCommonHandler.instance.getCurrentLanguage |
| contentForWithRedirects(cleanPath.replaceAll(LanguageKey, language)). |
| orElse(contentForWithRedirects(cleanPath.replaceAll(LanguageKey, FallbackLanguage))). |
| orNull |
| } |
| |
| override def imageFor(href: String): ImageRenderer = { |
| for ((prefix, provider) <- Manual.imageProviders.reverse) { |
| if (href.startsWith(prefix)) { |
| val image = try provider.getImage(href.stripPrefix(prefix)) catch { |
| case t: Throwable => |
| OpenComputers.log.warn("An image provider threw an error when queried.", t) |
| null |
| } |
| if (image != null) return image |
| } |
| } |
| null |
| } |
| |
| override def openFor(player: EntityPlayer): Unit = { |
| if (player.getEntityWorld.isRemote) { |
| player.openGui(OpenComputers, GuiType.Manual.id, player.getEntityWorld, 0, 0, 0) |
| } |
| } |
| |
| def reset(): Unit = { |
| history.clear() |
| history.push(new History(s"$LanguageKey/index.md")) |
| } |
| |
| override def navigate(path: String): Unit = { |
| Minecraft.getMinecraft.currentScreen match { |
| case manual: gui.Manual => manual.pushPage(path) |
| case _ => history.push(new History(path)) |
| } |
| } |
| |
| def makeRelative(path: String, base: String): String = |
| if (path.startsWith("/")) path |
| else { |
| val splitAt = base.lastIndexOf('/') |
| if (splitAt >= 0) base.splitAt(splitAt)._1 + "/" + path |
| else path |
| } |
| |
| @tailrec private def contentForWithRedirects(path: String, seen: List[String] = List.empty): Option[java.lang.Iterable[String]] = { |
| if (seen.contains(path)) return Some(asJavaIterable(Iterable("Redirection loop: ") ++ seen ++ Iterable(path))) |
| doContentLookup(path) match { |
| case Some(content) => content.headOption match { |
| case Some(line) if line.toLowerCase.startsWith("#redirect ") => |
| contentForWithRedirects(makeRelative(line.substring("#redirect ".length), path), seen :+ path) |
| case _ => Some(content) |
| } |
| case _ => None |
| } |
| } |
| |
| private def doContentLookup(path: String): Option[java.lang.Iterable[String]] = { |
| for (provider <- contentProviders) { |
| val lines = try provider.getContent(path) catch { |
| case t: Throwable => |
| OpenComputers.log.warn("A content provider threw an error when queried.", t) |
| null |
| } |
| if (lines != null) return Some(lines) |
| } |
| None |
| } |
| } |