diff --git a/.gitignore b/.gitignore index 5a07d9260..c2d0983b9 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ relwithdebinfo/ profile/ /super-build-shared/ /super-build-static/ +/Products/ + *.sublime-workspace *.sublime-project cmake-build-debug @@ -39,4 +41,7 @@ clang_format.patch conan-cache .ccache CMakeUserPresets.json -__pycache__ \ No newline at end of file +__pycache__ + +# Local working notes, not for commit +ENERGYPLUS_DOCS_WORKFLOW.md diff --git a/CMakeLists.txt b/CMakeLists.txt index e6ea949d2..2c89f8003 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,10 @@ endif() # Or it'll download a zip/tar.gz for you include(FindOpenStudioSDK.cmake) +# Downloads a local copy of the EnergyPlus Input Output Reference HTML documentation, +# so doc links in the app can point at it instead of a live website. +include(FindEnergyPlusDocs.cmake) + ############################################################################### # C O N A N # ############################################################################### @@ -788,6 +792,7 @@ endif() install(DIRECTORY "${openstudio_ROOT_DIR}/Radiance" DESTINATION "." COMPONENT "OpenStudioApp" USE_SOURCE_PERMISSIONS) install(DIRECTORY "${openstudio_ROOT_DIR}/Ruby" DESTINATION "." COMPONENT "OpenStudioApp" USE_SOURCE_PERMISSIONS) install(DIRECTORY "${openstudio_ROOT_DIR}/EnergyPlus" DESTINATION "." COMPONENT "OpenStudioApp" USE_SOURCE_PERMISSIONS) +install(DIRECTORY "${ENERGYPLUS_DOCS_DIR}" DESTINATION "EnergyPlus/doc" COMPONENT "OpenStudioApp" USE_SOURCE_PERMISSIONS) install(DIRECTORY "${openstudio_ROOT_DIR}/Examples" DESTINATION "." COMPONENT "OpenStudioApp" USE_SOURCE_PERMISSIONS) install(DIRECTORY "${openstudio_ROOT_DIR}/Python" DESTINATION "." COMPONENT "Python" USE_SOURCE_PERMISSIONS) diff --git a/FindEnergyPlusDocs.cmake b/FindEnergyPlusDocs.cmake new file mode 100644 index 000000000..bde642303 --- /dev/null +++ b/FindEnergyPlusDocs.cmake @@ -0,0 +1,113 @@ +# Downloads a pre-built HTML export of EnergyPlus's Input Output Reference documentation +# and stages it for installation alongside the application, so doc links can point at a +# local copy instead of a live website. +# +# This is a stopgap: the export is produced manually today, not by a reproducible pipeline +# we control. Bump ENERGYPLUS_DOCS_VERSION/_URL/_EXPECTED_MD5 together whenever a new export +# is published for a newer bundled EnergyPlus version. + +set(ENERGYPLUS_DOCS_VERSION "25.2.0") +set(ENERGYPLUS_DOCS_URL "https://drive.google.com/uc?export=download&id=1nUXQjdpX_AlCqA121Rnedr8edpS2lie5") +set(ENERGYPLUS_DOCS_EXPECTED_MD5 "f13e0a862e6a956f716d78def055a804") + +set(ENERGYPLUS_DOCS_ARCHIVE_DIR "${PROJECT_BINARY_DIR}/EnergyPlusDocsArchive") +set(ENERGYPLUS_DOCS_ARCHIVE_NAME "EnergyPlus-docs-${ENERGYPLUS_DOCS_VERSION}.tar.gz") +set(ENERGYPLUS_DOCS_ARCHIVE_PATH "${ENERGYPLUS_DOCS_ARCHIVE_DIR}/${ENERGYPLUS_DOCS_ARCHIVE_NAME}") + +# Where the extracted, installable HTML ends up. Kept as a cache variable so the rest of +# the build (the install() rule, and the .cxx.in path baked in for build-tree runs) can +# refer to it without re-deriving it. +set(ENERGYPLUS_DOCS_DIR "${PROJECT_BINARY_DIR}/EnergyPlus/doc/input-output-reference" + CACHE PATH "Directory containing the extracted EnergyPlus Input Output Reference HTML" FORCE) + +file(MAKE_DIRECTORY "${ENERGYPLUS_DOCS_ARCHIVE_DIR}") + +set(ENERGYPLUS_DOCS_HASH "") +if(EXISTS "${ENERGYPLUS_DOCS_ARCHIVE_PATH}") + file(MD5 "${ENERGYPLUS_DOCS_ARCHIVE_PATH}" ENERGYPLUS_DOCS_HASH) +endif() + +set(ENERGYPLUS_DOCS_NEEDS_EXTRACT FALSE) + +if(NOT EXISTS "${ENERGYPLUS_DOCS_ARCHIVE_PATH}" OR NOT "${ENERGYPLUS_DOCS_HASH}" MATCHES "${ENERGYPLUS_DOCS_EXPECTED_MD5}") + if(NOT EXISTS "${ENERGYPLUS_DOCS_ARCHIVE_PATH}") + message(STATUS "EnergyPlus docs archive doesn't exist at \"${ENERGYPLUS_DOCS_ARCHIVE_PATH}\"") + else() + message(STATUS + "Existing EnergyPlus docs archive md5sum HASH mismatch\n" + " for file: ${ENERGYPLUS_DOCS_ARCHIVE_PATH}\n" + " expected hash: [${ENERGYPLUS_DOCS_EXPECTED_MD5}]\n" + " actual hash: [${ENERGYPLUS_DOCS_HASH}]\n" + ) + endif() + + message(STATUS "Downloading EnergyPlus docs: ${ENERGYPLUS_DOCS_URL}") + file(DOWNLOAD "${ENERGYPLUS_DOCS_URL}" "${ENERGYPLUS_DOCS_ARCHIVE_PATH}" + SHOW_PROGRESS + INACTIVITY_TIMEOUT 900 # 15-min timeout + STATUS ENERGYPLUS_DOCS_DOWNLOAD_STATUS + ) + list(GET ENERGYPLUS_DOCS_DOWNLOAD_STATUS 0 ENERGYPLUS_DOCS_DOWNLOAD_STATUS_CODE) + list(GET ENERGYPLUS_DOCS_DOWNLOAD_STATUS 1 ENERGYPLUS_DOCS_DOWNLOAD_ERROR_MSG) + + if(ENERGYPLUS_DOCS_DOWNLOAD_STATUS_CODE) + message(FATAL_ERROR + "Download of EnergyPlus docs from ${ENERGYPLUS_DOCS_URL} failed: " + "status code = ${ENERGYPLUS_DOCS_DOWNLOAD_STATUS_CODE}, message = ${ENERGYPLUS_DOCS_DOWNLOAD_ERROR_MSG}" + ) + endif() + + file(MD5 "${ENERGYPLUS_DOCS_ARCHIVE_PATH}" ENERGYPLUS_DOCS_HASH) + if(NOT "${ENERGYPLUS_DOCS_HASH}" MATCHES "${ENERGYPLUS_DOCS_EXPECTED_MD5}") + message(FATAL_ERROR + "Download of EnergyPlus docs seemed to have worked, but archive md5sum HASH mismatch\n" + " for file: ${ENERGYPLUS_DOCS_ARCHIVE_PATH}\n" + " from URL: ${ENERGYPLUS_DOCS_URL}\n" + " expected hash: [${ENERGYPLUS_DOCS_EXPECTED_MD5}]\n" + " actual hash: [${ENERGYPLUS_DOCS_HASH}]\n" + ) + endif() + + message(STATUS "Download of EnergyPlus docs succeeded") + set(ENERGYPLUS_DOCS_NEEDS_EXTRACT TRUE) +endif() + +if(ENERGYPLUS_DOCS_NEEDS_EXTRACT OR NOT EXISTS "${ENERGYPLUS_DOCS_DIR}") + if(EXISTS "${ENERGYPLUS_DOCS_DIR}") + file(REMOVE_RECURSE "${ENERGYPLUS_DOCS_DIR}") + endif() + + set(ENERGYPLUS_DOCS_EXTRACT_STAGING "${ENERGYPLUS_DOCS_ARCHIVE_DIR}/extracted") + file(REMOVE_RECURSE "${ENERGYPLUS_DOCS_EXTRACT_STAGING}") + file(MAKE_DIRECTORY "${ENERGYPLUS_DOCS_EXTRACT_STAGING}") + + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar xfz "${ENERGYPLUS_DOCS_ARCHIVE_PATH}" + WORKING_DIRECTORY "${ENERGYPLUS_DOCS_EXTRACT_STAGING}" + RESULT_VARIABLE ENERGYPLUS_DOCS_EXTRACT_RESULT + ) + if(NOT ENERGYPLUS_DOCS_EXTRACT_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to extract ${ENERGYPLUS_DOCS_ARCHIVE_PATH}") + endif() + + # The archive wraps everything in a single top-level directory; find it regardless of name. + file(GLOB ENERGYPLUS_DOCS_EXTRACTED_SUBDIRS LIST_DIRECTORIES TRUE "${ENERGYPLUS_DOCS_EXTRACT_STAGING}/*") + list(LENGTH ENERGYPLUS_DOCS_EXTRACTED_SUBDIRS ENERGYPLUS_DOCS_EXTRACTED_SUBDIRS_COUNT) + if(NOT ENERGYPLUS_DOCS_EXTRACTED_SUBDIRS_COUNT EQUAL 1) + message(FATAL_ERROR "Expected exactly one top-level directory in ${ENERGYPLUS_DOCS_ARCHIVE_NAME}, found ${ENERGYPLUS_DOCS_EXTRACTED_SUBDIRS_COUNT}") + endif() + list(GET ENERGYPLUS_DOCS_EXTRACTED_SUBDIRS 0 ENERGYPLUS_DOCS_EXTRACTED_SUBDIR) + + # Drop macOS AppleDouble resource-fork files left over from how the export was archived. + file(GLOB_RECURSE ENERGYPLUS_DOCS_APPLEDOUBLE_FILES "${ENERGYPLUS_DOCS_EXTRACTED_SUBDIR}/._*") + if(ENERGYPLUS_DOCS_APPLEDOUBLE_FILES) + file(REMOVE ${ENERGYPLUS_DOCS_APPLEDOUBLE_FILES}) + endif() + + get_filename_component(ENERGYPLUS_DOCS_DIR_PARENT "${ENERGYPLUS_DOCS_DIR}" DIRECTORY) + file(MAKE_DIRECTORY "${ENERGYPLUS_DOCS_DIR_PARENT}") + file(RENAME "${ENERGYPLUS_DOCS_EXTRACTED_SUBDIR}" "${ENERGYPLUS_DOCS_DIR}") + file(REMOVE_RECURSE "${ENERGYPLUS_DOCS_EXTRACT_STAGING}") + + message(STATUS "EnergyPlus docs extracted to ${ENERGYPLUS_DOCS_DIR}") +endif() diff --git a/scripts/check_doc_urls.py b/scripts/check_doc_urls.py new file mode 100644 index 000000000..a5c3f61ac --- /dev/null +++ b/scripts/check_doc_urls.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +""" +check_doc_urls.py - Verify the local EnergyPlus Input Output Reference doc links in OpenStudioApp source. + +Scans IddObjectDocUrl.hpp for page#anchor references, and checks that every referenced page +exists and every anchor referenced actually has a matching id= in that page's HTML. + +Usage: + python scripts/check_doc_urls.py [--repo-root PATH] [--docs-dir PATH] + +The documentation directory is auto-detected by searching for EnergyPlus/doc/input-output-reference +under --repo-root (covers build-tree layouts), or pass --docs-dir to point at it directly +(this is the same directory openstudio::energyPlusDocDirectory() resolves to at runtime). + +Exit codes: + 0 All URLs valid + 1 One or more broken/missing pages or anchors found + 2 Usage / dependency error +""" + +import argparse +import re +import sys +from collections import defaultdict +from html.parser import HTMLParser +from pathlib import Path + +# --------------------------------------------------------------------------- +# Files to scan and the regex pattern that extracts page#anchor fragments from them +# --------------------------------------------------------------------------- + +# Matches values in the IddObjectDocUrl.hpp urlMap and groupMap: +# {"OS:Something", "1.5-group-foo.html#anchor"}, +# {"OpenStudio Group Name", "1.5-group-foo.html"}, +IDDOBJECTDOCURL_PATTERN = re.compile( + r'"(?:OS:|OpenStudio |Solar |Electric |Energy |User |Python |Airflow)[^"]*"\s*,\s*"([^"]+\.html(?:#[^"]*)?)"' +) + +SOURCE_FILES = [ + "src/model_editor/IddObjectDocUrl.hpp", +] + +# Where to look for the extracted documentation, relative to repo root. +DOCS_DIR_GLOBS = [ + "EnergyPlus/doc/input-output-reference", + "build*/EnergyPlus/doc/input-output-reference", +] + + +# --------------------------------------------------------------------------- +# HTML parser that collects all id= attributes +# --------------------------------------------------------------------------- + +class AnchorCollector(HTMLParser): + def __init__(self): + super().__init__() + self.ids = set() + + def handle_starttag(self, tag, attrs): + for name, value in attrs: + if name == "id" and value: + self.ids.add(value) + + +# --------------------------------------------------------------------------- +# Docs directory detection +# --------------------------------------------------------------------------- + +def find_docs_dir(repo_root: Path) -> Path | None: + for pattern in DOCS_DIR_GLOBS: + matches = sorted(repo_root.glob(pattern)) + if matches: + return matches[0] + return None + + +# --------------------------------------------------------------------------- +# URL extraction +# --------------------------------------------------------------------------- + +def extract_fragments(repo_root: Path): + """ + Returns a dict: page_name -> list of (anchor_or_None, source_file, line_no) + """ + results = defaultdict(list) + + for rel_path in SOURCE_FILES: + src = repo_root / rel_path + if not src.exists(): + print(f" WARNING: {rel_path} not found, skipping", file=sys.stderr) + continue + + text = src.read_text(encoding="utf-8") + + for lineno, line in enumerate(text.splitlines(), 1): + for m in IDDOBJECTDOCURL_PATTERN.finditer(line): + fragment = m.group(1) + if "#" in fragment: + page_name, anchor = fragment.split("#", 1) + else: + page_name, anchor = fragment, None + results[page_name].append((anchor, rel_path, lineno)) + + return results + + +# --------------------------------------------------------------------------- +# Page reading with simple cache +# --------------------------------------------------------------------------- + +def read_anchors(path: Path) -> set | None: + """Read a local HTML file and return the set of id= values, or None on error.""" + try: + html = path.read_text(encoding="utf-8", errors="replace") + parser = AnchorCollector() + parser.feed(html) + return parser.ids + except OSError as e: + print(f" ERROR reading {path}: {e}", file=sys.stderr) + return None + + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--repo-root", + default=".", + help="Path to the OpenStudioApplication repo root (default: current directory)", + ) + parser.add_argument( + "--docs-dir", + help="Path to the extracted EnergyPlus Input Output Reference HTML (default: auto-detect under --repo-root)", + ) + args = parser.parse_args() + + repo_root = Path(args.repo_root).resolve() + print(f"Scanning repo: {repo_root}") + + docs_dir = Path(args.docs_dir).resolve() if args.docs_dir else find_docs_dir(repo_root) + if docs_dir is None or not docs_dir.is_dir(): + print( + "ERROR: Could not find the EnergyPlus docs directory " + f"(looked for {DOCS_DIR_GLOBS} under {repo_root}).\n" + "Configure/build the project first, or pass --docs-dir explicitly.", + file=sys.stderr, + ) + sys.exit(2) + + print(f"Using EnergyPlus docs directory: {docs_dir}") + + fragments = extract_fragments(repo_root) + if not fragments: + print("No URLs found — check SOURCE_FILES list.", file=sys.stderr) + sys.exit(2) + + print(f"\nFound {sum(len(v) for v in fragments.values())} URL references across {len(fragments)} unique pages.\n") + + failures = [] + page_cache = {} + + for page_name in sorted(fragments): + page_path = docs_dir / page_name + print(f"Checking: {page_path}") + if page_name not in page_cache: + if not page_path.exists(): + page_cache[page_name] = None + else: + page_cache[page_name] = read_anchors(page_path) + + page_ids = page_cache[page_name] + + for anchor, src_file, lineno in fragments[page_name]: + if page_ids is None: + failures.append((src_file, lineno, page_name, anchor, "page not found")) + elif anchor and anchor not in page_ids: + failures.append((src_file, lineno, page_name, anchor, "anchor not found in page")) + else: + status = "OK" if anchor else "OK (no anchor)" + print(f" {status}: #{anchor or ''}") + + print() + if failures: + print(f"FAILURES ({len(failures)}):") + for src_file, lineno, page_name, anchor, reason in failures: + print(f" {src_file}:{lineno} #{anchor} -> {reason}") + print(f" {page_name}#{anchor or ''}") + sys.exit(1) + else: + print("All URLs OK.") + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/src/model_editor/IddObjectDocUrl.hpp b/src/model_editor/IddObjectDocUrl.hpp new file mode 100644 index 000000000..4cbf2aae3 --- /dev/null +++ b/src/model_editor/IddObjectDocUrl.hpp @@ -0,0 +1,530 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) OpenStudio Coalition and other contributors. +* See also https://openstudiocoalition.org/about/software_license/ +***********************************************************************************************************************/ + +#ifndef MODELEDITOR_IDDOBJECTDOCURL_HPP +#define MODELEDITOR_IDDOBJECTDOCURL_HPP + +#include +#include +#include +#include + +#include "../utilities/OpenStudioApplicationPathHelpers.hpp" +#include + +// Returns the local EnergyPlus Input Output Reference URL for the given OS IDD type name +// (e.g. "OS:ThermalZone"), or an empty string if none is known. +// Points at the documentation bundled alongside the app (see openstudio::energyPlusDocDirectory()). +inline QString iddObjectDocUrl(const QString& iddTypeName) { + static const QString base = + QUrl::fromLocalFile(QString::fromStdString(openstudio::toString(openstudio::energyPlusDocDirectory())) + "/").toString(); + + // clang-format off + static const QHash urlMap{ + // Simulation Parameters + {"OS:SimulationControl", "1.5-group-simulation-parameters.html#simulationcontrol"}, + {"OS:Building", "1.5-group-simulation-parameters.html#building"}, + {"OS:Timestep", "1.5-group-simulation-parameters.html#timestep"}, + {"OS:RunPeriod", "1.7-group-location-climate-weather-file-access.html#runperiod"}, + {"OS:ShadowCalculation", "1.5-group-simulation-parameters.html#shadowcalculation"}, + {"OS:SurfaceConvectionAlgorithm:Inside", "1.5-group-simulation-parameters.html#surfaceconvectionalgorithminside"}, + {"OS:SurfaceConvectionAlgorithm:Outside", "1.5-group-simulation-parameters.html#surfaceconvectionalgorithmoutside"}, + {"OS:HeatBalanceAlgorithm", "1.5-group-simulation-parameters.html#heatbalancealgorithm"}, + {"OS:ZoneAirHeatBalanceAlgorithm", "1.5-group-simulation-parameters.html#zoneairheatbalancealgorithm"}, + {"OS:ZoneAirContaminantBalance", "1.5-group-simulation-parameters.html#zoneaircontaminantbalance"}, + {"OS:ConvergenceLimits", "1.5-group-simulation-parameters.html#convergencelimits"}, + {"OS:ZoneCapacitanceMultiplier:ResearchSpecial", "1.5-group-simulation-parameters.html#zonecapacitancemultiplierresearchspecial"}, + + // Location and Climate + {"OS:Site:Location", "1.7-group-location-climate-weather-file-access.html#sitelocation"}, + {"OS:Site:GroundTemperature:Undisturbed:KusudaAchenbach", "1.7-group-location-climate-weather-file-access.html#sitegroundtemperatureundisturbedkusudaachenbach"}, + {"OS:Site:GroundTemperature:BuildingSurface", "1.7-group-location-climate-weather-file-access.html#sitegroundtemperaturebuildingsurface"}, + {"OS:Site:GroundTemperature:Shallow", "1.7-group-location-climate-weather-file-access.html#sitegroundtemperatureshallow"}, + {"OS:Site:GroundTemperature:Deep", "1.7-group-location-climate-weather-file-access.html#sitegroundtemperaturedeep"}, + {"OS:Site:GroundTemperature:FCfactorMethod", "1.7-group-location-climate-weather-file-access.html#sitegroundtemperaturefcfactormethod"}, + {"OS:Site:GroundReflectance", "1.7-group-location-climate-weather-file-access.html#sitegroundreflectance"}, + {"OS:Site:WaterMainsTemperature", "1.7-group-location-climate-weather-file-access.html#sitewatermainstemperature"}, + {"OS:SizingPeriod:DesignDay", "1.7-group-location-climate-weather-file-access.html#sizingperioddesignday"}, + {"OS:DesignDay", "1.7-group-location-climate-weather-file-access.html#sizingperioddesignday"}, + {"OS:RunPeriodControl:DaylightSavingTime", "1.7-group-location-climate-weather-file-access.html#runperiodcontroldaylightsavingtime"}, + + // Schedules + {"OS:ScheduleTypeLimits", "1.8-group-schedules.html#scheduletypelimits"}, + {"OS:Schedule:Compact", "1.8-group-schedules.html#schedulecompact"}, + {"OS:Schedule:Constant", "1.8-group-schedules.html#scheduleconstant"}, + {"OS:Schedule:Day", "1.8-group-schedules.html#day-schedules"}, + {"OS:Schedule:Day:Hourly", "1.8-group-schedules.html#scheduledayhourly"}, + {"OS:Schedule:Day:Interval", "1.8-group-schedules.html#scheduledayinterval"}, + {"OS:Schedule:Week:Daily", "1.8-group-schedules.html#scheduleweekdaily"}, + {"OS:Schedule:Week:Compact", "1.8-group-schedules.html#scheduleweekcompact"}, + {"OS:Schedule:Year", "1.8-group-schedules.html#scheduleyear"}, + {"OS:Schedule:Ruleset", "1.8-group-schedules.html#scheduleyear"}, + {"OS:Schedule:File", "1.8-group-schedules.html#schedulefile"}, + + // Surface Construction Elements + {"OS:Material", "1.9-group-surface-construction-elements.html#material"}, + {"OS:Material:NoMass", "1.9-group-surface-construction-elements.html#materialnomass"}, + {"OS:Material:AirGap", "1.9-group-surface-construction-elements.html#materialairgap"}, + {"OS:Material:InfraredTransparent", "1.9-group-surface-construction-elements.html#materialinfraredtransparent"}, + {"OS:Material:RoofVegetation", "1.9-group-surface-construction-elements.html#materialroofvegetation"}, + {"OS:WindowMaterial:Glazing", "1.9-group-surface-construction-elements.html#windowmaterialglazing"}, + {"OS:WindowMaterial:Gas", "1.9-group-surface-construction-elements.html#windowmaterialgas"}, + {"OS:WindowMaterial:GasMixture", "1.9-group-surface-construction-elements.html#windowmaterialgasmixture"}, + {"OS:WindowMaterial:SimpleGlazingSystem", "1.9-group-surface-construction-elements.html#windowmaterialsimpleglazingsystem"}, + {"OS:WindowMaterial:Blind", "1.9-group-surface-construction-elements.html#windowmaterialblind"}, + {"OS:WindowMaterial:Screen", "1.9-group-surface-construction-elements.html#windowmaterialscreen"}, + {"OS:WindowMaterial:Shade", "1.9-group-surface-construction-elements.html#windowmaterialshade"}, + {"OS:Construction", "1.9-group-surface-construction-elements.html#construction-000"}, + {"OS:Construction:InternalSource", "1.9-group-surface-construction-elements.html#constructioninternalsource"}, + {"OS:Construction:WindowDataFile", "1.9-group-surface-construction-elements.html#constructionwindowdatafile"}, + {"OS:Construction:AirBoundary", "1.9-group-surface-construction-elements.html#constructionairboundary"}, + {"OS:Construction:CfactorUndergroundWall", "1.9-group-surface-construction-elements.html#constructioncfactorundergroundwall"}, + {"OS:Construction:FfactorGroundFloor", "1.9-group-surface-construction-elements.html#constructionffactorgroundfloor"}, + {"OS:WindowMaterial:DaylightRedirectionDevice", "1.9-group-surface-construction-elements.html#windowmaterialblind"}, + {"OS:WindowMaterial:Glazing:RefractionExtinctionMethod", "1.9-group-surface-construction-elements.html#windowmaterialglazingrefractionextinctionmethod"}, + {"OS:WindowProperty:FrameAndDivider", "1.10-group-thermal-zone-descriptiongeometry.html#windowpropertyframeanddivider"}, + + // Thermal Zone Description and Geometry + {"OS:ThermalZone", "1.10-group-thermal-zone-descriptiongeometry.html"}, + {"OS:Space", "1.10-group-thermal-zone-descriptiongeometry.html"}, + {"OS:SpaceType", "1.10-group-thermal-zone-descriptiongeometry.html"}, + {"OS:BuildingStory", "1.10-group-thermal-zone-descriptiongeometry.html"}, + {"OS:Surface", "1.10-group-thermal-zone-descriptiongeometry.html#buildingsurfacedetailed"}, + {"OS:SubSurface", "1.10-group-thermal-zone-descriptiongeometry.html#fenestrationsurfacedetailed"}, + {"OS:ShadingControl", "1.10-group-thermal-zone-descriptiongeometry.html#windowpropertyshadingcontrol"}, + {"OS:InteriorPartitionSurfaceGroup", "1.10-group-thermal-zone-descriptiongeometry.html"}, + + // Daylighting + {"OS:Daylighting:Control", "1.15-group-daylighting.html#daylightingcontrols-000"}, + {"OS:DaylightingDevice:Shelf", "1.15-group-daylighting.html#daylightingdeviceshelf"}, + {"OS:DaylightingDevice:Tubular", "1.15-group-daylighting.html#daylightingdevicetubular"}, + {"OS:DaylightingDevice:LightWell", "1.15-group-daylighting.html#daylightingdevicelightwell"}, + + // Internal Gains + {"OS:People", "1.14-group-internal-gains-people-lights-other-internal-zone-equipment.html#people"}, + {"OS:Lights", "1.14-group-internal-gains-people-lights-other-internal-zone-equipment.html#lights-000"}, + {"OS:ElectricEquipment", "1.14-group-internal-gains-people-lights-other-internal-zone-equipment.html#electricequipment"}, + {"OS:GasEquipment", "1.14-group-internal-gains-people-lights-other-internal-zone-equipment.html#gasequipment"}, + {"OS:HotWaterEquipment", "1.14-group-internal-gains-people-lights-other-internal-zone-equipment.html#hotwaterequipment"}, + {"OS:SteamEquipment", "1.14-group-internal-gains-people-lights-other-internal-zone-equipment.html#steamequipment"}, + {"OS:OtherEquipment", "1.14-group-internal-gains-people-lights-other-internal-zone-equipment.html#otherequipment"}, + {"OS:InternalMass", "1.10-group-thermal-zone-descriptiongeometry.html#internalmass"}, + {"OS:SwimmingPool:Indoor", "1.14-group-internal-gains-people-lights-other-internal-zone-equipment.html#swimmingpoolindoor"}, + + // Exterior Energy Use + {"OS:Exterior:Lights", "1.16-group-exterior-energy-use-equipment.html#exteriorlights"}, + {"OS:Exterior:FuelEquipment", "1.16-group-exterior-energy-use-equipment.html#exteriorfuelequipment"}, + {"OS:Exterior:WaterEquipment", "1.16-group-exterior-energy-use-equipment.html#exteriorwaterequipment"}, + // Older OpenStudio type names + {"OS:ExteriorLights", "1.16-group-exterior-energy-use-equipment.html#exteriorlights"}, + {"OS:ExteriorFuelEquipment", "1.16-group-exterior-energy-use-equipment.html#exteriorfuelequipment"}, + {"OS:ExteriorWaterEquipment", "1.16-group-exterior-energy-use-equipment.html#exteriorwaterequipment"}, + + // Zone Airflow + {"OS:SpaceInfiltration:DesignFlowRate", "1.17-group-airflow.html#zoneinfiltrationdesignflowrate"}, + {"OS:SpaceInfiltration:EffectiveLeakageArea", "1.17-group-airflow.html#zoneinfiltrationeffectiveleakagearea"}, + {"OS:ZoneVentilation:DesignFlowRate", "1.17-group-airflow.html#zoneventilationdesignflowrate"}, + {"OS:ZoneVentilation:WindandStackOpenArea", "1.17-group-airflow.html#zoneventilationwindandstackopenarea"}, + {"OS:ZoneMixing", "1.17-group-airflow.html#zonemixing"}, + {"OS:ZoneCrossMixing", "1.17-group-airflow.html#zonecrossmixing"}, + + // Design Objects / Sizing + {"OS:DesignSpecification:OutdoorAir", "1.18-group-design-objects.html#designspecificationoutdoorair"}, + {"OS:DesignSpecification:ZoneAirDistribution", "1.18-group-design-objects.html#designspecificationzoneairdistribution"}, + {"OS:Sizing:Zone", "1.18-group-design-objects.html#sizingzone"}, + {"OS:Sizing:System", "1.18-group-design-objects.html#sizingsystem"}, + {"OS:Sizing:Plant", "1.18-group-design-objects.html#sizingplant"}, + {"OS:Sizing:Parameters", "1.18-group-design-objects.html#sizingparameters"}, + + // Zone HVAC Controls and Thermostats + {"OS:ThermostatSetpoint:DualSetpoint", "1.36-group---zone-controls---thermostats-and-humidistats.html#thermostatsetpointdualsetpoint"}, + {"OS:ThermostatSetpoint:SingleHeating", "1.36-group---zone-controls---thermostats-and-humidistats.html#thermostatsetpointsingleheating"}, + {"OS:ThermostatSetpoint:SingleCooling", "1.36-group---zone-controls---thermostats-and-humidistats.html#thermostatsetpointsinglecooling"}, + {"OS:ZoneControl:Thermostat", "1.36-group---zone-controls---thermostats-and-humidistats.html#zonecontrolthermostat"}, + {"OS:ZoneControl:ContaminantController", "1.36-group---zone-controls---thermostats-and-humidistats.html#zonecontrolcontaminantcontroller"}, + {"OS:ZoneControl:Humidistat", "1.36-group---zone-controls---thermostats-and-humidistats.html#zonecontrolhumidistat"}, + {"OS:ZoneControl:Thermostat:StagedDualSetpoint", "1.36-group---zone-controls---thermostats-and-humidistats.html#zonecontrolthermostatstageddualsetpoint"}, + + // Zone HVAC Equipment Connections + {"OS:ZoneHVAC:EquipmentList", "1.29-group---zone-equipment.html#zonehvacequipmentlist"}, + {"OS:ZoneHVAC:EquipmentConnections", "1.29-group---zone-equipment.html#zonehvacequipmentconnections"}, + + // Zone HVAC Forced Air Units + {"OS:ZoneHVAC:PackagedTerminalAirConditioner", "1.31-group-zone-forced-air-units.html#zonehvacpackagedterminalairconditioner"}, + {"OS:ZoneHVAC:PackagedTerminalHeatPump", "1.31-group-zone-forced-air-units.html#zonehvacpackagedterminalheatpump"}, + {"OS:ZoneHVAC:WaterToAirHeatPump", "1.31-group-zone-forced-air-units.html#zonehvacwatertoairheatpump"}, + {"OS:ZoneHVAC:FourPipeFanCoil", "1.31-group-zone-forced-air-units.html#zonehvacfourpipefancoil"}, + {"OS:ZoneHVAC:UnitVentilator", "1.31-group-zone-forced-air-units.html#zonehvacunitventilator"}, + {"OS:ZoneHVAC:UnitHeater", "1.31-group-zone-forced-air-units.html#zonehvacunitheater"}, + {"OS:ZoneHVAC:IdealLoadsAirSystem", "1.31-group-zone-forced-air-units.html#zonehvacidealloadsairsystem"}, + {"OS:ZoneHVAC:EnergyRecoveryVentilator", "1.31-group-zone-forced-air-units.html#zonehvacenergyrecoveryventilator"}, + {"OS:ZoneHVAC:Dehumidifier:DX", "1.31-group-zone-forced-air-units.html#zonehvacdehumidifierdx"}, + {"OS:ZoneHVAC:EvaporativeCoolerUnit", "1.31-group-zone-forced-air-units.html#zonehvacevaporativecoolerunit"}, + {"OS:ZoneHVAC:Baseboard:Convective:Water", "1.34-group-radiative-convective-units.html#zonehvacbaseboardconvectivewater"}, + {"OS:ZoneHVAC:Baseboard:Convective:Electric", "1.34-group-radiative-convective-units.html#zonehvacbaseboardconvectiveelectric"}, + {"OS:ZoneHVAC:TerminalUnit:VariableRefrigerantFlow", "1.31-group-zone-forced-air-units.html#zonehvacterminalunitvariablerefrigerantflow"}, + + // Zone HVAC Radiative / Cooling Panels + {"OS:ZoneHVAC:CoolingPanel:RadiantConvective:Water", "1.34-group-radiative-convective-units.html#zonehvaccoolingpanelradiantconvectivewater"}, + {"OS:ZoneHVAC:Baseboard:RadiantConvective:Water", "1.34-group-radiative-convective-units.html#zonehvacbaseboardradiantconvectivewater"}, + {"OS:ZoneHVAC:Baseboard:RadiantConvective:Electric","1.34-group-radiative-convective-units.html#zonehvacbaseboardradiantconvectiveelectric"}, + {"OS:ZoneHVAC:HighTemperatureRadiant", "1.34-group-radiative-convective-units.html#zonehvachightemperatureradiant"}, + {"OS:ZoneHVAC:LowTemperatureRadiant:VariableFlow", "1.34-group-radiative-convective-units.html#zonehvaclowtemperatureradiantvariableflow"}, + {"OS:ZoneHVAC:LowTemperatureRadiant:ConstantFlow", "1.34-group-radiative-convective-units.html#zonehvaclowtemperatureradiantconstantflow"}, + {"OS:ZoneHVAC:LowTemperatureRadiant:Electric", "1.34-group-radiative-convective-units.html#zonehvaclowtemperatureradiantelectric"}, + + // Air Terminals + {"OS:AirTerminal:SingleDuct:VAV:Reheat", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductvavreheat"}, + {"OS:AirTerminal:SingleDuct:VAV:NoReheat", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductvavnoreheat"}, + {"OS:AirTerminal:SingleDuct:VAV:HeatAndCool:Reheat", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductvavheatandcoolreheat"}, + {"OS:AirTerminal:SingleDuct:VAV:HeatAndCool:NoReheat", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductvavheatandcoolnoreheat"}, + {"OS:AirTerminal:SingleDuct:ConstantVolume:Reheat", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductconstantvolumereheat"}, + {"OS:AirTerminal:SingleDuct:ConstantVolume:NoReheat", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductconstantvolumenoreheat"}, + {"OS:AirTerminal:SingleDuct:ConstantVolume:CooledBeam", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductconstantvolumecooledbeam"}, + {"OS:AirTerminal:SingleDuct:ConstantVolume:FourPipeBeam", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductconstantvolumefourpipebeam"}, + {"OS:AirTerminal:SingleDuct:ParallelPIU:Reheat", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductparallelpiureheat"}, + {"OS:AirTerminal:SingleDuct:SeriesPIU:Reheat", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductseriespiureheat"}, + {"OS:AirTerminal:SingleDuct:InletSideMixer", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductmixer"}, + {"OS:AirTerminal:DualDuct:VAV", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminaldualductvav"}, + {"OS:AirTerminal:DualDuct:VAV:OutdoorAir", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminaldualductvavoutdoorair"}, + {"OS:AirTerminal:DualDuct:ConstantVolume", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminaldualductconstantvolume"}, + {"OS:AirTerminal:FourPipeInduction", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductconstantvolumefourpipeinduction"}, + {"OS:AirTerminal:SingleDuct:ConstantVolume:FourPipeInduction", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductconstantvolumefourpipeinduction"}, + + // Fans + {"OS:Fan:ConstantVolume", "1.43-group-fans.html#fanconstantvolume"}, + {"OS:Fan:VariableVolume", "1.43-group-fans.html#fanvariablevolume"}, + {"OS:Fan:OnOff", "1.43-group-fans.html#fanonoff"}, + {"OS:Fan:ZoneExhaust", "1.43-group-fans.html#fanzoneexhaust"}, + {"OS:Fan:SystemModel", "1.43-group-fans.html#fansystemmodel"}, + {"OS:Fan:ComponentModel", "1.43-group-fans.html#fancomponentmodel"}, + + // Coils - Cooling + {"OS:Coil:Cooling:DX:SingleSpeed", "1.42-group-heating-and-cooling-coils.html#coilcoolingdxsinglespeed"}, + {"OS:Coil:Cooling:DX:TwoSpeed", "1.42-group-heating-and-cooling-coils.html#coilcoolingdxtwospeed"}, + {"OS:Coil:Cooling:DX:MultiSpeed", "1.42-group-heating-and-cooling-coils.html#coilcoolingdxmultispeed"}, + {"OS:Coil:Cooling:DX:VariableSpeed", "1.42-group-heating-and-cooling-coils.html#coilcoolingdxvariablespeed"}, + {"OS:Coil:Cooling:DX:VariableRefrigerantFlow", "1.42-group-heating-and-cooling-coils.html#coilcoolingdxvariablerefrigerantflow"}, + {"OS:Coil:Cooling:DX:VariableRefrigerantFlow:FluidTemperatureControl", "1.42-group-heating-and-cooling-coils.html#coilcoolingdxvariablerefrigerantflowfluidtemperaturecontrol"}, + {"OS:Coil:Cooling:Water", "1.42-group-heating-and-cooling-coils.html#coilcoolingwater"}, + {"OS:Coil:Cooling:Water:DetailedGeometry", "1.42-group-heating-and-cooling-coils.html#coilcoolingwaterdetailedgeometry"}, + {"OS:Coil:Cooling:WaterToAirHeatPump:EquationFit", "1.42-group-heating-and-cooling-coils.html#coilcoolingwatertoairheatpumpequationfit"}, + {"OS:Coil:Cooling:WaterToAirHeatPump:VariableSpeedEquationFit", "1.42-group-heating-and-cooling-coils.html#coilcoolingwatertoairheatpumpvariablespeedequationfit"}, + {"OS:Coil:Cooling:FourPipeBeam", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductconstantvolumefourpipebeam"}, + {"OS:Coil:Cooling:DX:TwoStageWithHumidityControlMode", "1.42-group-heating-and-cooling-coils.html#coilcoolingdxtwostagewithhumiditycontrolmode"}, + {"OS:Coil:Cooling:DX:MultiSpeed:StageData", "1.42-group-heating-and-cooling-coils.html#coilcoolingdxmultispeed"}, + {"OS:Coil:Cooling:Water:Panel:Radiant", "1.34-group-radiative-convective-units.html#zonehvaccoolingpanelradiantconvectivewater"}, + {"OS:Coil:Cooling:LowTemperatureRadiant:ConstantFlow", "1.34-group-radiative-convective-units.html#zonehvaclowtemperatureradiantconstantflow"}, + {"OS:Coil:Cooling:LowTemperatureRadiant:VariableFlow", "1.34-group-radiative-convective-units.html#zonehvaclowtemperatureradiantvariableflow"}, + {"OS:CoilPerformance:DX:Cooling", "1.42-group-heating-and-cooling-coils.html#coilperformancedxcooling"}, + + // Coils - Heating + {"OS:Coil:Heating:Gas", "1.42-group-heating-and-cooling-coils.html#coilheatinggas-000"}, + {"OS:Coil:Heating:Fuel", "1.42-group-heating-and-cooling-coils.html#coilheatinggas-000"}, + {"OS:Coil:Heating:Electric", "1.42-group-heating-and-cooling-coils.html#coilheatingelectric"}, + {"OS:Coil:Heating:Water", "1.42-group-heating-and-cooling-coils.html#coilheatingwater"}, + {"OS:Coil:Heating:DX:SingleSpeed", "1.42-group-heating-and-cooling-coils.html#coilheatingdxsinglespeed"}, + {"OS:Coil:Heating:DX:MultiSpeed", "1.42-group-heating-and-cooling-coils.html#coilheatingdxmultispeed"}, + {"OS:Coil:Heating:DX:VariableSpeed", "1.42-group-heating-and-cooling-coils.html#coilheatingdxvariablespeed"}, + {"OS:Coil:Heating:DX:VariableRefrigerantFlow", "1.42-group-heating-and-cooling-coils.html#coilheatingdxvariablerefrigerantflow"}, + {"OS:Coil:Heating:DX:VariableRefrigerantFlow:FluidTemperatureControl", "1.42-group-heating-and-cooling-coils.html#coilheatingdxvariablerefrigerantflowfluidtemperaturecontrol"}, + {"OS:Coil:Heating:WaterToAirHeatPump:EquationFit", "1.42-group-heating-and-cooling-coils.html#coilheatingwatertoairheatpumpequationfit"}, + {"OS:Coil:Heating:WaterToAirHeatPump:VariableSpeedEquationFit", "1.42-group-heating-and-cooling-coils.html#coilheatingwatertoairheatpumpvariablespeedequationfit"}, + {"OS:Coil:Heating:Desuperheater", "1.42-group-heating-and-cooling-coils.html#coilheatingdesuperheater"}, + {"OS:Coil:Heating:FourPipeBeam", "1.30-group-zone-HVAC-air-loop-terminal-units.html#airterminalsingleductconstantvolumefourpipebeam"}, + {"OS:Coil:Heating:Gas:MultiStage", "1.42-group-heating-and-cooling-coils.html#coilheatinggasmultistage"}, + {"OS:Coil:Heating:Gas:MultiStage:StageData", "1.42-group-heating-and-cooling-coils.html#coilheatinggasmultistage"}, + {"OS:Coil:Heating:LowTemperatureRadiant:ConstantFlow", "1.34-group-radiative-convective-units.html#zonehvaclowtemperatureradiantconstantflow"}, + {"OS:Coil:Heating:LowTemperatureRadiant:VariableFlow", "1.34-group-radiative-convective-units.html#zonehvaclowtemperatureradiantvariableflowdesign"}, + {"OS:Coil:Heating:Water:Baseboard", "1.34-group-radiative-convective-units.html#zonehvacbaseboardconvectivewater"}, + {"OS:Coil:Heating:Water:Baseboard:Radiant", "1.34-group-radiative-convective-units.html#zonehvacbaseboardradiantconvectivewater"}, + {"OS:Coil:WaterHeating:AirToWaterHeatPump", "1.42-group-heating-and-cooling-coils.html#coilwaterheatingairtowaterheatpumppumped"}, + {"OS:Coil:WaterHeating:AirToWaterHeatPump:Pumped", "1.42-group-heating-and-cooling-coils.html#coilwaterheatingairtowaterheatpumppumped"}, + {"OS:Coil:WaterHeating:AirToWaterHeatPump:Wrapped", "1.42-group-heating-and-cooling-coils.html#coilwaterheatingairtowaterheatpumpwrapped"}, + {"OS:Coil:WaterHeating:Desuperheater", "1.42-group-heating-and-cooling-coils.html#coilwaterheatingdesuperheater"}, + + // Evaporative Coolers + {"OS:EvaporativeCooler:Direct:ResearchSpecial", "1.52-group-evaporative-coolers.html#evaporativecoolerdirectresearchspecial"}, + {"OS:EvaporativeCooler:Indirect:ResearchSpecial", "1.52-group-evaporative-coolers.html#evaporativecoolerindirectresearchspecial"}, + {"OS:EvaporativeCooler:Direct:CelDekPad", "1.52-group-evaporative-coolers.html#evaporativecoolerdirectceldekpad"}, + {"OS:EvaporativeCooler:Indirect:CelDekPad", "1.52-group-evaporative-coolers.html#evaporativecoolerindirectceldekpad"}, + {"OS:EvaporativeCooler:Indirect:WetCoil", "1.52-group-evaporative-coolers.html#evaporativecoolerindirectwetcoil"}, + + // Humidifiers + {"OS:Humidifier:Steam:Electric", "1.44-group-humidifiers-and-dehumidifiers.html#humidifiersteamelectric"}, + {"OS:Humidifier:Steam:Gas", "1.44-group-humidifiers-and-dehumidifiers.html#humidifiersteamgas"}, + + // Unitary Equipment + {"OS:AirLoopHVAC:UnitaryHeatPump:AirToAir", "1.32-group-unitary-equipment.html#airloophvacunitaryheatpumpairtoair"}, + {"OS:AirLoopHVAC:UnitaryHeatPump:AirToAir:MultiSpeed", "1.32-group-unitary-equipment.html#airloophvacunitaryheatpumpairtoairmultispeed"}, + {"OS:AirLoopHVAC:UnitarySystem", "1.32-group-unitary-equipment.html#airloophvacunitarysystem"}, + {"OS:AirLoopHVAC:UnitaryHeatCool:VAVChangeoverBypass", "1.32-group-unitary-equipment.html#airloophvacunitaryheatcoolvavchangeoverbypass"}, + + // Variable Refrigerant Flow + {"OS:AirConditioner:VariableRefrigerantFlow", "1.33-group-variable-refrigerant-flow-equipment.html#airconditionervariablerefrigerantflow"}, + {"OS:AirConditioner:VariableRefrigerantFlow:FluidTemperatureControl", "1.33-group-variable-refrigerant-flow-equipment.html#airconditionervariablerefrigerantflowfluidtemperaturecontrol"}, + {"OS:AirConditioner:VariableRefrigerantFlow:FluidTemperatureControl:HR", "1.33-group-variable-refrigerant-flow-equipment.html#airconditionervariablerefrigerantflowfluidtemperaturecontrolhr"}, + + // Air Distribution / AirLoop + {"OS:AirLoopHVAC", "1.26-group-air-distribution.html#group-air-distribution"}, + {"OS:AirLoopHVAC:OutdoorAirSystem", "1.26-group-air-distribution.html#airloophvacoutdoorairsystem"}, + {"OS:OutdoorAir:Mixer", "1.26-group-air-distribution.html#outdoorairmixer"}, + {"OS:Controller:OutdoorAir", "1.51-group-controllers.html#controlleroutdoorair"}, + {"OS:Controller:MechanicalVentilation", "1.51-group-controllers.html#controllermechanicalventilation"}, + {"OS:ZoneHVAC:EnergyRecoveryVentilator:Controller", "1.51-group-controllers.html#zonehvacenergyrecoveryventilatorcontroller"}, + {"OS:AirLoopHVAC:ZoneMixer", "1.37-group-air-path.html#airloophvaczonemixer"}, + {"OS:AirLoopHVAC:ZoneSplitter", "1.37-group-air-path.html#airloophvaczonesplitter"}, + {"OS:AirLoopHVAC:ReturnPlenum", "1.37-group-air-path.html#airloophvacreturnplenum"}, + {"OS:AirLoopHVAC:SupplyPlenum", "1.37-group-air-path.html#airloophvacsupplyplenum"}, + + // Setpoint Managers + {"OS:SetpointManager:Scheduled", "1.50-group-setpoint-managers.html#setpointmanagerscheduled"}, + {"OS:SetpointManager:Scheduled:DualSetpoint", "1.50-group-setpoint-managers.html#setpointmanagerscheduleddualsetpoint"}, + {"OS:SetpointManager:MixedAir", "1.50-group-setpoint-managers.html#setpointmanagermixedair"}, + {"OS:SetpointManager:OutdoorAirReset", "1.50-group-setpoint-managers.html#setpointmanageroutdoorairreset"}, + {"OS:SetpointManager:SingleZone:Reheat", "1.50-group-setpoint-managers.html#setpointmanagersinglezonereheat"}, + {"OS:SetpointManager:SingleZone:Heating", "1.50-group-setpoint-managers.html#setpointmanagersinglezoneheating"}, + {"OS:SetpointManager:SingleZone:Cooling", "1.50-group-setpoint-managers.html#setpointmanagersinglezonecooling"}, + {"OS:SetpointManager:Warmest", "1.50-group-setpoint-managers.html#setpointmanagerwarmest"}, + {"OS:SetpointManager:Coldest", "1.50-group-setpoint-managers.html#setpointmanagercoldest"}, + {"OS:SetpointManager:FollowOutdoorAirTemperature", "1.50-group-setpoint-managers.html#setpointmanagerfollowoutdoorairtemperature"}, + {"OS:SetpointManager:FollowGroundTemperature", "1.50-group-setpoint-managers.html#setpointmanagerfollowgroundtemperature"}, + {"OS:SetpointManager:CondenserEnteringReset", "1.50-group-setpoint-managers.html#setpointmanagercondenserenteringreset"}, + {"OS:SetpointManager:WarmestTemperatureFlow", "1.50-group-setpoint-managers.html#setpointmanagerwarmesttemperatureflow"}, + {"OS:SetpointManager:OutdoorAirPretreat", "1.50-group-setpoint-managers.html#setpointmanageroutdoorairpretreat"}, + {"OS:SetpointManager:MultiZone:Heating:Average", "1.50-group-setpoint-managers.html#setpointmanagermultizoneheatingaverage"}, + {"OS:SetpointManager:MultiZone:Cooling:Average", "1.50-group-setpoint-managers.html#setpointmanagermultizonecoolingaverage"}, + {"OS:SetpointManager:FollowSystemNodeTemperature", "1.50-group-setpoint-managers.html#setpointmanagerfollowsystemnodetemperature"}, + {"OS:SetpointManager:MultiZone:Humidity:Maximum", "1.50-group-setpoint-managers.html#setpointmanagermultizonehumiditymaximum"}, + {"OS:SetpointManager:MultiZone:Humidity:Minimum", "1.50-group-setpoint-managers.html#setpointmanagermultizonehumidityminimum"}, + {"OS:SetpointManager:MultiZone:MaximumHumidity:Average", "1.50-group-setpoint-managers.html#setpointmanagermultizonemaximumhumidityaverage"}, + {"OS:SetpointManager:MultiZone:MinimumHumidity:Average", "1.50-group-setpoint-managers.html#setpointmanagermultizoneminimumhumidityaverage"}, + {"OS:SetpointManager:SingleZone:Humidity:Maximum", "1.50-group-setpoint-managers.html#setpointmanagersinglezonehumiditymaximum"}, + {"OS:SetpointManager:SingleZone:Humidity:Minimum", "1.50-group-setpoint-managers.html#setpointmanagersinglezonehumidityminimum"}, + {"OS:SetpointManager:SingleZone:OneStageCooling", "1.50-group-setpoint-managers.html#setpointmanagersinglezoneonestagecooling"}, + {"OS:SetpointManager:SingleZone:OneStageHeating", "1.50-group-setpoint-managers.html#setpointmanagersinglezoneonestageheating"}, + {"OS:SetpointManager:SystemNodeReset:Humidity", "1.50-group-setpoint-managers.html#setpointmanagersystemnoderesethumidity"}, + {"OS:SetpointManager:SystemNodeReset:Temperature", "1.50-group-setpoint-managers.html#setpointmanagersystemnoderesettemperature"}, + + // Pumps + {"OS:Pump:VariableSpeed", "1.40-group-pumps.html#pumpvariablespeed"}, + {"OS:Pump:ConstantSpeed", "1.40-group-pumps.html#pumpconstantspeed"}, + {"OS:HeaderedPumps:VariableSpeed", "1.40-group-pumps.html#headeredpumpsvariablespeed"}, + {"OS:HeaderedPumps:ConstantSpeed", "1.40-group-pumps.html#headeredpumpsconstantspeed"}, + + // Solar Collectors + {"OS:SolarCollector:FlatPlate:Water", "1.39-group-solar-collectors.html#solarcollectorflatplatewater"}, + {"OS:SolarCollector:IntegralCollectorStorage", "1.39-group-solar-collectors.html#solarcollectorintegralcollectorstorage"}, + {"OS:SolarCollector:UnglazedTranspired", "1.39-group-solar-collectors.html#solarcollectorunglazedtranspired"}, + {"OS:SolarCollector:FlatPlate:PhotovoltaicThermal", "1.39-group-solar-collectors.html#solarcollectorflatplatephotovoltaicthermal"}, + {"OS:SolarCollectorPerformance:FlatPlate", "1.39-group-solar-collectors.html#solarcollectorperformanceflatplate"}, + {"OS:SolarCollectorPerformance:IntegralCollectorStorage", "1.39-group-solar-collectors.html#solarcollectorperformanceintegralcollectorstorage"}, + + // Plant Heating and Cooling Equipment + {"OS:Boiler:HotWater", "1.23-group-plant-equipment.html#boilerhotwater"}, + {"OS:Boiler:Steam", "1.23-group-plant-equipment.html#boilersteam"}, + {"OS:Chiller:Electric:EIR", "1.23-group-plant-equipment.html#chillerelectriceir"}, + {"OS:Chiller:Electric:ReformulatedEIR", "1.23-group-plant-equipment.html#chillerelectricreformulatedeir"}, + {"OS:Chiller:Absorption", "1.23-group-plant-equipment.html#chillerabsorption"}, + {"OS:Chiller:Absorption:Indirect", "1.23-group-plant-equipment.html#chillerabsorptionindirect"}, + {"OS:Chiller:Absorption:Direct", "1.23-group-plant-equipment.html#chillerabsorption"}, + {"OS:ChillerHeater:Absorption:DirectFired", "1.23-group-plant-equipment.html#chillerheaterabsorptiondirectfired"}, + {"OS:DistrictCooling", "1.23-group-plant-equipment.html#districtcooling"}, + {"OS:DistrictHeating", "1.23-group-plant-equipment.html#districtheating"}, + {"OS:DistrictHeating:Water", "1.23-group-plant-equipment.html#districtheating"}, + {"OS:HeatPump:PlantLoop:EIR:Cooling", "1.23-group-plant-equipment.html#plhp_eir_cooling"}, + {"OS:HeatPump:PlantLoop:EIR:Heating", "1.23-group-plant-equipment.html#plhp_eir_heating"}, + {"OS:HeatPump:WaterToWater:EquationFit:Cooling", "1.23-group-plant-equipment.html#heatpumpwatertowaterequationfitcooling"}, + {"OS:HeatPump:WaterToWater:EquationFit:Heating", "1.23-group-plant-equipment.html#heatpumpwatertowaterequationfitheating"}, + {"OS:HeatPump:AirToWater:FuelFired:Cooling", "1.23-group-plant-equipment.html#plhp_fuelfired"}, + {"OS:HeatPump:AirToWater:FuelFired:Heating", "1.23-group-plant-equipment.html#plhp_fuelfired"}, + {"OS:CentralHeatPumpSystem", "1.23-group-plant-equipment.html#centralheatpumpsystem"}, + {"OS:CentralHeatPumpSystem:Module", "1.23-group-plant-equipment.html#centralheatpumpsystem"}, + {"OS:ChillerHeaterPerformance:Electric:EIR", "1.23-group-plant-equipment.html#chillerheaterperformancelectriceir"}, + {"OS:PlantComponent:TemperatureSource", "1.23-group-plant-equipment.html#plantcomponenttemperaturesource"}, + {"OS:PlantComponent:UserDefined", "1.48-group-user-defined-hvac-and-plant-component-models.html#plantcomponentuserdefined"}, + + // Cooling Towers and Fluid Coolers + {"OS:CoolingTower:SingleSpeed", "1.25-group-condenser-equipment.html#coolingtowersinglespeed"}, + {"OS:CoolingTower:TwoSpeed", "1.25-group-condenser-equipment.html#coolingtowertwospeed"}, + {"OS:CoolingTower:VariableSpeed", "1.25-group-condenser-equipment.html#coolingtowervariablespeed"}, + {"OS:CoolingTower:VariableSpeed:Merkel", "1.25-group-condenser-equipment.html#coolingtowervariablespeedmerkel"}, + {"OS:CoolingTowerPerformance:CoolTools", "1.25-group-condenser-equipment.html#coolingtowerperformancecooltools"}, + {"OS:CoolingTowerPerformance:YorkCalc", "1.25-group-condenser-equipment.html#coolingtowerperformanceyorkcalc"}, + {"OS:EvaporativeFluidCooler:SingleSpeed", "1.25-group-condenser-equipment.html#evaporativefluidcoolersinglespeed"}, + {"OS:EvaporativeFluidCooler:TwoSpeed", "1.25-group-condenser-equipment.html#evaporativefluidcoolertwospeed"}, + {"OS:FluidCooler:SingleSpeed", "1.25-group-condenser-equipment.html#fluidcoolersinglespeed"}, + {"OS:FluidCooler:TwoSpeed", "1.25-group-condenser-equipment.html#fluidcoolertwospeed"}, + + // Heat Recovery + {"OS:HeatExchanger:AirToAir:SensibleAndLatent", "1.53-group-heat-recovery.html#heatexchangerairtoairsensibleandlatent"}, + {"OS:HeatExchanger:FluidToFluid", "1.25-group-condenser-equipment.html#heatexchangerfluidtofluid"}, + {"OS:HeatExchanger:Desiccant:BalancedFlow", "1.53-group-heat-recovery.html#heatexchangerdesiccantbalancedflow"}, + + // Condenser Equipment and Ground Heat Exchangers + {"OS:GroundHeatExchanger:Vertical", "1.25-group-condenser-equipment.html#groundheatexchangersystem"}, + {"OS:GroundHeatExchanger:HorizontalTrench", "1.25-group-condenser-equipment.html#groundheatexchangerhorizontaltrench"}, + {"OS:GroundHeatExchanger:Slinky", "1.25-group-condenser-equipment.html#groundheatexchangerslinky"}, + + // Water Heaters and Thermal Storage + {"OS:WaterHeater:Mixed", "1.24-group-water-heaters.html#waterheatermixed"}, + {"OS:WaterHeater:Stratified", "1.24-group-water-heaters.html#waterheaterstratified"}, + {"OS:WaterHeater:HeatPump", "1.24-group-water-heaters.html#waterheaterheatpumppumpedcondenser"}, + {"OS:WaterHeater:HeatPump:PumpedCondenser", "1.24-group-water-heaters.html#waterheaterheatpumppumpedcondenser"}, + {"OS:WaterHeater:HeatPump:WrappedCondenser", "1.24-group-water-heaters.html#waterheaterheatpumpwrappedcondenser"}, + {"OS:WaterHeater:Sizing", "1.24-group-water-heaters.html#waterheatersizing"}, + {"OS:ThermalStorage:Ice:Detailed", "1.24-group-water-heaters.html#thermalstorageicedetailed"}, + {"OS:ThermalStorage:ChilledWater:Stratified", "1.24-group-water-heaters.html#thermalstoragechilledwaterstratified"}, + + // Water Systems + {"OS:WaterUse:Equipment", "1.56-group-water-systems.html#wateruseequipment"}, + {"OS:WaterUse:Connections", "1.56-group-water-systems.html#wateruseconnections"}, + + // Refrigeration + {"OS:Refrigeration:AirChiller", "1.35-group-refrigeration.html#refrigerationairchiller"}, + {"OS:Refrigeration:Case", "1.35-group-refrigeration.html#refrigerationcase"}, + {"OS:Refrigeration:Compressor", "1.35-group-refrigeration.html#refrigerationcompressor"}, + {"OS:Refrigeration:CompressorRack", "1.35-group-refrigeration.html#refrigerationcompressorrack"}, + {"OS:Refrigeration:Condenser:AirCooled", "1.35-group-refrigeration.html#refrigerationcondenseraircooled"}, + {"OS:Refrigeration:Condenser:Cascade", "1.35-group-refrigeration.html#refrigerationcondensercascade"}, + {"OS:Refrigeration:Condenser:EvaporativeCooled", "1.35-group-refrigeration.html#refrigerationcondenserevaporativecooled"}, + {"OS:Refrigeration:Condenser:WaterCooled", "1.35-group-refrigeration.html#refrigerationcondenserwatercooled"}, + {"OS:Refrigeration:DefrostCycleParameters", "1.35-group-refrigeration.html#refrigerationwalkin"}, + {"OS:Refrigeration:GasCooler:AirCooled", "1.35-group-refrigeration.html#refrigerationgascooleraircooled"}, + {"OS:Refrigeration:SecondarySystem", "1.35-group-refrigeration.html#refrigerationsecondarysystem"}, + {"OS:Refrigeration:Subcooler:LiquidSuction", "1.35-group-refrigeration.html#refrigerationsubcooler"}, + {"OS:Refrigeration:Subcooler:Mechanical", "1.35-group-refrigeration.html#refrigerationsubcooler"}, + {"OS:Refrigeration:System", "1.35-group-refrigeration.html#refrigerationsystem"}, + {"OS:Refrigeration:TranscriticalSystem", "1.35-group-refrigeration.html#refrigerationtranscriticalsystem"}, + {"OS:Refrigeration:WalkIn", "1.35-group-refrigeration.html#refrigerationwalkin"}, + {"OS:Refrigeration:WalkIn:ZoneBoundary", "1.35-group-refrigeration.html#refrigerationwalkin"}, + + // Node / Branch Management + {"OS:Pipe:Adiabatic", "1.20-group-node-branch-management.html#pipeadiabatic"}, + {"OS:Pipe:Indoor", "1.20-group-node-branch-management.html#pipeindoor"}, + {"OS:Pipe:Outdoor", "1.20-group-node-branch-management.html#pipeoutdoor"}, + {"OS:Duct", "1.20-group-node-branch-management.html#duct"}, + + // Plant / Condenser Control + {"OS:PlantLoop", "1.21-group-plant-condenser-loops.html#plantloop"}, + {"OS:CondenserLoop", "1.21-group-plant-condenser-loops.html#condenserloop"}, + {"OS:PlantEquipmentList", "1.22-group-plant-condenser-control.html#plantequipmentlist"}, + {"OS:PlantEquipmentOperation:CoolingLoad", "1.22-group-plant-condenser-control.html#plantequipmentoperationcoolingload"}, + {"OS:PlantEquipmentOperation:HeatingLoad", "1.22-group-plant-condenser-control.html#plantequipmentoperationheatingload"}, + {"OS:PlantEquipmentOperation:ComponentSetpoint", "1.22-group-plant-condenser-control.html#plantequipmentoperationcomponentsetpoint"}, + {"OS:PlantEquipmentOperation:OutdoorDryBulb", "1.22-group-plant-condenser-control.html#plantequipmentoperationoutdoordrybulb"}, + {"OS:PlantEquipmentOperation:OutdoorWetBulb", "1.22-group-plant-condenser-control.html#plantequipmentoperationoutdoorwetbulb"}, + {"OS:PlantEquipmentOperation:ThermalEnergyStorage", "1.22-group-plant-condenser-control.html#plantequipmentoperationthermalenergystorage"}, + {"OS:PlantEquipmentOperation:Uncontrolled", "1.22-group-plant-condenser-control.html#plantequipmentoperationuncontrolled"}, + {"OS:TemperingValve", "1.41-plant-condenser-flow-control.html#temperingvalve"}, + {"OS:LoadProfile:Plant", "1.38-group-non-zone-equipment.html#loadprofileplant"}, + {"OS:AvailabilityManagerAssignmentList", "1.26-group-air-distribution.html#availabilitymanagerassignmentlist"}, + {"OS:AvailabilityManager:Scheduled", "1.49-group-system-availability-managers.html#availabilitymanagerscheduled"}, + {"OS:AvailabilityManager:ScheduledOn", "1.49-group-system-availability-managers.html#availabilitymanagerscheduledon"}, + {"OS:AvailabilityManager:ScheduledOff", "1.49-group-system-availability-managers.html#availabilitymanagerscheduledoff"}, + {"OS:AvailabilityManager:NightCycle", "1.49-group-system-availability-managers.html#availabilitymanagernightcycle"}, + {"OS:AvailabilityManager:DifferentialThermostat", "1.49-group-system-availability-managers.html#availabilitymanagerdifferentialthermostat"}, + {"OS:AvailabilityManager:OptimumStart", "1.49-group-system-availability-managers.html#availabilitymanageroptimumstart"}, + {"OS:AvailabilityManager:NightVentilation", "1.49-group-system-availability-managers.html#availabilitymanagernightventilation"}, + {"OS:AvailabilityManager:HybridVentilation", "1.49-group-system-availability-managers.html#availabilitymanagerhybridventilation"}, + {"OS:AvailabilityManager:LowTemperatureTurnOn", "1.49-group-system-availability-managers.html#availabilitymanagerlowtemperatureturnon"}, + {"OS:AvailabilityManager:HighTemperatureTurnOff", "1.49-group-system-availability-managers.html#availabilitymanagerhightemperatureturnoff"}, + {"OS:AvailabilityManager:LowTemperatureTurnOff", "1.49-group-system-availability-managers.html#availabilitymanagerlowtemperatureturnoff"}, + {"OS:AvailabilityManager:HighTemperatureTurnOn", "1.49-group-system-availability-managers.html#availabilitymanagerhightemperatureturnon"}, + + // Energy Management System + {"OS:EnergyManagementSystem:Sensor", "1.45-group-energy-management-system-ems.html#energymanagementsystemsensor"}, + {"OS:EnergyManagementSystem:Actuator", "1.45-group-energy-management-system-ems.html#energymanagementsystemactuator"}, + {"OS:EnergyManagementSystem:Program", "1.45-group-energy-management-system-ems.html#energymanagementsystemprogram"}, + {"OS:EnergyManagementSystem:ProgramCallingManager", "1.45-group-energy-management-system-ems.html#energymanagementsystemprogramcallingmanager"}, + {"OS:EnergyManagementSystem:GlobalVariable", "1.45-group-energy-management-system-ems.html#energymanagementsystemglobalvariable"}, + {"OS:EnergyManagementSystem:OutputVariable", "1.45-group-energy-management-system-ems.html#energymanagementsystemoutputvariable"}, + {"OS:EnergyManagementSystem:TrendVariable", "1.45-group-energy-management-system-ems.html#energymanagementsystemtrendvariable"}, + {"OS:EnergyManagementSystem:InternalVariable", "1.45-group-energy-management-system-ems.html#energymanagementsysteminternalvariable"}, + {"OS:EnergyManagementSystem:ConstructionIndexVariable", "1.45-group-energy-management-system-ems.html#energymanagementsystemconstructionindexvariable"}, + + // Performance Curves + {"OS:Curve:Linear", "1.58-group---performance-curves.html#curvelinear"}, + {"OS:Curve:Quadratic", "1.58-group---performance-curves.html#curvequadratic"}, + {"OS:Curve:Cubic", "1.58-group---performance-curves.html#curvecubic"}, + {"OS:Curve:Quartic", "1.58-group---performance-curves.html#curvequartic"}, + {"OS:Curve:Exponent", "1.58-group---performance-curves.html#curveexponent"}, + {"OS:Curve:Bicubic", "1.58-group---performance-curves.html#curvebicubic"}, + {"OS:Curve:Biquadratic", "1.58-group---performance-curves.html#curvebiquadratic"}, + {"OS:Curve:QuadraticLinear", "1.58-group---performance-curves.html#curvequadraticlinear"}, + {"OS:Curve:CubicLinear", "1.58-group---performance-curves.html#curvecubiclinear"}, + {"OS:Curve:Triquadratic", "1.58-group---performance-curves.html#curvetriquadratic"}, + {"OS:Curve:FanPressureRise", "1.58-group---performance-curves.html#curvefanpressurerise"}, + {"OS:Curve:ExponentialDecay", "1.58-group---performance-curves.html#curveexponentialdecay"}, + {"OS:Curve:ExponentialSkewNormal", "1.58-group---performance-curves.html#curveexponentialskewnormal"}, + {"OS:Curve:Sigmoid", "1.58-group---performance-curves.html#curvesigmoid"}, + {"OS:Curve:QuadLinear", "1.58-group---performance-curves.html#curvequadlinear"}, + {"OS:Curve:QuintLinear", "1.58-group---performance-curves.html#curvequintlinear"}, + {"OS:Curve:RectangularHyperbola1", "1.58-group---performance-curves.html#curverectangularhyperbola1"}, + {"OS:Curve:RectangularHyperbola2", "1.58-group---performance-curves.html#curverectangularhyperbola2"}, + {"OS:Table:IndependentVariable", "1.59-group-performance-tables.html#tableindependentvariable"}, + {"OS:Table:IndependentVariableList", "1.59-group-performance-tables.html#tableindependentvariablelist"}, + {"OS:Table:Lookup", "1.59-group-performance-tables.html#tablelookup"}, + + // Output + {"OS:Output:Diagnostics", "1.5-group-simulation-parameters.html#outputdiagnostics"}, + {"OS:OutputControl:ReportingTolerances", "5.1-group-reports.html#outputcontrolreportingtolerances"}, + {"OS:Output:JSON", "5.1-group-reports.html#outputjson"}, + {"OS:Output:Table:SummaryReports", "7.5-outputtablesummaryreports.html#outputtablesummaryreports"}, + {"OS:OutputControl:ResilienceSummaries", "5.1-group-reports.html#OutputControlResilienceSummaries"}, + + // Life Cycle Cost + {"OS:LifeCycleCost:Parameters", "3.17-lifecyclecostparameters.html#lifecyclecostparameters"}, + {"OS:LifeCycleCost:UsePriceEscalation", "3.20-lifecyclecostusepriceescalation.html#lifecyclecostusepriceescalation"}, + }; + // clang-format on + + auto it = urlMap.constFind(iddTypeName); + if (it != urlMap.constEnd()) { + return base + it.value(); + } + qWarning() << "Cannot find doc url for: " + iddTypeName; + return {}; +} + +// Returns the local EnergyPlus Input Output Reference page URL for a given +// OpenStudio IDD group name (e.g. "OpenStudio Simulation"), or an empty string +// if the group has no known page. Group names match \group +// declarations in OpenStudio.idd. Groups that span multiple EnergyPlus +// chapters (e.g. "OpenStudio HVAC") are omitted. +inline QString iddGroupDocUrl(const QString& groupName) { + static const QString base = + QUrl::fromLocalFile(QString::fromStdString(openstudio::toString(openstudio::energyPlusDocDirectory())) + "/").toString(); + + // clang-format off + static const QHash groupMap{ + {"OpenStudio Core", "1.5-group-simulation-parameters.html"}, + {"OpenStudio Simulation", "1.5-group-simulation-parameters.html"}, + {"OpenStudio Site", "1.7-group-location-climate-weather-file-access.html"}, + {"OpenStudio Materials", "1.9-group-surface-construction-elements.html"}, + {"OpenStudio Constructions", "1.9-group-surface-construction-elements.html"}, + {"OpenStudio Space Load Definitions", "1.14-group-internal-gains-people-lights-other-internal-zone-equipment.html"}, + {"OpenStudio Exterior Equipment Definitions", "1.16-group-exterior-energy-use-equipment.html"}, + {"OpenStudio Schedules", "1.8-group-schedules.html"}, + {"OpenStudio Geometry", "1.10-group-thermal-zone-descriptiongeometry.html"}, + {"OpenStudio Space Loads", "1.14-group-internal-gains-people-lights-other-internal-zone-equipment.html"}, + {"OpenStudio Exterior Equipment", "1.16-group-exterior-energy-use-equipment.html"}, + {"OpenStudio Lighting Simulation", "1.15-group-daylighting.html"}, + {"OpenStudio Refrigeration", "1.35-group-refrigeration.html"}, + {"Solar Collectors", "1.39-group-solar-collectors.html"}, + {"Energy Management System (EMS)", "1.45-group-energy-management-system-ems.html"}, + {"User Defined HVAC and Plant Component Models", "1.48-group-user-defined-hvac-and-plant-component-models.html"}, + }; + // clang-format on + + auto it = groupMap.constFind(groupName); + if (it != groupMap.constEnd()) { + return base + it.value(); + } + qWarning() << "Cannot find doc url for: " + groupName; + return {}; +} + +#endif // MODELEDITOR_IDDOBJECTDOCURL_HPP diff --git a/src/model_editor/InspectorGadget.cpp b/src/model_editor/InspectorGadget.cpp index 5433cc7d7..971ad1723 100644 --- a/src/model_editor/InspectorGadget.cpp +++ b/src/model_editor/InspectorGadget.cpp @@ -6,6 +6,7 @@ #include "InspectorGadget.hpp" #include "BridgeClasses.hpp" +#include "IddObjectDocUrl.hpp" #include "IGLineEdit.hpp" #include "IGSpinBoxes.hpp" @@ -289,7 +290,7 @@ void InspectorGadget::layoutItems(QVBoxLayout* masterLayout, QWidget* parent, bo hlayout->setContentsMargins(0, 0, 0, 0); masterLayout->addLayout(hlayout); hlayout->addLayout(layout); - layoutText(layout, parent, AccessPolicy::LOCKED, iddObj.type().valueDescription(), -1, comment); + layoutHeaderText(layout, parent, iddObj.type().valueDescription(), comment); const AccessPolicy* pAccessPolicy = AccessPolicyStore::Instance().getPolicy(iddObj.type()); @@ -478,8 +479,7 @@ void InspectorGadget::parseItem(QVBoxLayout* layout, QWidget* parent, openstudio } } -void InspectorGadget::layoutText(QVBoxLayout* layout, QWidget* parent, openstudio::model::AccessPolicy::ACCESS_LEVEL level, const std::string& val, - int index, const std::string& comment) { +void InspectorGadget::layoutHeaderText(QVBoxLayout* layout, QWidget* parent, const std::string& val, const std::string& comment) { auto* frame = new QFrame(parent); frame->setContentsMargins(0, 0, 0, 0); auto* hbox = new QHBoxLayout(); @@ -488,30 +488,30 @@ void InspectorGadget::layoutText(QVBoxLayout* layout, QWidget* parent, openstudi hbox->setSpacing(0); hbox->setContentsMargins(0, 0, 0, 0); - if (level == AccessPolicy::LOCKED) { - // string stripped(val); - // std::replace(stripped.begin(), stripped.end(), '_', ' '); // replace all '_' to ' ' - auto* label = new QLabel(QString(val.c_str()), parent); - label->setObjectName("IGHeader"); - label->setStyleSheet("font: bold"); - // Qt::Alignment a = Qt::AlignHCenter; - hbox->addWidget(label); - - hbox->addStretch(); + // string stripped(val); + // std::replace(stripped.begin(), stripped.end(), '_', ' '); // replace all '_' to ' ' + auto* label = new QLabel(parent); + label->setObjectName("IGHeader"); + label->setStyleSheet("font: bold"); + + const QString typeName = QString::fromStdString(val); + const QString docUrl = iddObjectDocUrl(typeName); + if (!docUrl.isEmpty()) { + label->setTextFormat(Qt::RichText); + label->setOpenExternalLinks(true); + label->setToolTip(docUrl); + label->setText(QStringLiteral(R"(%2)").arg(docUrl, typeName.toHtmlEscaped())); } else { - //QLineEdit* text = new QLineEdit( QString(val.c_str()), parent ); - auto* text = new IGLineEdit(QString(val.c_str()), this, parent); - hbox->addWidget(text); - text->setProperty(s_indexSlotName, index); + label->setText(typeName); + } - //connect(text, &IGLineEdit::textEdited, this, &InspectorGadget::IGvalueChanged, Qt::QueuedConnection); + // Qt::Alignment a = Qt::AlignHCenter; + hbox->addWidget(label); - connect(text, &IGLineEdit::editingFinished, text, &IGLineEdit::editDone); - connect(text, &IGLineEdit::newValue, this, &InspectorGadget::IGvalueChanged); - } + hbox->addStretch(); auto* commentText = new QLineEdit(QString(comment.c_str()), parent); - commentText->setProperty(s_indexSlotName, index); + commentText->setProperty(s_indexSlotName, -1); connect(commentText, &QLineEdit::textEdited, this, &InspectorGadget::IGcommentChanged); if (!m_showComments) { commentText->hide(); diff --git a/src/model_editor/InspectorGadget.hpp b/src/model_editor/InspectorGadget.hpp index aa923bab5..04a06cb8c 100644 --- a/src/model_editor/InspectorGadget.hpp +++ b/src/model_editor/InspectorGadget.hpp @@ -246,8 +246,7 @@ class InspectorGadget void parseItem(QVBoxLayout* layout, QWidget* parent, openstudio::IddField& field, const std::string& name, const std::string& curVal, openstudio::model::AccessPolicy::ACCESS_LEVEL level, int index, const std::string& comment, bool exists); - void layoutText(QVBoxLayout* layout, QWidget* parent, openstudio::model::AccessPolicy::ACCESS_LEVEL level, const std::string& val, int index, - const std::string& comment); + void layoutHeaderText(QVBoxLayout* layout, QWidget* parent, const std::string& val, const std::string& comment); void layoutText(QVBoxLayout* layout, QWidget* parent, openstudio::IddField& field, openstudio::model::AccessPolicy::ACCESS_LEVEL level, const std::string& name, const std::string& curVal, int index, const std::string& comment, bool exists, bool number, diff --git a/src/openstudio_lib/BuildingInspectorView.cpp b/src/openstudio_lib/BuildingInspectorView.cpp index 3eae89697..1534ec542 100644 --- a/src/openstudio_lib/BuildingInspectorView.cpp +++ b/src/openstudio_lib/BuildingInspectorView.cpp @@ -4,6 +4,7 @@ ***********************************************************************************************************************/ #include "BuildingInspectorView.hpp" +#include "../model_editor/IddObjectDocUrl.hpp" #include "../shared_gui_components/OSComboBox.hpp" #include "../shared_gui_components/OSIntegerEdit.hpp" @@ -41,6 +42,7 @@ #include #include #include +#include namespace openstudio { @@ -437,8 +439,14 @@ BuildingInspectorView::BuildingInspectorView(bool isIP, bool displayAdditionalPr vLayout = new QVBoxLayout(); label = new QLabel(); - label->setText(tr("North Axis: ")); label->setStyleSheet("QLabel { font: bold; }"); + label->setTextFormat(Qt::RichText); + label->setOpenExternalLinks(true); + { + static const QString url = iddObjectDocUrl(QStringLiteral("OS:Building")); + label->setToolTip(url); + label->setText(QStringLiteral(R"(%2)").arg(url, tr("North Axis: "))); + } vLayout->addWidget(label); m_northAxisEdit = new OSQuantityEdit2("deg", "deg", "deg", m_isIP); diff --git a/src/openstudio_lib/CollapsibleInspector.cpp b/src/openstudio_lib/CollapsibleInspector.cpp index 91a3e6146..a7a14c470 100644 --- a/src/openstudio_lib/CollapsibleInspector.cpp +++ b/src/openstudio_lib/CollapsibleInspector.cpp @@ -20,6 +20,11 @@ CollapsibleInspector::CollapsibleInspector(QString text, QWidget* inspector, QWi createLayout(); } +CollapsibleInspector::CollapsibleInspector(QString text, QString url, QWidget* inspector, QWidget* parent) + : QWidget(parent), m_header(new CollapsibleInspectorHeader(std::move(text), std::move(url))), m_inspector(inspector) { + createLayout(); +} + void CollapsibleInspector::createLayout() { setContentsMargins(0, 0, 0, 0); @@ -46,7 +51,8 @@ void CollapsibleInspector::on_headerToggled(bool checked) { //////////////////////////////////////////////////////////////////////////////// -CollapsibleInspectorHeader::CollapsibleInspectorHeader(QString text, QWidget* parent) : QAbstractButton(parent), m_text(std::move(text)) { +CollapsibleInspectorHeader::CollapsibleInspectorHeader(QString text, QString url, QWidget* parent) + : QAbstractButton(parent), m_text(std::move(text)), m_url(std::move(url)) { createLayout(); } @@ -67,9 +73,17 @@ void CollapsibleInspectorHeader::createLayout() { mainHLayout->addWidget(m_arrowLabel, 0, Qt::AlignLeft); // Name - auto* textLabel = new QLabel(m_text); + auto* textLabel = new QLabel(); textLabel->setWordWrap(false); textLabel->setObjectName("H2"); + if (!m_url.isEmpty()) { + textLabel->setTextFormat(Qt::RichText); + textLabel->setOpenExternalLinks(true); + textLabel->setToolTip(m_url); + textLabel->setText(QStringLiteral(R"(%2)").arg(m_url, m_text.toHtmlEscaped())); + } else { + textLabel->setText(m_text); + } mainHLayout->addWidget(textLabel, 0, Qt::AlignLeft); // Stretch diff --git a/src/openstudio_lib/CollapsibleInspector.hpp b/src/openstudio_lib/CollapsibleInspector.hpp index 71cce79e0..855e59639 100644 --- a/src/openstudio_lib/CollapsibleInspector.hpp +++ b/src/openstudio_lib/CollapsibleInspector.hpp @@ -22,6 +22,7 @@ class CollapsibleInspector : public QWidget public: CollapsibleInspector(QString text, QWidget* inspector, QWidget* parent = nullptr); + CollapsibleInspector(QString text, QString url, QWidget* inspector, QWidget* parent = nullptr); virtual ~CollapsibleInspector() {} @@ -42,7 +43,7 @@ class CollapsibleInspectorHeader : public QAbstractButton Q_OBJECT public: - explicit CollapsibleInspectorHeader(QString text, QWidget* parent = nullptr); + explicit CollapsibleInspectorHeader(QString text, QString url = {}, QWidget* parent = nullptr); void setChecked(bool isChecked); @@ -57,6 +58,7 @@ class CollapsibleInspectorHeader : public QAbstractButton void setImage(bool isChecked); QString m_text; + QString m_url; QLabel* m_arrowLabel; diff --git a/src/openstudio_lib/ConstructionsView.cpp b/src/openstudio_lib/ConstructionsView.cpp index 950e4a9ef..9327c42c5 100644 --- a/src/openstudio_lib/ConstructionsView.cpp +++ b/src/openstudio_lib/ConstructionsView.cpp @@ -12,6 +12,7 @@ #include "ConstructionInternalSourceInspectorView.hpp" #include "ConstructionWindowDataFileInspectorView.hpp" #include "ModelObjectTypeListView.hpp" +#include "../model_editor/IddObjectDocUrl.hpp" #include @@ -19,28 +20,31 @@ #include #include +#include +#include namespace openstudio { ConstructionsView::ConstructionsView(bool isIP, const openstudio::model::Model& model, QWidget* parent) - : ModelSubTabView(new ModelObjectTypeListView(ConstructionsView::modelObjectTypesAndNames(), model, true, OSItemType::ListItem, false, parent), + : ModelSubTabView(new ModelObjectTypeListView(ConstructionsView::modelObjectTypesNamesAndUrls(), model, true, OSItemType::ListItem, false, parent), new ConstructionsInspectorView(isIP, model, parent), false, parent) { connect(this, &ConstructionsView::toggleUnitsClicked, modelObjectInspectorView(), &ModelObjectInspectorView::toggleUnitsClicked); } -std::vector> ConstructionsView::modelObjectTypesAndNames() { - std::vector> result; - result.push_back(std::make_pair(IddObjectType::OS_Construction, tr("Constructions"))); - result.push_back(std::make_pair(IddObjectType::OS_Construction_AirBoundary, tr("Air Boundary Constructions"))); - result.push_back(std::make_pair(IddObjectType::OS_Construction_InternalSource, tr("Internal Source Constructions"))); - result.push_back( - std::make_pair(IddObjectType::OS_Construction_CfactorUndergroundWall, tr("C-factor Underground Wall Constructions"))); - result.push_back( - std::make_pair(IddObjectType::OS_Construction_FfactorGroundFloor, tr("F-factor Ground Floor Constructions"))); - // Not currently supported - //result.push_back(std::make_pair(IddObjectType::OS_Construction_WindowDataFile, "Window Data File Constructions")); - - return result; +std::vector> ConstructionsView::modelObjectTypesNamesAndUrls() { + using T = std::tuple; + return { + T{IddObjectType::OS_Construction, tr("Constructions"), iddObjectDocUrl(QStringLiteral("OS:Construction"))}, + T{IddObjectType::OS_Construction_AirBoundary, tr("Air Boundary Constructions"), iddObjectDocUrl(QStringLiteral("OS:Construction:AirBoundary"))}, + T{IddObjectType::OS_Construction_InternalSource, tr("Internal Source Constructions"), + iddObjectDocUrl(QStringLiteral("OS:Construction:InternalSource"))}, + T{IddObjectType::OS_Construction_CfactorUndergroundWall, tr("C-factor Underground Wall Constructions"), + iddObjectDocUrl(QStringLiteral("OS:Construction:CfactorUndergroundWall"))}, + T{IddObjectType::OS_Construction_FfactorGroundFloor, tr("F-factor Ground Floor Constructions"), + iddObjectDocUrl(QStringLiteral("OS:Construction:FfactorGroundFloor"))}, + // Not currently supported + // T{IddObjectType::OS_Construction_WindowDataFile, tr("Window Data File Constructions"), {}}, + }; } ConstructionsInspectorView::ConstructionsInspectorView(bool isIP, const model::Model& model, QWidget* parent) diff --git a/src/openstudio_lib/ConstructionsView.hpp b/src/openstudio_lib/ConstructionsView.hpp index 0867f73b4..ccc303d5f 100644 --- a/src/openstudio_lib/ConstructionsView.hpp +++ b/src/openstudio_lib/ConstructionsView.hpp @@ -11,6 +11,9 @@ #include +#include +#include + namespace openstudio { class ConstructionsView : public ModelSubTabView @@ -23,7 +26,7 @@ class ConstructionsView : public ModelSubTabView virtual ~ConstructionsView() {} private: - static std::vector> modelObjectTypesAndNames(); + static std::vector> modelObjectTypesNamesAndUrls(); }; class ConstructionsInspectorView : public ModelObjectInspectorView diff --git a/src/openstudio_lib/GroundTemperatureView.cpp b/src/openstudio_lib/GroundTemperatureView.cpp index c0ea67850..0bd6cf1da 100644 --- a/src/openstudio_lib/GroundTemperatureView.cpp +++ b/src/openstudio_lib/GroundTemperatureView.cpp @@ -4,6 +4,7 @@ ***********************************************************************************************************************/ #include "GroundTemperatureView.hpp" +#include "../model_editor/IddObjectDocUrl.hpp" #include "OSAppBase.hpp" #include "OSDocument.hpp" @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +37,7 @@ namespace openstudio { // GroundTemperatureEntry // ───────────────────────────────────────────────────────── -GroundTemperatureEntry::GroundTemperatureEntry(const QString& label, QWidget* parent) : QWidget(parent) { +GroundTemperatureEntry::GroundTemperatureEntry(const QString& label, const QString& url, QWidget* parent) : QWidget(parent) { setFixedHeight(50); setObjectName("GroundTemperatureEntry"); setProperty("style", "0"); @@ -48,9 +50,17 @@ GroundTemperatureEntry::GroundTemperatureEntry(const QString& label, QWidget* pa layout->setContentsMargins(9, 0, 9, 0); setLayout(layout); - m_label = new QLabel(label); + m_label = new QLabel(); m_label->setObjectName("H2"); m_label->setWordWrap(true); + if (!url.isEmpty()) { + m_label->setTextFormat(Qt::RichText); + m_label->setOpenExternalLinks(true); + m_label->setToolTip(url); + m_label->setText(QStringLiteral(R"(%2)").arg(url, label.toHtmlEscaped())); + } else { + m_label->setText(label); + } layout->addWidget(m_label); } @@ -90,11 +100,15 @@ GroundTemperatureListView::GroundTemperatureListView(QWidget* parent) : QWidget( layout->setSpacing(0); setLayout(layout); - m_bsEntry = new GroundTemperatureEntry(tr("Building Surface Ground Temperatures"), this); - m_shEntry = new GroundTemperatureEntry(tr("Shallow Ground Temperatures"), this); - m_deepEntry = new GroundTemperatureEntry(tr("Deep Ground Temperatures"), this); - m_fcEntry = new GroundTemperatureEntry(tr("FCfactorMethod Ground Temperatures"), this); - m_waterMainsEntry = new GroundTemperatureEntry(tr("Water Mains Temperature"), this); + m_bsEntry = new GroundTemperatureEntry(tr("Building Surface Ground Temperatures"), + iddObjectDocUrl(QStringLiteral("OS:Site:GroundTemperature:BuildingSurface")), this); + m_shEntry = + new GroundTemperatureEntry(tr("Shallow Ground Temperatures"), iddObjectDocUrl(QStringLiteral("OS:Site:GroundTemperature:Shallow")), this); + m_deepEntry = new GroundTemperatureEntry(tr("Deep Ground Temperatures"), iddObjectDocUrl(QStringLiteral("OS:Site:GroundTemperature:Deep")), this); + m_fcEntry = new GroundTemperatureEntry(tr("FCfactorMethod Ground Temperatures"), + iddObjectDocUrl(QStringLiteral("OS:Site:GroundTemperature:FCfactorMethod")), this); + m_waterMainsEntry = + new GroundTemperatureEntry(tr("Water Mains Temperature"), iddObjectDocUrl(QStringLiteral("OS:Site:WaterMainsTemperature")), this); connect(m_bsEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onBuildingSurfaceClicked); connect(m_shEntry, &GroundTemperatureEntry::clicked, this, &GroundTemperatureListView::onShallowClicked); diff --git a/src/openstudio_lib/GroundTemperatureView.hpp b/src/openstudio_lib/GroundTemperatureView.hpp index 94c30fa25..a8e426d24 100644 --- a/src/openstudio_lib/GroundTemperatureView.hpp +++ b/src/openstudio_lib/GroundTemperatureView.hpp @@ -12,6 +12,7 @@ #include #include +#include class QLabel; class QPushButton; @@ -36,7 +37,7 @@ class GroundTemperatureEntry : public QWidget Q_OBJECT public: - explicit GroundTemperatureEntry(const QString& label, QWidget* parent = nullptr); + explicit GroundTemperatureEntry(const QString& label, const QString& url = {}, QWidget* parent = nullptr); void setSelected(bool selected); diff --git a/src/openstudio_lib/LifeCycleCostsTabView.cpp b/src/openstudio_lib/LifeCycleCostsTabView.cpp index 4583f2118..355cf572d 100644 --- a/src/openstudio_lib/LifeCycleCostsTabView.cpp +++ b/src/openstudio_lib/LifeCycleCostsTabView.cpp @@ -4,6 +4,7 @@ ***********************************************************************************************************************/ #include "LifeCycleCostsTabView.hpp" +#include "../model_editor/IddObjectDocUrl.hpp" #include "../shared_gui_components/OSComboBox.hpp" #include "../shared_gui_components/OSDoubleEdit.hpp" @@ -19,6 +20,7 @@ #include #include #include +#include #include #define OS_EDIT_WIDTH 150 @@ -58,8 +60,15 @@ void LifeCycleCostsView::createWidgets() { vLayout->setSpacing(10); label = new QLabel(); - label->setText(tr("Life Cycle Cost Parameters")); label->setObjectName("H2"); + label->setTextFormat(Qt::RichText); + label->setOpenExternalLinks(true); + { + static const QString url = iddObjectDocUrl(QStringLiteral("OS:LifeCycleCost:Parameters")); + label->setToolTip(url); + label->setText( + QStringLiteral(R"(%2)").arg(url, tr("Life Cycle Cost Parameters"))); + } vLayout->addWidget(label); label = new QLabel(); @@ -142,8 +151,15 @@ void LifeCycleCostsView::createWidgets() { vLayout->setSpacing(5); label = new QLabel(); - label->setText(tr("Use National Institute of Standards and Technology (NIST) Fuel Escalation Rates")); label->setObjectName("H2"); + label->setTextFormat(Qt::RichText); + label->setOpenExternalLinks(true); + { + static const QString url = iddObjectDocUrl(QStringLiteral("OS:LifeCycleCost:UsePriceEscalation")); + label->setToolTip(url); + label->setText(QStringLiteral(R"(%2)") + .arg(url, tr("Use National Institute of Standards and Technology (NIST) Fuel Escalation Rates"))); + } vLayout->addWidget(label); m_nistGroup = new QButtonGroup(this); diff --git a/src/openstudio_lib/LoadsView.cpp b/src/openstudio_lib/LoadsView.cpp index 5c1d1c363..b1d12b0ac 100644 --- a/src/openstudio_lib/LoadsView.cpp +++ b/src/openstudio_lib/LoadsView.cpp @@ -5,6 +5,7 @@ #include "LoadsView.hpp" #include "ModelObjectTypeListView.hpp" +#include "../model_editor/IddObjectDocUrl.hpp" #include "PeopleInspectorView.hpp" #include "InternalMassInspectorView.hpp" #include "LightsInspectorView.hpp" @@ -20,6 +21,7 @@ #include +#include #include #include #include @@ -28,32 +30,33 @@ #include #include #include +#include #include namespace openstudio { LoadsView::LoadsView(bool isIP, const openstudio::model::Model& model, QWidget* parent) - : ModelSubTabView(new ModelObjectTypeListView(LoadsView::modelObjectTypesAndNames(), model, true, OSItemType::CollapsibleListHeader, false, parent), - new LoadsInspectorView(isIP, model, parent), false, parent) { + : ModelSubTabView( + new ModelObjectTypeListView(LoadsView::modelObjectTypesNamesAndUrls(), model, true, OSItemType::CollapsibleListHeader, false, parent), + new LoadsInspectorView(isIP, model, parent), false, parent) { connect(this, &LoadsView::toggleUnitsClicked, modelObjectInspectorView(), &ModelObjectInspectorView::toggleUnitsClicked); } -std::vector> LoadsView::modelObjectTypesAndNames() { - std::vector> result; - - result.push_back(std::make_pair(IddObjectType::OS_People_Definition, tr("People Definitions"))); - result.push_back(std::make_pair(IddObjectType::OS_Lights_Definition, tr("Lights Definitions"))); - result.push_back(std::make_pair(IddObjectType::OS_Luminaire_Definition, tr("Luminaire Definitions"))); - result.push_back(std::make_pair(IddObjectType::OS_ElectricEquipment_Definition, tr("Electric Equipment Definitions"))); - result.push_back(std::make_pair(IddObjectType::OS_GasEquipment_Definition, tr("Gas Equipment Definitions"))); - result.push_back(std::make_pair(IddObjectType::OS_SteamEquipment_Definition, tr("Steam Equipment Definitions"))); - result.push_back(std::make_pair(IddObjectType::OS_OtherEquipment_Definition, tr("Other Equipment Definitions"))); - result.push_back(std::make_pair(IddObjectType::OS_InternalMass_Definition, tr("Internal Mass Definitions"))); - result.push_back(std::make_pair(IddObjectType::OS_WaterUse_Equipment_Definition, tr("Water Use Equipment Definitions"))); - result.push_back(std::make_pair(IddObjectType::OS_HotWaterEquipment_Definition, tr("Hot Water Equipment Definitions"))); - - return result; +std::vector> LoadsView::modelObjectTypesNamesAndUrls() { + using T = std::tuple; + return { + T{IddObjectType::OS_People_Definition, tr("People Definitions"), iddObjectDocUrl(QStringLiteral("OS:People"))}, + T{IddObjectType::OS_Lights_Definition, tr("Lights Definitions"), iddObjectDocUrl(QStringLiteral("OS:Lights"))}, + T{IddObjectType::OS_Luminaire_Definition, tr("Luminaire Definitions"), {}}, + T{IddObjectType::OS_ElectricEquipment_Definition, tr("Electric Equipment Definitions"), iddObjectDocUrl(QStringLiteral("OS:ElectricEquipment"))}, + T{IddObjectType::OS_GasEquipment_Definition, tr("Gas Equipment Definitions"), iddObjectDocUrl(QStringLiteral("OS:GasEquipment"))}, + T{IddObjectType::OS_SteamEquipment_Definition, tr("Steam Equipment Definitions"), iddObjectDocUrl(QStringLiteral("OS:SteamEquipment"))}, + T{IddObjectType::OS_OtherEquipment_Definition, tr("Other Equipment Definitions"), iddObjectDocUrl(QStringLiteral("OS:OtherEquipment"))}, + T{IddObjectType::OS_InternalMass_Definition, tr("Internal Mass Definitions"), iddObjectDocUrl(QStringLiteral("OS:InternalMass"))}, + T{IddObjectType::OS_WaterUse_Equipment_Definition, tr("Water Use Equipment Definitions"), iddObjectDocUrl(QStringLiteral("OS:WaterUse:Equipment"))}, + T{IddObjectType::OS_HotWaterEquipment_Definition, tr("Hot Water Equipment Definitions"), iddObjectDocUrl(QStringLiteral("OS:HotWaterEquipment"))}, + }; } void LoadsView::toggleUnits(bool displayIP) {} diff --git a/src/openstudio_lib/LoadsView.hpp b/src/openstudio_lib/LoadsView.hpp index ff8ef736d..9b379a122 100644 --- a/src/openstudio_lib/LoadsView.hpp +++ b/src/openstudio_lib/LoadsView.hpp @@ -13,6 +13,9 @@ #include +#include +#include + class QStackedWidget; namespace openstudio { @@ -27,7 +30,7 @@ class LoadsView : public ModelSubTabView virtual ~LoadsView() {} private: - static std::vector> modelObjectTypesAndNames(); + static std::vector> modelObjectTypesNamesAndUrls(); public slots: diff --git a/src/openstudio_lib/LocationTabView.cpp b/src/openstudio_lib/LocationTabView.cpp index 2763e70fa..4edb7a7ec 100644 --- a/src/openstudio_lib/LocationTabView.cpp +++ b/src/openstudio_lib/LocationTabView.cpp @@ -4,6 +4,7 @@ ***********************************************************************************************************************/ #include "LocationTabView.hpp" +#include "../model_editor/IddObjectDocUrl.hpp" #include "DesignDayGridView.hpp" #include "ModelObjectListView.hpp" @@ -209,8 +210,15 @@ LocationView::LocationView(bool isIP, const model::Model& model, const QString& mainHLine->setFrameShadow(QFrame::Sunken); // ***** Weather File ***** - auto* label = new QLabel(tr("Weather File")); + auto* label = new QLabel(); label->setObjectName("H2"); + label->setTextFormat(Qt::RichText); + label->setOpenExternalLinks(true); + { + static const QString url = iddGroupDocUrl(QStringLiteral("OpenStudio Site")); + label->setToolTip(url); + label->setText(QStringLiteral(R"(%2)").arg(url, tr("Weather File"))); + } m_weatherFileBtn = new QPushButton(this); m_weatherFileBtn->setFlat(true); @@ -456,8 +464,15 @@ LocationView::LocationView(bool isIP, const model::Model& model, const QString& scrollLayout->addWidget(mainHLine); // ***** Design Days ***** - label = new QLabel(tr("Design Days")); + label = new QLabel(); label->setObjectName("H2"); + label->setTextFormat(Qt::RichText); + label->setOpenExternalLinks(true); + { + static const QString url = iddObjectDocUrl(QStringLiteral("OS:SizingPeriod:DesignDay")); + label->setToolTip(url); + label->setText(QStringLiteral(R"(%2)").arg(url, tr("Design Days"))); + } auto* btn = new QPushButton(tr("Import From DDY"), this); btn->setFlat(true); diff --git a/src/openstudio_lib/MaterialsView.cpp b/src/openstudio_lib/MaterialsView.cpp index 214bc9d61..69e0fe8ee 100644 --- a/src/openstudio_lib/MaterialsView.cpp +++ b/src/openstudio_lib/MaterialsView.cpp @@ -11,6 +11,7 @@ #include "MaterialNoMassInspectorView.hpp" #include "MaterialRoofVegetationInspectorView.hpp" #include "ModelObjectTypeListView.hpp" +#include "../model_editor/IddObjectDocUrl.hpp" #include "WindowMaterialBlindInspectorView.hpp" #include "WindowMaterialDaylightRedirectionDeviceInspectorView.hpp" #include "WindowMaterialGasInspectorView.hpp" @@ -29,44 +30,44 @@ #include #include +#include +#include namespace openstudio { MaterialsView::MaterialsView(bool isIP, const openstudio::model::Model& model, const QString& tabLabel, bool hasSubTabs, QWidget* parent) : ModelSubTabView( - new ModelObjectTypeListView(MaterialsView::modelObjectTypesAndNames(), model, true, OSItemType::CollapsibleListHeader, false, parent), + new ModelObjectTypeListView(MaterialsView::modelObjectTypesNamesAndUrls(), model, true, OSItemType::CollapsibleListHeader, false, parent), new MaterialsInspectorView(isIP, model, parent), false, parent) { // ModelObjectTypeListView will call reportItems for each IddObjectType, this results in inspector being build for each IddObjecType then thrown away connect(this, &MaterialsView::toggleUnitsClicked, modelObjectInspectorView(), &ModelObjectInspectorView::toggleUnitsClicked); } -std::vector> MaterialsView::modelObjectTypesAndNames() { - std::vector> result; - result.push_back(std::make_pair(IddObjectType::OS_Material, tr("Materials"))); - result.push_back(std::make_pair(IddObjectType::OS_Material_NoMass, tr("No Mass Materials"))); - result.push_back(std::make_pair(IddObjectType::OS_Material_AirGap, tr("Air Gap Materials"))); - - result.push_back( - std::make_pair(IddObjectType::OS_WindowMaterial_SimpleGlazingSystem, tr("Simple Glazing System Window Materials"))); - result.push_back(std::make_pair(IddObjectType::OS_WindowMaterial_Glazing, tr("Glazing Window Materials"))); - result.push_back(std::make_pair(IddObjectType::OS_WindowMaterial_Gas, tr("Gas Window Materials"))); - result.push_back(std::make_pair(IddObjectType::OS_WindowMaterial_GasMixture, tr("Gas Mixture Window Materials"))); - result.push_back(std::make_pair(IddObjectType::OS_WindowMaterial_Blind, tr("Blind Window Materials"))); - result.push_back(std::make_pair(IddObjectType::OS_WindowMaterial_DaylightRedirectionDevice, - tr("Daylight Redirection Device Window Materials"))); - result.push_back(std::make_pair(IddObjectType::OS_WindowMaterial_Screen, tr("Screen Window Materials"))); - result.push_back(std::make_pair(IddObjectType::OS_WindowMaterial_Shade, tr("Shade Window Materials"))); - - // Oddballs to be listed at the bottom of the list - result.push_back(std::make_pair(IddObjectType::OS_Material_InfraredTransparent, tr("Infrared Transparent Materials"))); - result.push_back(std::make_pair(IddObjectType::OS_Material_RoofVegetation, tr("Roof Vegetation Materials"))); - result.push_back(std::make_pair(IddObjectType::OS_WindowMaterial_Glazing_RefractionExtinctionMethod, - tr("Refraction Extinction Method Glazing Window Materials"))); - - // TODO: commented out until ThermochromicGlazing is properly wrapped - // result.push_back(std::make_pair(IddObjectType::OS_WindowMaterial_GlazingGroup_Thermochromic, "Glazing Group Thermochromic Window Materials")); - - return result; +std::vector> MaterialsView::modelObjectTypesNamesAndUrls() { + using T = std::tuple; + return { + T{IddObjectType::OS_Material, tr("Materials"), iddObjectDocUrl(QStringLiteral("OS:Material"))}, + T{IddObjectType::OS_Material_NoMass, tr("No Mass Materials"), iddObjectDocUrl(QStringLiteral("OS:Material:NoMass"))}, + T{IddObjectType::OS_Material_AirGap, tr("Air Gap Materials"), iddObjectDocUrl(QStringLiteral("OS:Material:AirGap"))}, + T{IddObjectType::OS_WindowMaterial_SimpleGlazingSystem, tr("Simple Glazing System Window Materials"), + iddObjectDocUrl(QStringLiteral("OS:WindowMaterial:SimpleGlazingSystem"))}, + T{IddObjectType::OS_WindowMaterial_Glazing, tr("Glazing Window Materials"), iddObjectDocUrl(QStringLiteral("OS:WindowMaterial:Glazing"))}, + T{IddObjectType::OS_WindowMaterial_Gas, tr("Gas Window Materials"), iddObjectDocUrl(QStringLiteral("OS:WindowMaterial:Gas"))}, + T{IddObjectType::OS_WindowMaterial_GasMixture, tr("Gas Mixture Window Materials"), iddObjectDocUrl(QStringLiteral("OS:WindowMaterial:GasMixture"))}, + T{IddObjectType::OS_WindowMaterial_Blind, tr("Blind Window Materials"), iddObjectDocUrl(QStringLiteral("OS:WindowMaterial:Blind"))}, + T{IddObjectType::OS_WindowMaterial_DaylightRedirectionDevice, tr("Daylight Redirection Device Window Materials"), + iddObjectDocUrl(QStringLiteral("OS:WindowMaterial:DaylightRedirectionDevice"))}, + T{IddObjectType::OS_WindowMaterial_Screen, tr("Screen Window Materials"), iddObjectDocUrl(QStringLiteral("OS:WindowMaterial:Screen"))}, + T{IddObjectType::OS_WindowMaterial_Shade, tr("Shade Window Materials"), iddObjectDocUrl(QStringLiteral("OS:WindowMaterial:Shade"))}, + // Oddballs at the bottom + T{IddObjectType::OS_Material_InfraredTransparent, tr("Infrared Transparent Materials"), + iddObjectDocUrl(QStringLiteral("OS:Material:InfraredTransparent"))}, + T{IddObjectType::OS_Material_RoofVegetation, tr("Roof Vegetation Materials"), iddObjectDocUrl(QStringLiteral("OS:Material:RoofVegetation"))}, + T{IddObjectType::OS_WindowMaterial_Glazing_RefractionExtinctionMethod, tr("Refraction Extinction Method Glazing Window Materials"), + iddObjectDocUrl(QStringLiteral("OS:WindowMaterial:Glazing:RefractionExtinctionMethod"))}, + // TODO: commented out until ThermochromicGlazing is properly wrapped + // T{IddObjectType::OS_WindowMaterial_GlazingGroup_Thermochromic, tr("Glazing Group Thermochromic Window Materials"), sce + "#windowmaterialglazinggroupthermochromic"}, + }; } MaterialsInspectorView::MaterialsInspectorView(bool isIP, const model::Model& model, QWidget* parent) diff --git a/src/openstudio_lib/MaterialsView.hpp b/src/openstudio_lib/MaterialsView.hpp index be04cfd70..87e8b7cce 100644 --- a/src/openstudio_lib/MaterialsView.hpp +++ b/src/openstudio_lib/MaterialsView.hpp @@ -11,6 +11,9 @@ #include +#include +#include + class QStackedWidget; namespace openstudio { @@ -25,7 +28,7 @@ class MaterialsView : public ModelSubTabView virtual ~MaterialsView() {} private: - static std::vector> modelObjectTypesAndNames(); + static std::vector> modelObjectTypesNamesAndUrls(); }; class MaterialsInspectorView : public ModelObjectInspectorView diff --git a/src/openstudio_lib/ModelObjectTypeListView.cpp b/src/openstudio_lib/ModelObjectTypeListView.cpp index 34c8c584e..7520dc0df 100644 --- a/src/openstudio_lib/ModelObjectTypeListView.cpp +++ b/src/openstudio_lib/ModelObjectTypeListView.cpp @@ -38,8 +38,18 @@ ModelObjectTypeListView::ModelObjectTypeListView(const std::vector>& modelObjectTypesNamesAndUrls, + const model::Model& model, bool addScrollArea, OSItemType headerType, bool isLibrary, + QWidget* parent) + : OSCollapsibleItemList(addScrollArea, parent), m_model(model), m_headerType(headerType), m_isLibrary(isLibrary) { + for (auto it = modelObjectTypesNamesAndUrls.rbegin(); it != modelObjectTypesNamesAndUrls.rend(); ++it) { + addModelObjectType(std::get<0>(*it), std::get<1>(*it), std::get<2>(*it)); + } + selectFirstCollapsibleItem(); +} + +void ModelObjectTypeListView::addModelObjectType(const IddObjectType& iddObjectType, const QString& name, const QString& url) { + auto* collapsibleItemHeader = new OSCollapsibleItemHeader(name, OSItemId("", "", false), m_headerType, url); auto* modelObjectListView = new ModelObjectListView(iddObjectType, m_model, false, m_isLibrary); auto* modelObjectTypeItem = new ModelObjectTypeItem(collapsibleItemHeader, modelObjectListView); diff --git a/src/openstudio_lib/ModelObjectTypeListView.hpp b/src/openstudio_lib/ModelObjectTypeListView.hpp index 89caa50dd..43137eac1 100644 --- a/src/openstudio_lib/ModelObjectTypeListView.hpp +++ b/src/openstudio_lib/ModelObjectTypeListView.hpp @@ -14,6 +14,10 @@ #include +#include + +#include + class QVBoxLayout; class QHBoxLayout; @@ -30,9 +34,12 @@ class ModelObjectTypeListView : public OSCollapsibleItemList ModelObjectTypeListView(const std::vector>& modelObjectTypesAndNames, const model::Model& model, bool addScrollArea, OSItemType headerType, bool isLibrary, QWidget* parent = nullptr); + ModelObjectTypeListView(const std::vector>& modelObjectTypesNamesAndUrls, + const model::Model& model, bool addScrollArea, OSItemType headerType, bool isLibrary, QWidget* parent = nullptr); + virtual ~ModelObjectTypeListView() {} - void addModelObjectType(const IddObjectType& iddObjectType, const QString& name); + void addModelObjectType(const IddObjectType& iddObjectType, const QString& name, const QString& url = {}); void addModelObjectCategoryPlaceholder(const QString& name); diff --git a/src/openstudio_lib/OSCollapsibleItemHeader.cpp b/src/openstudio_lib/OSCollapsibleItemHeader.cpp index d2f1f8af9..dc56c9871 100644 --- a/src/openstudio_lib/OSCollapsibleItemHeader.cpp +++ b/src/openstudio_lib/OSCollapsibleItemHeader.cpp @@ -18,7 +18,8 @@ namespace openstudio { -OSCollapsibleItemHeader::OSCollapsibleItemHeader(const QString& text, const OSItemId& itemId, OSItemType type, QWidget* parent) +OSCollapsibleItemHeader::OSCollapsibleItemHeader(const QString& text, const OSItemId& itemId, OSItemType type, const QString& url, + QWidget* parent) : QWidget(parent), m_mouseDown(false) { setFixedHeight(50); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); @@ -35,9 +36,18 @@ OSCollapsibleItemHeader::OSCollapsibleItemHeader(const QString& text, const OSIt // Label - m_textLabel = new QLabel(text); + m_plainText = text; + m_textLabel = new QLabel(); m_textLabel->setWordWrap(true); m_textLabel->setObjectName("H2"); + if (!url.isEmpty()) { + m_textLabel->setTextFormat(Qt::RichText); + m_textLabel->setOpenExternalLinks(true); + m_textLabel->setToolTip(url); + m_textLabel->setText(QStringLiteral(R"(%2)").arg(url, m_plainText.toHtmlEscaped())); + } else { + m_textLabel->setText(m_plainText); + } mainHLayout->addWidget(m_textLabel, 10); mainHLayout->addStretch(); @@ -57,7 +67,7 @@ QSize OSCollapsibleItemHeader::sizeHint() const { } QString OSCollapsibleItemHeader::text() const { - return m_textLabel->text(); + return m_plainText; } bool OSCollapsibleItemHeader::expanded() const { diff --git a/src/openstudio_lib/OSCollapsibleItemHeader.hpp b/src/openstudio_lib/OSCollapsibleItemHeader.hpp index ba57febc0..01ba81930 100644 --- a/src/openstudio_lib/OSCollapsibleItemHeader.hpp +++ b/src/openstudio_lib/OSCollapsibleItemHeader.hpp @@ -8,6 +8,8 @@ #include "../shared_gui_components/OSItem.hpp" +#include + class QLabel; class QVBoxLayout; class QPaintEvent; @@ -21,7 +23,7 @@ class OSCollapsibleItemHeader : public QWidget public: OSCollapsibleItemHeader(const QString& text, const OSItemId& itemId, OSItemType type = OSItemType::CollapsibleListHeader, - QWidget* parent = nullptr); + const QString& url = {}, QWidget* parent = nullptr); virtual ~OSCollapsibleItemHeader() = default; @@ -49,6 +51,7 @@ class OSCollapsibleItemHeader : public QWidget QLabel* m_arrowLabel; QLabel* m_textLabel; + QString m_plainText; bool m_expanded; bool m_selected; diff --git a/src/openstudio_lib/ScheduleOthersView.cpp b/src/openstudio_lib/ScheduleOthersView.cpp index b2264172f..46eeb247a 100644 --- a/src/openstudio_lib/ScheduleOthersView.cpp +++ b/src/openstudio_lib/ScheduleOthersView.cpp @@ -5,6 +5,7 @@ #include "ScheduleOthersView.hpp" #include "ModelObjectTypeListView.hpp" +#include "../model_editor/IddObjectDocUrl.hpp" #include "ScheduleConstantInspectorView.hpp" #include "ScheduleCompactInspectorView.hpp" @@ -16,19 +17,22 @@ #include #include +#include +#include namespace openstudio { ScheduleOthersView::ScheduleOthersView(const openstudio::model::Model& model, QWidget* parent) : ModelSubTabView( - new ModelObjectTypeListView(ScheduleOthersView::modelObjectTypesAndNames(), model, true, OSItemType::CollapsibleListHeader, false, parent), + new ModelObjectTypeListView(ScheduleOthersView::modelObjectTypesNamesAndUrls(), model, true, OSItemType::CollapsibleListHeader, false, parent), new ScheduleOthersInspectorView(model, parent), false, parent) {} -std::vector> ScheduleOthersView::modelObjectTypesAndNames() { +std::vector> ScheduleOthersView::modelObjectTypesNamesAndUrls() { + using T = std::tuple; return { - {IddObjectType::OS_Schedule_Constant, tr("Schedule Constant")}, - {IddObjectType::OS_Schedule_Compact, tr("Schedule Compact")}, - {IddObjectType::OS_Schedule_File, tr("Schedule File")}, + T{IddObjectType::OS_Schedule_Constant, tr("Schedule Constant"), iddObjectDocUrl(QStringLiteral("OS:Schedule:Constant"))}, + T{IddObjectType::OS_Schedule_Compact, tr("Schedule Compact"), iddObjectDocUrl(QStringLiteral("OS:Schedule:Compact"))}, + T{IddObjectType::OS_Schedule_File, tr("Schedule File"), iddObjectDocUrl(QStringLiteral("OS:Schedule:File"))}, }; } diff --git a/src/openstudio_lib/ScheduleOthersView.hpp b/src/openstudio_lib/ScheduleOthersView.hpp index 7a6c95b72..b8712e031 100644 --- a/src/openstudio_lib/ScheduleOthersView.hpp +++ b/src/openstudio_lib/ScheduleOthersView.hpp @@ -11,6 +11,9 @@ #include +#include +#include + class QStackedWidget; namespace openstudio { @@ -25,7 +28,7 @@ class ScheduleOthersView : public ModelSubTabView virtual ~ScheduleOthersView() = default; private: - static std::vector> modelObjectTypesAndNames(); + static std::vector> modelObjectTypesNamesAndUrls(); }; class ScheduleOthersInspectorView : public ModelObjectInspectorView diff --git a/src/openstudio_lib/SimSettingsView.cpp b/src/openstudio_lib/SimSettingsView.cpp index 2ccf1d369..b5d1ea5bc 100644 --- a/src/openstudio_lib/SimSettingsView.cpp +++ b/src/openstudio_lib/SimSettingsView.cpp @@ -7,6 +7,7 @@ #include "CollapsibleInspector.hpp" #include "ModelObjectTypeListView.hpp" +#include "../model_editor/IddObjectDocUrl.hpp" #include "OSAppBase.hpp" #include "../shared_gui_components/OSComboBox.hpp" #include "OSDocument.hpp" @@ -239,7 +240,8 @@ void SimSettingsView::createWidgets() { //******************* OS:Timestep ******************* mainLayout->addWidget(createTimestepWidget()); - collapsibleInspector = new CollapsibleInspector(tr("Advanced RunPeriod Parameters"), createRunPeriodAdvancedWidget()); + collapsibleInspector = + new CollapsibleInspector(tr("Advanced RunPeriod Parameters"), iddObjectDocUrl(QStringLiteral("OS:RunPeriod")), createRunPeriodAdvancedWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:RadianceParameters ******************* @@ -247,7 +249,8 @@ void SimSettingsView::createWidgets() { mainLayout->addWidget(collapsibleInspector); //******************* OS:SimulationControl ******************* - collapsibleInspector = new CollapsibleInspector(tr("Simulation Control"), createSimulationControlWidget()); + collapsibleInspector = + new CollapsibleInspector(tr("Simulation Control"), iddObjectDocUrl(QStringLiteral("OS:SimulationControl")), createSimulationControlWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:ProgramControl ******************* @@ -255,56 +258,73 @@ void SimSettingsView::createWidgets() { mainLayout->addWidget(collapsibleInspector); //******************* OS:OutputControl:ReportingTolerances ******************* - collapsibleInspector = new CollapsibleInspector(tr("Output Control Reporting Tolerances"), createOutputControlReportingTolerancesWidget()); + collapsibleInspector = + new CollapsibleInspector(tr("Output Control Reporting Tolerances"), iddObjectDocUrl(QStringLiteral("OS:OutputControl:ReportingTolerances")), + createOutputControlReportingTolerancesWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:ConvergenceLimits ******************* - collapsibleInspector = new CollapsibleInspector(tr("Convergence Limits"), createConvergenceLimitsWidget()); + collapsibleInspector = + new CollapsibleInspector(tr("Convergence Limits"), iddObjectDocUrl(QStringLiteral("OS:ConvergenceLimits")), createConvergenceLimitsWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:ShadowCalculation ******************* - collapsibleInspector = new CollapsibleInspector(tr("Shadow Calculation"), createShadowCalculationWidget()); + collapsibleInspector = + new CollapsibleInspector(tr("Shadow Calculation"), iddObjectDocUrl(QStringLiteral("OS:ShadowCalculation")), createShadowCalculationWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:SurfaceConvectionAlgorithm:Inside ******************* - collapsibleInspector = new CollapsibleInspector(tr("Inside Surface Convection Algorithm"), createSurfaceConvectionAlgorithmInsideWidget()); + collapsibleInspector = + new CollapsibleInspector(tr("Inside Surface Convection Algorithm"), iddObjectDocUrl(QStringLiteral("OS:SurfaceConvectionAlgorithm:Inside")), + createSurfaceConvectionAlgorithmInsideWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:SurfaceConvectionAlgorithm:Outside ******************* - collapsibleInspector = new CollapsibleInspector(tr("Outside Surface Convection Algorithm"), createSurfaceConvectionAlgorithmOutsideWidget()); + collapsibleInspector = + new CollapsibleInspector(tr("Outside Surface Convection Algorithm"), iddObjectDocUrl(QStringLiteral("OS:SurfaceConvectionAlgorithm:Outside")), + createSurfaceConvectionAlgorithmOutsideWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:HeatBalanceAlgorithm ******************* - collapsibleInspector = new CollapsibleInspector(tr("Heat Balance Algorithm"), createHeatBalanceAlgorithmWidget()); + collapsibleInspector = new CollapsibleInspector(tr("Heat Balance Algorithm"), iddObjectDocUrl(QStringLiteral("OS:HeatBalanceAlgorithm")), + createHeatBalanceAlgorithmWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:ZoneAirHeatBalanceAlgorithm ******************* - collapsibleInspector = new CollapsibleInspector(tr("Zone Air Heat Balance Algorithm"), createZoneAirHeatBalanceAlgorithmWidget()); + collapsibleInspector = + new CollapsibleInspector(tr("Zone Air Heat Balance Algorithm"), iddObjectDocUrl(QStringLiteral("OS:ZoneAirHeatBalanceAlgorithm")), + createZoneAirHeatBalanceAlgorithmWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:ZoneAirContaminantBalance ******************* - collapsibleInspector = new CollapsibleInspector(tr("Zone Air Contaminant Balance"), createZoneAirContaminantBalanceWidget()); + collapsibleInspector = new CollapsibleInspector(tr("Zone Air Contaminant Balance"), iddObjectDocUrl(QStringLiteral("OS:ZoneAirContaminantBalance")), + createZoneAirContaminantBalanceWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:ZoneCapacitanceMultiplier:ResearchSpecial ******************* - collapsibleInspector = - new CollapsibleInspector(tr("Zone Capacitance Multiple Research Special"), createZoneCapacitanceMultipleResearchSpecialWidget()); + collapsibleInspector = new CollapsibleInspector(tr("Zone Capacitance Multiple Research Special"), + iddObjectDocUrl(QStringLiteral("OS:ZoneCapacitanceMultiplier:ResearchSpecial")), + createZoneCapacitanceMultipleResearchSpecialWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:Output:JSON ******************* - collapsibleInspector = new CollapsibleInspector(tr("Output JSON"), createOutputJSONWidget()); + collapsibleInspector = new CollapsibleInspector(tr("Output JSON"), iddObjectDocUrl(QStringLiteral("OS:Output:JSON")), createOutputJSONWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:Output:Table:SummaryReports ******************* - collapsibleInspector = new CollapsibleInspector(tr("Output Table Summary Reports"), createOutputTableSummaryReportsWidget()); + collapsibleInspector = new CollapsibleInspector( + tr("Output Table Summary Reports"), iddObjectDocUrl(QStringLiteral("OS:Output:Table:SummaryReports")), createOutputTableSummaryReportsWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:Output:Diagnostics ******************* - collapsibleInspector = new CollapsibleInspector(tr("Output Diagnostics"), createOutputDiagnosticsWidget()); + collapsibleInspector = + new CollapsibleInspector(tr("Output Diagnostics"), iddObjectDocUrl(QStringLiteral("OS:Output:Diagnostics")), createOutputDiagnosticsWidget()); mainLayout->addWidget(collapsibleInspector); //******************* OS:OutputControl:ResilienceSummaries ******************* - collapsibleInspector = new CollapsibleInspector(tr("Output Control Resilience Summaries"), createOutputControlResilienceSummariesWidget()); + collapsibleInspector = + new CollapsibleInspector(tr("Output Control Resilience Summaries"), iddObjectDocUrl(QStringLiteral("OS:OutputControl:ResilienceSummaries")), + createOutputControlResilienceSummariesWidget()); mainLayout->addWidget(collapsibleInspector); mainLayout->addStretch(); @@ -325,8 +345,15 @@ QWidget* SimSettingsView::createRunPeriodWidget() { QHBoxLayout* hLayout = nullptr; - label = new QLabel(tr("Run Period")); + label = new QLabel(); label->setObjectName("H1"); + label->setTextFormat(Qt::RichText); + label->setOpenExternalLinks(true); + { + static const QString url = iddObjectDocUrl(QStringLiteral("OS:RunPeriod")); + label->setToolTip(url); + label->setText(QStringLiteral(R"(%2)").arg(url, tr("Run Period"))); + } mainVLayout->addWidget(label); mainVLayout->addLayout(mainHLayout); @@ -602,8 +629,16 @@ QWidget* SimSettingsView::createSizingParametersWidget() { QLabel* label = nullptr; - label = new QLabel(tr("Sizing Parameters")); + label = new QLabel(); label->setObjectName("H1"); + label->setTextFormat(Qt::RichText); + label->setOpenExternalLinks(true); + { + static const QString url = iddObjectDocUrl(QStringLiteral("OS:Sizing:Parameters")); + label->setToolTip(url); + label->setText( + QStringLiteral(R"(%2)").arg(url, tr("Sizing Parameters"))); + } mainLayout->addWidget(label); auto* gridLayout = new QGridLayout(); @@ -652,8 +687,15 @@ QWidget* SimSettingsView::createTimestepWidget() { QLabel* label = nullptr; - label = new QLabel(tr("Timestep")); + label = new QLabel(); label->setObjectName("H1"); + label->setTextFormat(Qt::RichText); + label->setOpenExternalLinks(true); + { + static const QString url = iddObjectDocUrl(QStringLiteral("OS:Timestep")); + label->setToolTip(url); + label->setText(QStringLiteral(R"(%2)").arg(url, tr("Timestep"))); + } mainLayout->addWidget(label); auto* gridLayout = new QGridLayout(); diff --git a/src/openstudio_lib/YearSettingsWidget.cpp b/src/openstudio_lib/YearSettingsWidget.cpp index db3d1c52e..2ecf83370 100644 --- a/src/openstudio_lib/YearSettingsWidget.cpp +++ b/src/openstudio_lib/YearSettingsWidget.cpp @@ -4,6 +4,7 @@ ***********************************************************************************************************************/ #include "YearSettingsWidget.hpp" +#include "../model_editor/IddObjectDocUrl.hpp" #include "OSAppBase.hpp" #include "OSDocument.hpp" @@ -102,8 +103,15 @@ YearSettingsWidget::YearSettingsWidget(const model::Model& model, QWidget* paren dstHLayout1->setContentsMargins(0, 0, 0, 0); dstHLayout1->setSpacing(10); - auto* dstLabel = new QLabel(tr("Daylight Savings Time:")); + auto* dstLabel = new QLabel(); dstLabel->setObjectName("H2"); + dstLabel->setTextFormat(Qt::RichText); + dstLabel->setOpenExternalLinks(true); + { + static const QString url = iddObjectDocUrl(QStringLiteral("OS:RunPeriodControl:DaylightSavingTime")); + dstLabel->setToolTip(url); + dstLabel->setText(QStringLiteral(R"(%2)").arg(url, tr("Daylight Savings Time:"))); + } dstHLayout1->addWidget(dstLabel); m_dstOnOffButton = new OSSwitch2(); diff --git a/src/utilities/OpenStudioApplicationPathHelpers.cxx.in b/src/utilities/OpenStudioApplicationPathHelpers.cxx.in index ff82e797d..d8e6b61c0 100644 --- a/src/utilities/OpenStudioApplicationPathHelpers.cxx.in +++ b/src/utilities/OpenStudioApplicationPathHelpers.cxx.in @@ -8,7 +8,6 @@ // Include the core one as well #include #include -#include #include #include @@ -205,6 +204,13 @@ bool isOpenStudioApplicationModuleRunningFromBuildDirectory() { return pathBeginsWith(buildDir, runDir); } +openstudio::path energyPlusDocDirectory() { + if (isOpenStudioApplicationRunningFromBuildDirectory()) { + return getOpenStudioApplicationBuildDirectory() / toPath("EnergyPlus/doc/input-output-reference"); + } + return getOpenStudioApplicationDirectory() / toPath("../EnergyPlus/doc/input-output-reference"); +} + openstudio::path getOpenStudioCoreCLI() { openstudio::path cliPath; diff --git a/src/utilities/OpenStudioApplicationPathHelpers.hpp b/src/utilities/OpenStudioApplicationPathHelpers.hpp index b2648ac09..273b2c194 100644 --- a/src/utilities/OpenStudioApplicationPathHelpers.hpp +++ b/src/utilities/OpenStudioApplicationPathHelpers.hpp @@ -59,6 +59,9 @@ bool isOpenStudioApplicationModuleRunningFromBuildDirectory(); /// \returns The path to the OpenStudio Command Line Interface if it exists. openstudio::path getOpenStudioCoreCLI(); +/// \returns The directory containing the local EnergyPlus Input Output Reference HTML documentation +openstudio::path energyPlusDocDirectory(); + } // namespace openstudio #endif //OSAPP_UTILITIES_APPLICATIONPATHHELPERS_HPP