TreeLine/ 0000755 0001750 0001750 00000000000 14307147630 011250 5 ustar doug doug TreeLine/samples/ 0000755 0001750 0001750 00000000000 13262465526 012722 5 ustar doug doug TreeLine/samples/210en_sample_char_format.trln 0000644 0001750 0001750 00000005740 13262465526 020364 0 ustar doug doug {
"formats": [
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formatname": "ROOT",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "TOPIC",
"fieldtype": "Text"
},
{
"fieldname": "TEXT",
"fieldtype": "Text",
"lines": 7
}
],
"formathtml": true,
"formatname": "TEXT_PARA",
"icon": "doc",
"outputlines": [
"{*TOPIC*}",
"{*TEXT*}"
],
"titleline": "{*TOPIC*}"
}
],
"nodes": [
{
"children": [
"14e55e4895a411e79cb17054d2175f18",
"14e5604695a411e79cb17054d2175f18",
"14e5616895a411e79cb17054d2175f18",
"14e5626c95a411e79cb17054d2175f18",
"14e5635c95a411e79cb17054d2175f18"
],
"data": {
"Name": "Character Format Examples"
},
"format": "ROOT",
"uid": "14e559fc95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"TEXT": "All titles can be set to bold by adding <b>...</b> tags in the Configure Data Types dialog's Output tab. The \"Allow HTML rich text in format\" option must also be checked in the Type Config tab.",
"TOPIC": "Bold Titles"
},
"format": "TEXT_PARA",
"uid": "14e55e4895a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"TEXT": "Font formatting commands can be found in the Edit menu and in the right-click context menu in the \"Data Editor\" view.",
"TOPIC": "Bold Text"
},
"format": "TEXT_PARA",
"uid": "14e5604695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"TEXT": "Text can also be set to various colors.",
"TOPIC": "Colored Text"
},
"format": "TEXT_PARA",
"uid": "14e5616895a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"TEXT": "Large text and small text",
"TOPIC": "Font Size"
},
"format": "TEXT_PARA",
"uid": "14e5626c95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"TEXT": "Text in italics and even text underlined",
"TOPIC": "Italics and Underline"
},
"format": "TEXT_PARA",
"uid": "14e5635c95a411e79cb17054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"14e559fc95a411e79cb17054d2175f18"
]
}
} TreeLine/samples/320en_sample_other_fields.trln 0000644 0001750 0001750 00000010664 13262465526 020551 0 ustar doug doug {
"formats": [
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "DEFAULT",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"childtype": "DEFAULT",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "FILE_INFO",
"outputlines": [
"{*Name*}",
"File: {*!File_Path*}/{*!File_Name*}",
"File Size: {*!File_Size*} bytes",
"File Modified: {*!File_Mod_Date*} {*!File_Mod_Time*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "File_Name",
"fieldtype": "Text"
},
{
"fieldname": "File_Path",
"fieldtype": "Text"
},
{
"fieldname": "File_Size",
"fieldtype": "Number",
"format": "#"
},
{
"fieldname": "File_Mod_Date",
"fieldtype": "Date",
"format": "%B %-d, %Y"
},
{
"fieldname": "File_Mod_Time",
"fieldtype": "Time",
"format": "%-I:%M:%S %p"
},
{
"fieldname": "File_Owner",
"fieldtype": "Text"
},
{
"fieldname": "Page_Number",
"fieldtype": "Text"
},
{
"fieldname": "Number_of_Pages",
"fieldtype": "Text"
}
],
"formatname": "INT_TL_FILE_DATA_FORM",
"outputlines": [
""
],
"titleline": ""
},
{
"childtype": "DEFAULT",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "PARENT_CHILD",
"outputlines": [
"{*Name*}",
"Parent's Name: {**Name*}",
"Children's Names: {*&Name*}"
],
"titleline": "{*Name*}"
},
{
"childtype": "DEFAULT",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "ROOT",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
}
],
"nodes": [
{
"children": [
"50cdc8dc95a411e79cb17054d2175f18",
"50cdce0495a411e79cb17054d2175f18",
"50cdcf7695a411e79cb17054d2175f18"
],
"data": {
"Name": "Other Field References"
},
"format": "ROOT",
"uid": "50cdc36e95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "File Information"
},
"format": "FILE_INFO",
"uid": "50cdc8dc95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Parent Info"
},
"format": "PARENT_CHILD",
"uid": "50cdce0495a411e79cb17054d2175f18"
},
{
"children": [
"50cdd27895a411e79cb17054d2175f18",
"50cdd3e095a411e79cb17054d2175f18",
"50cdd4e495a411e79cb17054d2175f18"
],
"data": {
"Name": "Child Info"
},
"format": "PARENT_CHILD",
"uid": "50cdcf7695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "First child"
},
"format": "DEFAULT",
"uid": "50cdd27895a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Next child"
},
"format": "DEFAULT",
"uid": "50cdd3e095a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Third child"
},
"format": "DEFAULT",
"uid": "50cdd4e495a411e79cb17054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"50cdc36e95a411e79cb17054d2175f18"
]
}
} TreeLine/samples/240en_sample_table_booklist.trln 0000644 0001750 0001750 00000006601 13262465526 021074 0 ustar doug doug {
"formats": [
{
"childtype": "BOOK",
"fields": [
{
"fieldname": "AuthorFirstName",
"fieldtype": "Text"
},
{
"fieldname": "AuthorLastName",
"fieldtype": "Text"
},
{
"fieldname": "WebSite",
"fieldtype": "ExternalLink",
"prefix": "<",
"suffix": ">"
}
],
"formatname": "AUTHOR",
"icon": "book_1",
"outputlines": [
"{*AuthorFirstName*} {*AuthorLastName*} {*WebSite*}"
],
"titleline": "{*AuthorFirstName*} {*AuthorLastName*}"
},
{
"fields": [
{
"fieldname": "Title",
"fieldtype": "Text"
},
{
"fieldname": "Copyright",
"fieldtype": "Number",
"format": "0000"
},
{
"fieldname": "Own",
"fieldtype": "Boolean",
"format": "yes/no"
},
{
"fieldname": "ReadDate",
"fieldtype": "Date",
"format": "%-m/%-d/%Y"
},
{
"fieldname": "Rating",
"fieldtype": "Choice",
"format": "1/2/3/4/5"
},
{
"fieldname": "Plot",
"fieldtype": "Text",
"lines": 7
}
],
"formathtml": true,
"formatname": "BOOK",
"icon": "book_3",
"outputlines": [
"Title: \"{*Title*}\"",
"Status: (c) {*Copyright*}",
"Own: {*Own*}",
"Last Read: {*ReadDate*}",
"Rating: {*Rating*}",
"Plot: {*Plot*}"
],
"spacebetween": false,
"tables": true,
"titleline": "\"{*Title*}\""
},
{
"childtype": "AUTHOR",
"fields": [
{
"fieldname": "NAME",
"fieldtype": "Text"
}
],
"formatname": "ROOT",
"outputlines": [
"{*NAME*}"
],
"titleline": "{*NAME*}"
}
],
"nodes": [
{
"children": [
"5cd2946cfe1011e7b8dc7054d2175f18",
"5cd2bffafe1011e7b8dc7054d2175f18"
],
"data": {
"NAME": "SF Books"
},
"format": "ROOT",
"uid": "5cd28d1efe1011e7b8dc7054d2175f18"
},
{
"children": [
"5cd2b3d4fe1011e7b8dc7054d2175f18",
"5cd2bdc0fe1011e7b8dc7054d2175f18"
],
"data": {
"AuthorFirstName": "Greg",
"AuthorLastName": "Bear",
"WebSite": "www.gregbear.com"
},
"format": "AUTHOR",
"uid": "5cd2946cfe1011e7b8dc7054d2175f18"
},
{
"children": [],
"data": {
"Copyright": "1999",
"Own": "false",
"Plot": "Evolution caused by virus begining again",
"Rating": "4",
"ReadDate": "2000-10-01",
"Title": "Darwin's Radio"
},
"format": "BOOK",
"uid": "5cd2b3d4fe1011e7b8dc7054d2175f18"
},
{
"children": [],
"data": {
"Copyright": "1985",
"Own": "true",
"Plot": "Smart viruses take over",
"Rating": "2",
"ReadDate": "1998-07-01",
"Title": "Blood Music"
},
"format": "BOOK",
"uid": "5cd2bdc0fe1011e7b8dc7054d2175f18"
},
{
"children": [
"5cd2c1bcfe1011e7b8dc7054d2175f18",
"5cd2c392fe1011e7b8dc7054d2175f18",
"5cd2c54afe1011e7b8dc7054d2175f18"
],
"data": {
"AuthorFirstName": "Orson Scott",
"AuthorLastName": "Card",
"WebSite": "www.hatrack.com"
},
"format": "AUTHOR",
"uid": "5cd2bffafe1011e7b8dc7054d2175f18"
},
{
"children": [],
"data": {
"Copyright": "1996",
"Own": "Yes",
"Plot": "Time travel to change history; discovery of America",
"Rating": "4",
"ReadDate": "1998-09-01",
"Title": "Pastwatch, The Redemption of Christopher Columbus"
},
"format": "BOOK",
"uid": "5cd2c1bcfe1011e7b8dc7054d2175f18"
},
{
"children": [],
"data": {
"Copyright": "1999",
"Own": "Yes",
"Plot": "Boy travels back to Russian fairy tale",
"Rating": "5",
"ReadDate": "2000-08-01",
"Title": "Enchantment"
},
"format": "BOOK",
"uid": "5cd2c392fe1011e7b8dc7054d2175f18"
},
{
"children": [],
"data": {
"Copyright": "1999",
"Own": "Yes",
"Plot": "Ender's Game from Bean's perspective",
"Rating": "5",
"ReadDate": "2001-05-01",
"Title": "Ender's Shadow"
},
"format": "BOOK",
"uid": "5cd2c54afe1011e7b8dc7054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"5cd28d1efe1011e7b8dc7054d2175f18"
]
}
} TreeLine/samples/310en_sample_conditional_todo.trln 0000644 0001750 0001750 00000013751 13262465526 021431 0 ustar doug doug {
"formats": [
{
"childtype": "TASK_UNDONE",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "ROOT",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"condition": "Done == \"true\"",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Done",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "false"
},
{
"fieldname": "Urgent",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "false"
}
],
"formathtml": true,
"formatname": "TASK_DONE",
"icon": "smiley_4",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Done",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "false"
},
{
"fieldname": "Urgent",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "false"
}
],
"formathtml": true,
"formatname": "TASK_UNDONE",
"generic": "TASK_DONE",
"icon": "smiley_2",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"condition": "Done == \"false\" and Urgent == \"true\"",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Done",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "false"
},
{
"fieldname": "Urgent",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "true"
}
],
"formathtml": true,
"formatname": "TASK_UNDONE_URGENT",
"generic": "TASK_DONE",
"icon": "smiley_5",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}!!!"
}
],
"nodes": [
{
"children": [
"4295fc4e95a411e79cb17054d2175f18",
"42960f7c95a411e79cb17054d2175f18"
],
"data": {
"Name": "Conditional Task List"
},
"format": "ROOT",
"uid": "4295faaa95a411e79cb17054d2175f18"
},
{
"children": [
"4295ff6e95a411e79cb17054d2175f18",
"429602ac95a411e79cb17054d2175f18",
"429603c495a411e79cb17054d2175f18",
"429607b695a411e79cb17054d2175f18",
"42960cca95a411e79cb17054d2175f18"
],
"data": {
"Name": "Home Tasks"
},
"format": "ROOT",
"uid": "4295fc4e95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Mow lawn",
"Urgent": "false"
},
"format": "TASK_UNDONE",
"uid": "4295ff6e95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Patch wall",
"Urgent": "false"
},
"format": "TASK_UNDONE",
"uid": "429602ac95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Vacuum",
"Urgent": "false"
},
"format": "TASK_UNDONE",
"uid": "429603c495a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Walk dog",
"Urgent": "true"
},
"format": "TASK_UNDONE_URGENT",
"uid": "429607b695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "true",
"Name": "Watch TV",
"Urgent": "false"
},
"format": "TASK_DONE",
"uid": "42960cca95a411e79cb17054d2175f18"
},
{
"children": [
"4296107695a411e79cb17054d2175f18",
"429611b695a411e79cb17054d2175f18",
"429612a695a411e79cb17054d2175f18",
"4296139695a411e79cb17054d2175f18"
],
"data": {
"Name": "Work Tasks"
},
"format": "ROOT",
"uid": "42960f7c95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "true",
"Name": "Play solitaire",
"Urgent": "false"
},
"format": "TASK_DONE",
"uid": "4296107695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Write documents",
"Urgent": "false"
},
"format": "TASK_UNDONE",
"uid": "429611b695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "true",
"Name": "Eat lunch",
"Urgent": "false"
},
"format": "TASK_DONE",
"uid": "429612a695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Compliment boss",
"Urgent": "true"
},
"format": "TASK_UNDONE_URGENT",
"uid": "4296139695a411e79cb17054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"4295faaa95a411e79cb17054d2175f18"
]
}
} TreeLine/samples/110en_sample_basic_longtext.trln 0000644 0001750 0001750 00000007322 13262465526 021101 0 ustar doug doug {
"formats": [
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Text",
"fieldtype": "HtmlText",
"lines": 12
}
],
"formathtml": true,
"formatname": "HTML_TEXT",
"outputlines": [
"{*Name*}",
"{*Text*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Text",
"fieldtype": "Text",
"lines": 12
}
],
"formathtml": true,
"formatname": "REGULAR_TEXT",
"outputlines": [
"{*Name*}",
"{*Text*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formatname": "ROOT",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Text",
"fieldtype": "SpacedText",
"lines": 12
}
],
"formathtml": true,
"formatname": "SPACED_TEXT",
"outputlines": [
"{*Name*}{*Text*}"
],
"titleline": "{*Name*}"
}
],
"nodes": [
{
"children": [
"ed02751e95a311e79cb17054d2175f18",
"ed02771c95a311e79cb17054d2175f18",
"ed027aaa95a311e79cb17054d2175f18",
"ed027f0095a311e79cb17054d2175f18"
],
"data": {
"Name": "Text Fields"
},
"format": "ROOT",
"uid": "ed0270d295a311e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Similar to Treepad",
"Text": "This file provides a single long text field for each node. This is similar to how the Treepad program on windows is usually used."
},
"format": "REGULAR_TEXT",
"uid": "ed02751e95a311e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Regular text field",
"Text": "The most commonly used field type is regular text. Formatting such as bold, italics, font sizes and font colors can be used from the Edit menu.
\n
\nIt preserves carriage return spaces, but not multiple spaces within a line."
},
"format": "REGULAR_TEXT",
"uid": "ed02771c95a311e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "HTML text field",
"Text": "An HTML field allows tags such as italics to be added manually.\n\nIt does not preserve white space.\n\nCharacters like <, >, and & must be escaped."
},
"format": "HTML_TEXT",
"uid": "ed027aaa95a311e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Spaced text field",
"Text": "A spaced text field preserves all white space.\n\nIt does not allow character formatting."
},
"format": "SPACED_TEXT",
"uid": "ed027f0095a311e79cb17054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"ed0270d295a311e79cb17054d2175f18"
]
}
} TreeLine/samples/330en_sample_math_fields.trln 0000644 0001750 0001750 00000015640 13262465526 020361 0 ustar doug doug {
"formats": [
{
"childtype": "PART",
"fields": [
{
"fieldname": "PartNumber",
"fieldtype": "Text"
},
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"eqn": "{**Level*} + 1",
"fieldname": "Level",
"fieldtype": "Math",
"format": "#"
},
{
"fieldname": "LaborCost",
"fieldtype": "Number",
"format": "0.00",
"prefix": "$"
},
{
"eqn": "sum({*&Cost*}) + {*LaborCost*}",
"fieldname": "Cost",
"fieldtype": "Math",
"format": "0.00",
"prefix": "$"
},
{
"eqn": "{**TotalCost*}",
"fieldname": "TotalCost",
"fieldtype": "Math",
"format": "0.00",
"prefix": "$"
},
{
"eqn": "{*Cost*} / {*TotalCost*} * 100",
"fieldname": "PercentCost",
"fieldtype": "Math",
"format": "0",
"suffix": "%"
}
],
"formatname": "ASSEMBLY",
"outputlines": [
"Part {*PartNumber*}",
"{*Name*}",
"Assembly Level: {*Level*}",
"Cost: {*Cost*}",
"Percent Cost: {*PercentCost*}"
],
"titleline": "{*PartNumber*} {*Name*}"
},
{
"fields": [
{
"fieldname": "PartNumber",
"fieldtype": "Text"
},
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"eqn": "{**Level*} + 1",
"fieldname": "Level",
"fieldtype": "Math",
"format": "#"
},
{
"fieldname": "Cost",
"fieldtype": "Number",
"format": "0.00",
"prefix": "$"
},
{
"eqn": "{*Cost*} / {**TotalCost*} * 100",
"fieldname": "PercentCost",
"fieldtype": "Math",
"format": "0",
"suffix": "%"
},
{
"eqn": "('Low') if ({*PercentCost*} < 20) else ('High')",
"fieldname": "CostLevel",
"fieldtype": "Math",
"format": "#.##",
"resulttype": "text"
}
],
"formatname": "PART",
"outputlines": [
"Part {*PartNumber*}",
"{*Name*}",
"Assembly Level: {*Level*}",
"Cost: {*Cost*}",
"Percent Cost: {*PercentCost*}",
"Cost Level: {*CostLevel*}"
],
"titleline": "{*PartNumber*} {*Name*}"
},
{
"childtype": "ASSEMBLY",
"fields": [
{
"fieldname": "PartNumber",
"fieldtype": "Text"
},
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Level",
"fieldtype": "Number",
"format": "#"
},
{
"fieldname": "LaborCost",
"fieldtype": "Number",
"format": "0.00",
"prefix": "$"
},
{
"eqn": "sum({*&Cost*}) + {*LaborCost*}",
"fieldname": "TotalCost",
"fieldtype": "Math",
"format": "0.00",
"prefix": "$"
}
],
"formatname": "TOP_ASSEMBLY",
"outputlines": [
"Part {*PartNumber*}",
"{*Name*}",
"Assembly Level: {*Level*}",
"Cost: {*TotalCost*}"
],
"titleline": "{*PartNumber*} {*Name*}"
}
],
"nodes": [
{
"children": [
"5f8ff84a95a411e79cb17054d2175f18",
"5f901e5695a411e79cb17054d2175f18"
],
"data": {
"LaborCost": "3.5",
"Level": "1",
"Name": "Widget Top Assembly",
"PartNumber": "123456",
"TotalCost": "23.89"
},
"format": "TOP_ASSEMBLY",
"uid": "5f8fdd3895a411e79cb17054d2175f18"
},
{
"children": [
"5f900eb695a411e79cb17054d2175f18",
"5f901d0c95a411e79cb17054d2175f18"
],
"data": {
"Cost": "10.040000000000001",
"LaborCost": "1.75",
"Level": "2",
"Name": "Lever Assembly",
"PartNumber": "456789",
"PercentCost": "42.02595228128925",
"TotalCost": "23.89"
},
"format": "ASSEMBLY",
"uid": "5f8ff84a95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Cost": "5.4",
"CostLevel": "High",
"Level": "3",
"Name": "Lever",
"PartNumber": "987654",
"PercentCost": "22.60359983256593"
},
"format": "PART",
"uid": "5f900eb695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Cost": "2.89",
"CostLevel": "Low",
"Level": "3",
"Name": "Lever Bolt",
"PartNumber": "998877",
"PercentCost": "12.097111762243616"
},
"format": "PART",
"uid": "5f901d0c95a411e79cb17054d2175f18"
},
{
"children": [
"5f901f6e95a411e79cb17054d2175f18",
"5f90208695a411e79cb17054d2175f18"
],
"data": {
"Cost": "10.35",
"LaborCost": "2.25",
"Level": "2",
"Name": "Bracket Assembly",
"PartNumber": "112233",
"PercentCost": "43.32356634575136",
"TotalCost": "23.89"
},
"format": "ASSEMBLY",
"uid": "5f901e5695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Cost": "6.2",
"CostLevel": "High",
"Level": "3",
"Name": "Bracket",
"PartNumber": "445566",
"PercentCost": "25.952281289242364"
},
"format": "PART",
"uid": "5f901f6e95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Cost": "1.9",
"CostLevel": "Low",
"Level": "3",
"Name": "Bracket Pin",
"PartNumber": "665544",
"PercentCost": "7.95311845960653"
},
"format": "PART",
"uid": "5f90208695a411e79cb17054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"5f8fdd3895a411e79cb17054d2175f18"
]
}
} TreeLine/samples/220en_sample_bookmarks.trln 0000644 0001750 0001750 00000030447 13262465526 020072 0 ustar doug doug {
"formats": [
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Link",
"fieldtype": "ExternalLink"
}
],
"formatname": "BOOKMARK",
"icon": "bookmark",
"outputlines": [
"{*Name*}",
"{*Link*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formatname": "FOLDER",
"icon": "folder_3",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "SEPARATOR",
"outputlines": [
"
| {1} | '. format(self.siblingPrefix, head)) self.siblingPrefix += '
|---|
tags to preserve spacing.
Does not use the rich text editor.
Provides methods to return formatted data.
"""
typeName = 'SpacedText'
showRichTextInCell = False
editorClassName = 'PlainTextEditor'
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the dict that defines this field's format
"""
super().__init__(name, formatData)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
if storedText:
storedText = '{0}'.format(storedText)
return super().formatOutput(storedText, oneLine, noHtml, formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Arguments:
storedText -- the source text to format
"""
return saxutils.unescape(storedText)
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Arguments:
editorText -- the new text entered into the editor
"""
return saxutils.escape(editorText)
def storedTextFromTitle(self, titleText):
"""Return new text to be stored based on title text edits.
Arguments:
titleText -- the new title text
"""
return self.storedText(titleText)
class NumberField(HtmlTextField):
"""Class to handle a general number field format type.
Stores options and format strings for a number field type.
Provides methods to return formatted data.
"""
typeName = 'Number'
defaultFormat = '#.##'
evalHtmlDefault = False
editorClassName = 'LineEditor'
sortTypeStr = '20_num'
formatHelpMenuList = [(_('Optional Digit\t#'), '#'),
(_('Required Digit\t0'), '0'),
(_('Digit or Space (external)\t'), ' '),
('', ''),
(_('Decimal Point\t.'), '.'),
(_('Decimal Comma\t,'), ','),
('', ''),
(_('Comma Separator\t\\,'), '\\,'),
(_('Dot Separator\t\\.'), '\\.'),
(_('Space Separator (internal)\t'), ' '),
('', ''),
(_('Optional Sign\t-'), '-'),
(_('Required Sign\t+'), '+'),
('', ''),
(_('Exponent (capital)\tE'), 'E'),
(_('Exponent (small)\te'), 'e')]
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the dict that defines this field's format
"""
super().__init__(name, formatData)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
try:
text = gennumber.GenNumber(storedText).numStr(self.format)
except ValueError:
text = _errorStr
return super().formatOutput(text, oneLine, noHtml, formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
if not storedText:
return ''
return gennumber.GenNumber(storedText).numStr(self.format)
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new text entered into the editor
"""
if not editorText:
return ''
return repr(gennumber.GenNumber().setFromStr(editorText, self.format))
def mathValue(self, node, zeroBlanks=True, noMarkup=True):
"""Return a numeric value to be used in math field equations.
Return None if blank and not zeroBlanks,
raise a ValueError if it isn't a number.
Arguments:
node -- the tree item storing the data
zeroBlanks -- replace blank field values with zeros if True
noMarkup -- not applicable to numbers
"""
storedText = node.data.get(self.name, '')
if storedText:
return gennumber.GenNumber(storedText).num
return 0 if zeroBlanks else None
def adjustedCompareValue(self, value):
"""Return value adjusted like the compareValue for use in conditionals.
Number version converts to a numeric value.
Arguments:
value -- the comparison value to adjust
"""
try:
return gennumber.GenNumber(value).num
except ValueError:
return 0
class MathField(HtmlTextField):
"""Class to handle a math calculation field type.
Stores options and format strings for a math field type.
Provides methods to return formatted data.
"""
typeName = 'Math'
defaultFormat = '#.##'
evalHtmlDefault = False
fixEvalHtmlSetting = False
editorClassName = 'ReadOnlyEditor'
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the attributes that define this field's format
"""
super().__init__(name, formatData)
self.equation = None
self.resultType = MathResult[formatData.get('resulttype', 'number')]
equationText = formatData.get('eqn', '').strip()
if equationText:
self.equation = matheval.MathEquation(equationText)
try:
self.equation.validate()
except ValueError:
self.equation = None
def formatData(self):
"""Return a dictionary of this field's attributes.
Add the math equation to the standard XML output.
"""
formatData = super().formatData()
if self.equation:
formatData['eqn'] = self.equation.equationText()
if self.resultType != MathResult.number:
formatData['resulttype'] = self.resultType.name
return formatData
def setFormat(self, format):
"""Set the format string and initialize as required.
Arguments:
format -- the new format string
"""
if not hasattr(self, 'equation'):
self.equation = None
self.resultType = MathResult.number
super().setFormat(format)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
text = storedText
try:
if self.resultType == MathResult.number:
text = gennumber.GenNumber(text).numStr(self.format)
elif self.resultType == MathResult.date:
date = datetime.datetime.strptime(text,
DateField.isoFormat).date()
text = date.strftime(adjOutDateFormat(self.format))
elif self.resultType == MathResult.time:
time = datetime.datetime.strptime(text,
TimeField.isoFormat).time()
text = time.strftime(adjOutDateFormat(self.format))
elif self.resultType == MathResult.boolean:
text = genboolean.GenBoolean(text).boolStr(self.format)
except ValueError:
text = _errorStr
return super().formatOutput(text, oneLine, noHtml, formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
if not storedText:
return ''
if self.resultType == MathResult.number:
return gennumber.GenNumber(storedText).numStr(self.format)
if self.resultType == MathResult.date:
date = datetime.datetime.strptime(storedText,
DateField.isoFormat).date()
editorFormat = adjOutDateFormat(globalref.
genOptions['EditDateFormat'])
return date.strftime(editorFormat)
if self.resultType == MathResult.time:
time = datetime.datetime.strptime(storedText,
TimeField.isoFormat).time()
editorFormat = adjOutDateFormat(globalref.
genOptions['EditTimeFormat'])
return time.strftime(editorFormat)
if self.resultType == MathResult.boolean:
return genboolean.GenBoolean(storedText).boolStr(self.format)
if storedText == _errorStr:
raise ValueError
return storedText
def equationText(self):
"""Return the current equation text.
"""
if self.equation:
return self.equation.equationText()
return ''
def equationValue(self, node):
"""Return a text value from the result of the equation.
Returns the '#####' error string for illegal math operations.
Arguments:
node -- the tree item with this equation
"""
if self.equation:
zeroValue = _mathResultBlank[self.resultType]
try:
num = self.equation.equationValue(node, self.resultType,
zeroValue, not self.evalHtml)
except ValueError:
return _errorStr
if num == None:
return ''
if self.resultType == MathResult.date:
date = DateField.refDate + datetime.timedelta(days=num)
return date.strftime(DateField.isoFormat)
if self.resultType == MathResult.time:
dateTime = datetime.datetime.combine(DateField.refDate,
TimeField.refTime)
dateTime = dateTime + datetime.timedelta(seconds=num)
time = dateTime.time()
return time.strftime(TimeField.isoFormat)
text = str(num)
if not self.evalHtml:
text = saxutils.escape(text)
return text
return ''
def resultClass(self):
"""Return the result type's field class.
"""
return globals()[self.resultType.name.capitalize() + 'Field']
def changeResultType(self, resultType):
"""Change the result type and reset the output format.
Arguments:
resultType -- the new result type
"""
if resultType != self.resultType:
self.resultType = resultType
self.setFormat(self.resultClass().defaultFormat)
def mathValue(self, node, zeroBlanks=True, noMarkup=True):
"""Return a numeric value to be used in math field equations.
Return None if blank and not zeroBlanks,
raise a ValueError if it isn't valid.
Arguments:
node -- the tree item storing the data
zeroBlanks -- replace blank field values with zeros if True
noMarkup -- if true, remove html markup
"""
storedText = node.data.get(self.name, '')
if storedText:
if self.resultType == MathResult.number:
return gennumber.GenNumber(storedText).num
if self.resultType == MathResult.date:
date = datetime.datetime.strptime(storedText,
DateField.isoFormat).date()
return (date - DateField.refDate).days
if self.resultType == MathResult.time:
time = datetime.datetime.strptime(storedText,
TimeField.isoFormat).time()
return (time - TimeField.refTime).seconds
if self.resultType == MathResult.boolean:
return genboolean.GenBoolean(storedText).value
if noMarkup:
storedText = removeMarkup(storedText)
return storedText
return _mathResultBlank[self.resultType] if zeroBlanks else None
def adjustedCompareValue(self, value):
"""Return value adjusted like the compareValue for use in conditionals.
Number version converts to a numeric value.
Arguments:
value -- the comparison value to adjust
"""
try:
if self.resultType == MathResult.number:
return gennumber.GenNumber(value).num
if self.resultType == MathResult.date:
date = datetime.datetime.strptime(value,
DateField.isoFormat).date()
return date.strftime(DateField.isoFormat)
if self.resultType == MathResult.time:
time = datetime.datetime.strptime(value,
TimeField.isoFormat).time()
return time.strftime(TimeField.isoFormat)
if self.resultType == MathResult.boolean:
return genboolean.GenBoolean(value).value
return value.lower()
except ValueError:
return 0
def sortKey(self, node):
"""Return a tuple with field type and comparison value for sorting.
Allows different types to be sorted.
Arguments:
node -- the tree item storing the data
"""
return (self.resultClass().sortTypeStr, self.compareValue(node))
def getFormatHelpMenuList(self):
"""Return the list of descriptions and keys for the format help menu.
"""
return self.resultClass().formatHelpMenuList
class NumberingField(HtmlTextField):
"""Class to handle formats for hierarchical node numbering.
Stores options and format strings for a node numbering field type.
Provides methods to return formatted node numbers.
"""
typeName = 'Numbering'
defaultFormat = '1..'
evalHtmlDefault = False
editorClassName = 'LineEditor'
sortTypeStr = '10_numbering'
formatHelpMenuList = [(_('Number\t1'), '1'),
(_('Capital Letter\tA'), 'A'),
(_('Small Letter\ta'), 'a'),
(_('Capital Roman Numeral\tI'), 'I'),
(_('Small Roman Numeral\ti'), 'i'),
('', ''),
(_('Level Separator\t/'), '/'),
(_('Section Separator\t.'), '.'),
('', ''),
(_('"/" Character\t//'), '//'),
(_('"." Character\t..'), '..'),
('', ''),
(_('Outline Example\tI../A../1../a)/i)'),
'I../A../1../a)/i)'),
(_('Section Example\t1.1.1.1'), '1.1.1.1')]
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the attributes that define this field's format
"""
self.numFormat = None
super().__init__(name, formatData)
def setFormat(self, format):
"""Set the format string and initialize as required.
Arguments:
format -- the new format string
"""
self.numFormat = numbering.NumberingGroup(format)
super().setFormat(format)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
try:
text = self.numFormat.numString(storedText)
except ValueError:
text = _errorStr
return super().formatOutput(text, oneLine, noHtml, formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
if storedText:
checkData = [int(num) for num in storedText.split('.')]
return storedText
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new text entered into the editor
"""
if editorText:
checkData = [int(num) for num in editorText.split('.')]
return editorText
def adjustedCompareValue(self, value):
"""Return value adjusted like the compareValue for use in conditionals.
Number version converts to a numeric value.
Arguments:
value -- the comparison value to adjust
"""
if value:
try:
return [int(num) for num in value.split('.')]
except ValueError:
pass
return [0]
class DateField(HtmlTextField):
"""Class to handle a general date field format type.
Stores options and format strings for a date field type.
Provides methods to return formatted data.
"""
typeName = 'Date'
defaultFormat = '%B %-d, %Y'
isoFormat = '%Y-%m-%d'
evalHtmlDefault = False
fixEvalHtmlSetting = False
editorClassName = 'DateEditor'
refDate = datetime.date(1970, 1, 1)
sortTypeStr = '40_date'
formatHelpMenuList = [(_('Day (1 or 2 digits)\t%-d'), '%-d'),
(_('Day (2 digits)\t%d'), '%d'), ('', ''),
(_('Weekday Abbreviation\t%a'), '%a'),
(_('Weekday Name\t%A'), '%A'), ('', ''),
(_('Month (1 or 2 digits)\t%-m'), '%-m'),
(_('Month (2 digits)\t%m'), '%m'),
(_('Month Abbreviation\t%b'), '%b'),
(_('Month Name\t%B'), '%B'), ('', ''),
(_('Year (2 digits)\t%y'), '%y'),
(_('Year (4 digits)\t%Y'), '%Y'), ('', ''),
(_('Week Number (0 to 53)\t%-U'), '%-U'),
(_('Day of year (1 to 366)\t%-j'), '%-j')]
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the dict that defines this field's format
"""
super().__init__(name, formatData)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
try:
date = datetime.datetime.strptime(storedText,
DateField.isoFormat).date()
text = date.strftime(adjOutDateFormat(self.format))
except ValueError:
text = _errorStr
if not self.evalHtml:
text = saxutils.escape(text)
return super().formatOutput(text, oneLine, noHtml, formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
if not storedText:
return ''
date = datetime.datetime.strptime(storedText,
DateField.isoFormat).date()
editorFormat = adjOutDateFormat(globalref.genOptions['EditDateFormat'])
return date.strftime(editorFormat)
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Two digit years are interpretted as 1950-2049.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new text entered into the editor
"""
editorText = _multipleSpaceRegEx.sub(' ', editorText.strip())
if not editorText:
return ''
editorFormat = adjInDateFormat(globalref.genOptions['EditDateFormat'])
try:
date = datetime.datetime.strptime(editorText, editorFormat).date()
except ValueError: # allow use of a 4-digit year to fix invalid dates
fullYearFormat = editorFormat.replace('%y', '%Y')
if fullYearFormat != editorFormat:
date = datetime.datetime.strptime(editorText,
fullYearFormat).date()
else:
raise
return date.strftime(DateField.isoFormat)
def getInitDefault(self):
"""Return the initial stored value for newly created nodes.
"""
if self.initDefault == _dateStampString:
date = datetime.date.today()
return date.strftime(DateField.isoFormat)
return super().getInitDefault()
def setInitDefault(self, editorText):
"""Set the default initial value from editor text.
The function for default text field just returns the stored text.
Arguments:
editorText -- the new text entered into the editor
"""
if editorText == _dateStampString:
self.initDefault = _dateStampString
else:
super().setInitDefault(editorText)
def getEditorInitDefault(self):
"""Return initial value in editor format.
"""
if self.initDefault == _dateStampString:
return _dateStampString
return super().getEditorInitDefault()
def initDefaultChoices(self):
"""Return a list of choices for setting the init default.
"""
return [_dateStampString]
def mathValue(self, node, zeroBlanks=True, noMarkup=True):
"""Return a numeric value to be used in math field equations.
Return None if blank and not zeroBlanks,
raise a ValueError if it isn't a valid date.
Arguments:
node -- the tree item storing the data
zeroBlanks -- replace blank field values with zeros if True
"""
storedText = node.data.get(self.name, '')
if storedText:
date = datetime.datetime.strptime(storedText,
DateField.isoFormat).date()
return (date - DateField.refDate).days
return 0 if zeroBlanks else None
def compareValue(self, node):
"""Return a value for comparison to other nodes and for sorting.
Returns lowercase text for text fields or numbers for non-text fields.
Date field uses ISO date format (YYY-MM-DD).
Arguments:
node -- the tree item storing the data
"""
return node.data.get(self.name, '')
def adjustedCompareValue(self, value):
"""Return value adjusted like the compareValue for use in conditionals.
Date version converts to an ISO date format (YYYY-MM-DD).
Arguments:
value -- the comparison value to adjust
"""
value = _multipleSpaceRegEx.sub(' ', value.strip())
if not value:
return ''
if value == _dateStampString:
date = datetime.date.today()
return date.strftime(DateField.isoFormat)
try:
return self.storedText(value)
except ValueError:
return value
class TimeField(HtmlTextField):
"""Class to handle a general time field format type
Stores options and format strings for a time field type.
Provides methods to return formatted data.
"""
typeName = 'Time'
defaultFormat = '%-I:%M:%S %p'
isoFormat = '%H:%M:%S.%f'
evalHtmlDefault = False
fixEvalHtmlSetting = False
editorClassName = 'TimeEditor'
numChoiceColumns = 2
autoAddChoices = False
refTime = datetime.time()
sortTypeStr = '50_time'
formatHelpMenuList = [(_('Hour (0-23, 1 or 2 digits)\t%-H'), '%-H'),
(_('Hour (00-23, 2 digits)\t%H'), '%H'),
(_('Hour (1-12, 1 or 2 digits)\t%-I'), '%-I'),
(_('Hour (01-12, 2 digits)\t%I'), '%I'), ('', ''),
(_('Minute (1 or 2 digits)\t%-M'), '%-M'),
(_('Minute (2 digits)\t%M'), '%M'), ('', ''),
(_('Second (1 or 2 digits)\t%-S'), '%-S'),
(_('Second (2 digits)\t%S'), '%S'), ('', ''),
(_('Microseconds (6 digits)\t%f'), '%f'), ('', ''),
(_('AM/PM\t%p'), '%p')]
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the attributes that define this field's format
"""
super().__init__(name, formatData)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
try:
time = datetime.datetime.strptime(storedText,
TimeField.isoFormat).time()
outFormat = adjOutDateFormat(self.format)
outFormat = adjTimeAmPm(outFormat, time)
text = time.strftime(outFormat)
except ValueError:
text = _errorStr
if not self.evalHtml:
text = saxutils.escape(text)
return super().formatOutput(text, oneLine, noHtml, formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
if not storedText:
return ''
time = datetime.datetime.strptime(storedText,
TimeField.isoFormat).time()
editorFormat = adjOutDateFormat(globalref.genOptions['EditTimeFormat'])
editorFormat = adjTimeAmPm(editorFormat, time)
return time.strftime(editorFormat)
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new text entered into the editor
"""
editorText = _multipleSpaceRegEx.sub(' ', editorText.strip())
if not editorText:
return ''
editorFormat = adjInDateFormat(globalref.genOptions['EditTimeFormat'])
time = None
try:
time = datetime.datetime.strptime(editorText, editorFormat).time()
except ValueError:
noSecFormat = editorFormat.replace(':%S', '')
noSecFormat = _multipleSpaceRegEx.sub(' ', noSecFormat.strip())
try:
time = datetime.datetime.strptime(editorText,
noSecFormat).time()
except ValueError:
for altFormat in (editorFormat, noSecFormat):
noAmFormat = altFormat.replace('%p', '')
noAmFormat = _multipleSpaceRegEx.sub(' ',
noAmFormat.strip())
try:
time = datetime.datetime.strptime(editorText,
noAmFormat).time()
break
except ValueError:
pass
if not time:
raise ValueError
return time.strftime(TimeField.isoFormat)
def annotatedComboChoices(self, editorText):
"""Return a list of (choice, annotation) tuples for the combo box.
Arguments:
editorText -- the text entered into the editor
"""
editorFormat = adjOutDateFormat(globalref.genOptions['EditTimeFormat'])
choices = [(datetime.datetime.now().time().strftime(editorFormat),
'({0})'.format(_timeStampString))]
for hour in (6, 9, 12, 15, 18, 21, 0):
choices.append((datetime.time(hour).strftime(editorFormat), ''))
return choices
def getInitDefault(self):
"""Return the initial stored value for newly created nodes.
"""
if self.initDefault == _timeStampString:
time = datetime.datetime.now().time()
return time.strftime(TimeField.isoFormat)
return super().getInitDefault()
def setInitDefault(self, editorText):
"""Set the default initial value from editor text.
The function for default text field just returns the stored text.
Arguments:
editorText -- the new text entered into the editor
"""
if editorText == _timeStampString:
self.initDefault = _timeStampString
else:
super().setInitDefault(editorText)
def getEditorInitDefault(self):
"""Return initial value in editor format.
"""
if self.initDefault == _timeStampString:
return _timeStampString
return super().getEditorInitDefault()
def initDefaultChoices(self):
"""Return a list of choices for setting the init default.
"""
return [_timeStampString]
def mathValue(self, node, zeroBlanks=True, noMarkup=True):
"""Return a numeric value to be used in math field equations.
Return None if blank and not zeroBlanks,
raise a ValueError if it isn't a valid time.
Arguments:
node -- the tree item storing the data
zeroBlanks -- replace blank field values with zeros if True
"""
storedText = node.data.get(self.name, '')
if storedText:
time = datetime.datetime.strptime(storedText,
TimeField.isoFormat).time()
dateTime = datetime.datetime.combine(DateField.refDate, time)
refDateTime = datetime.datetime.combine(DateField.refDate,
TimeField.refTime)
return (dateTime - refDateTime).seconds
return 0 if zeroBlanks else None
def compareValue(self, node):
"""Return a value for comparison to other nodes and for sorting.
Returns lowercase text for text fields or numbers for non-text fields.
Time field uses HH:MM:SS format.
Arguments:
node -- the tree item storing the data
"""
return node.data.get(self.name, '')
def adjustedCompareValue(self, value):
"""Return value adjusted like the compareValue for use in conditionals.
Time version converts to HH:MM:SS format.
Arguments:
value -- the comparison value to adjust
"""
value = _multipleSpaceRegEx.sub(' ', value.strip())
if not value:
return ''
if value == _timeStampString:
time = datetime.datetime.now().time()
return time.strftime(TimeField.isoFormat)
try:
return self.storedText(value)
except ValueError:
return value
class DateTimeField(HtmlTextField):
"""Class to handle a general date and time field format type.
Stores options and format strings for a date and time field type.
Provides methods to return formatted data.
"""
typeName = 'DateTime'
defaultFormat = '%B %-d, %Y %-I:%M:%S %p'
isoFormat = '%Y-%m-%d %H:%M:%S.%f'
evalHtmlDefault = False
fixEvalHtmlSetting = False
editorClassName = 'DateTimeEditor'
refDateTime = datetime.datetime(1970, 1, 1)
sortTypeStr ='45_datetime'
formatHelpMenuList = [(_('Day (1 or 2 digits)\t%-d'), '%-d'),
(_('Day (2 digits)\t%d'), '%d'), ('', ''),
(_('Weekday Abbreviation\t%a'), '%a'),
(_('Weekday Name\t%A'), '%A'), ('', ''),
(_('Month (1 or 2 digits)\t%-m'), '%-m'),
(_('Month (2 digits)\t%m'), '%m'),
(_('Month Abbreviation\t%b'), '%b'),
(_('Month Name\t%B'), '%B'), ('', ''),
(_('Year (2 digits)\t%y'), '%y'),
(_('Year (4 digits)\t%Y'), '%Y'), ('', ''),
(_('Week Number (0 to 53)\t%-U'), '%-U'),
(_('Day of year (1 to 366)\t%-j'), '%-j'),
(_('Hour (0-23, 1 or 2 digits)\t%-H'), '%-H'),
(_('Hour (00-23, 2 digits)\t%H'), '%H'),
(_('Hour (1-12, 1 or 2 digits)\t%-I'), '%-I'),
(_('Hour (01-12, 2 digits)\t%I'), '%I'), ('', ''),
(_('Minute (1 or 2 digits)\t%-M'), '%-M'),
(_('Minute (2 digits)\t%M'), '%M'), ('', ''),
(_('Second (1 or 2 digits)\t%-S'), '%-S'),
(_('Second (2 digits)\t%S'), '%S'), ('', ''),
(_('Microseconds (6 digits)\t%f'), '%f'), ('', ''),
(_('AM/PM\t%p'), '%p')]
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the dict that defines this field's format
"""
super().__init__(name, formatData)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
try:
dateTime = datetime.datetime.strptime(storedText,
DateTimeField.isoFormat)
outFormat = adjOutDateFormat(self.format)
outFormat = adjTimeAmPm(outFormat, dateTime)
text = dateTime.strftime(outFormat)
except ValueError:
text = _errorStr
if not self.evalHtml:
text = saxutils.escape(text)
return super().formatOutput(text, oneLine, noHtml, formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
if not storedText:
return ''
dateTime = datetime.datetime.strptime(storedText,
DateTimeField.isoFormat)
editorFormat = '{0} {1}'.format(globalref.genOptions['EditDateFormat'],
globalref.genOptions['EditTimeFormat'])
editorFormat = adjOutDateFormat(editorFormat)
editorFormat = adjTimeAmPm(editorFormat, dateTime)
return dateTime.strftime(editorFormat)
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Two digit years are interpretted as 1950-2049.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new text entered into the editor
"""
editorText = _multipleSpaceRegEx.sub(' ', editorText.strip())
if not editorText:
return ''
editorFormat = '{0} {1}'.format(globalref.genOptions['EditDateFormat'],
globalref.genOptions['EditTimeFormat'])
editorFormat = adjInDateFormat(editorFormat)
dateTime = None
try:
dateTime = datetime.datetime.strptime(editorText, editorFormat)
except ValueError:
noSecFormat = editorFormat.replace(':%S', '')
noSecFormat = _multipleSpaceRegEx.sub(' ', noSecFormat.strip())
altFormats = [editorFormat, noSecFormat]
for altFormat in altFormats[:]:
noAmFormat = altFormat.replace('%p', '')
noAmFormat = _multipleSpaceRegEx.sub(' ', noAmFormat.strip())
altFormats.append(noAmFormat)
for altFormat in altFormats[:]:
fullYearFormat = altFormat.replace('%y', '%Y')
altFormats.append(fullYearFormat)
for editorFormat in altFormats[1:]:
try:
dateTime = datetime.datetime.strptime(editorText,
editorFormat)
break
except ValueError:
pass
if not dateTime:
raise ValueError
return dateTime.strftime(DateTimeField.isoFormat)
def getInitDefault(self):
"""Return the initial stored value for newly created nodes.
"""
if self.initDefault == _timeStampString:
dateTime = datetime.datetime.now()
return dateTime.strftime(DateTimeField.isoFormat)
return super().getInitDefault()
def setInitDefault(self, editorText):
"""Set the default initial value from editor text.
The function for default text field just returns the stored text.
Arguments:
editorText -- the new text entered into the editor
"""
if editorText == _timeStampString:
self.initDefault = _timeStampString
else:
super().setInitDefault(editorText)
def getEditorInitDefault(self):
"""Return initial value in editor format.
"""
if self.initDefault == _timeStampString:
return _timeStampString
return super().getEditorInitDefault()
def initDefaultChoices(self):
"""Return a list of choices for setting the init default.
"""
return [_timeStampString]
def mathValue(self, node, zeroBlanks=True, noMarkup=True):
"""Return a numeric value to be used in math field equations.
Return None if blank and not zeroBlanks,
raise a ValueError if it isn't a valid time.
Arguments:
node -- the tree item storing the data
zeroBlanks -- replace blank field values with zeros if True
"""
storedText = node.data.get(self.name, '')
if storedText:
dateTime = datetime.datetime.strptime(storedText,
DateTimeField.isoFormat)
return (dateTime - DateTimeField.refDateTime).total_seconds()
return 0 if zeroBlanks else None
def compareValue(self, node):
"""Return a value for comparison to other nodes and for sorting.
Returns lowercase text for text fields or numbers for non-text fields.
DateTime field uses YYYY-MM-DD HH:MM:SS format.
Arguments:
node -- the tree item storing the data
"""
return node.data.get(self.name, '')
def adjustedCompareValue(self, value):
"""Return value adjusted like the compareValue for use in conditionals.
Time version converts to HH:MM:SS format.
Arguments:
value -- the comparison value to adjust
"""
value = _multipleSpaceRegEx.sub(' ', value.strip())
if not value:
return ''
if value == _timeStampString:
dateTime = datetime.datetime.now()
return dateTime.strftime(DateTimeField.isoFormat)
try:
return self.storedText(value)
except ValueError:
return value
class ChoiceField(HtmlTextField):
"""Class to handle a field with pre-defined, individual text choices.
Stores options and format strings for a choice field type.
Provides methods to return formatted data.
"""
typeName = 'Choice'
editSep = '/'
defaultFormat = '1/2/3/4'
evalHtmlDefault = False
fixEvalHtmlSetting = False
editorClassName = 'ComboEditor'
numChoiceColumns = 1
autoAddChoices = False
formatHelpMenuList = [(_('Separator\t/'), '/'), ('', ''),
(_('"/" Character\t//'), '//'), ('', ''),
(_('Example\t1/2/3/4'), '1/2/3/4')]
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the dict that defines this field's format
"""
super().__init__(name, formatData)
def setFormat(self, format):
"""Set the format string and initialize as required.
Arguments:
format -- the new format string
"""
super().setFormat(format)
self.choiceList = self.splitText(self.format)
if self.evalHtml:
self.choices = set(self.choiceList)
else:
self.choices = set([saxutils.escape(choice) for choice in
self.choiceList])
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
if storedText not in self.choices:
storedText = _errorStr
return super().formatOutput(storedText, oneLine, noHtml, formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
if storedText and storedText not in self.choices:
raise ValueError
if self.evalHtml:
return storedText
return saxutils.unescape(storedText)
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new text entered into the editor
"""
if not self.evalHtml:
editorText = saxutils.escape(editorText)
if not editorText or editorText in self.choices:
return editorText
raise ValueError
def comboChoices(self):
"""Return a list of choices for the combo box.
"""
return self.choiceList
def initDefaultChoices(self):
"""Return a list of choices for setting the init default.
"""
return self.choiceList
def splitText(self, textStr):
"""Split textStr using editSep, return a list of strings.
Double editSep's are not split (become single).
Removes duplicates and empty strings.
Arguments:
textStr -- the text to split
"""
result = []
textStr = textStr.replace(self.editSep * 2, '\0')
for text in textStr.split(self.editSep):
text = text.strip().replace('\0', self.editSep)
if text and text not in result:
result.append(text)
return result
class AutoChoiceField(HtmlTextField):
"""Class to handle a field with automatically populated text choices.
Stores options and possible entries for an auto-choice field type.
Provides methods to return formatted data.
"""
typeName = 'AutoChoice'
evalHtmlDefault = False
fixEvalHtmlSetting = False
editorClassName = 'ComboEditor'
numChoiceColumns = 1
autoAddChoices = True
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the attributes that define this field's format
"""
super().__init__(name, formatData)
self.choices = set()
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Arguments:
storedText -- the source text to format
"""
if self.evalHtml:
return storedText
return saxutils.unescape(storedText)
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Arguments:
editorText -- the new text entered into the editor
"""
if self.evalHtml:
return editorText
return saxutils.escape(editorText)
def comboChoices(self):
"""Return a list of choices for the combo box.
"""
if self.evalHtml:
choices = self.choices
else:
choices = [saxutils.unescape(text) for text in
self.choices]
return sorted(choices, key=str.lower)
def addChoice(self, text):
"""Add a new choice.
Arguments:
text -- the choice to be added
"""
if text:
self.choices.add(text)
def clearChoices(self):
"""Remove all current choices.
"""
self.choices = set()
class CombinationField(ChoiceField):
"""Class to handle a field with multiple pre-defined text choices.
Stores options and format strings for a combination field type.
Provides methods to return formatted data.
"""
typeName = 'Combination'
editorClassName = 'CombinationEditor'
numChoiceColumns = 2
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the dict that defines this field's format
"""
super().__init__(name, formatData)
def setFormat(self, format):
"""Set the format string and initialize as required.
Arguments:
format -- the new format string
"""
TextField.setFormat(self, format)
if not self.evalHtml:
format = saxutils.escape(format)
self.choiceList = self.splitText(format)
self.choices = set(self.choiceList)
self.outputSep = ''
def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None):
"""Return formatted output text for this field in this node.
Sets output separator prior to calling base class methods.
Arguments:
node -- the tree item storing the data
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
spotRef -- optional, used for ancestor field refs
"""
self.outputSep = node.formatRef.outputSeparator
return super().outputText(node, oneLine, noHtml, formatHtml, spotRef)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
selections, valid = self.sortedSelections(storedText)
if valid:
result = self.outputSep.join(selections)
else:
result = _errorStr
return TextField.formatOutput(self, result, oneLine, noHtml,
formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
selections = set(self.splitText(storedText))
if selections.issubset(self.choices):
if self.evalHtml:
return storedText
return saxutils.unescape(storedText)
raise ValueError
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new text entered into the editor
"""
if not self.evalHtml:
editorText = saxutils.escape(editorText)
selections, valid = self.sortedSelections(editorText)
if not valid:
raise ValueError
return self.joinText(selections)
def comboChoices(self):
"""Return a list of choices for the combo box.
"""
if self.evalHtml:
return self.choiceList
return [saxutils.unescape(text) for text in self.choiceList]
def comboActiveChoices(self, editorText):
"""Return a sorted list of choices currently in editorText.
Arguments:
editorText -- the text entered into the editor
"""
selections, valid = self.sortedSelections(saxutils.escape(editorText))
if self.evalHtml:
return selections
return [saxutils.unescape(text) for text in selections]
def initDefaultChoices(self):
"""Return a list of choices for setting the init default.
"""
return []
def sortedSelections(self, inText):
"""Split inText using editSep and sort like format string.
Return a tuple of resulting selection list and bool validity.
Valid if all choices are in the format string.
Arguments:
inText -- the text to split and sequence
"""
selections = set(self.splitText(inText))
result = [text for text in self.choiceList if text in selections]
return (result, len(selections) == len(result))
def joinText(self, textList):
"""Join the text list using editSep, return the string.
Any editSep in text items become double.
Arguments:
textList -- the list of text items to join
"""
return self.editSep.join([text.replace(self.editSep, self.editSep * 2)
for text in textList])
class AutoCombinationField(CombinationField):
"""Class for a field with multiple automatically populated text choices.
Stores options and possible entries for an auto-choice field type.
Provides methods to return formatted data.
"""
typeName = 'AutoCombination'
autoAddChoices = True
defaultFormat = ''
formatHelpMenuList = []
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the attributes that define this field's format
"""
super().__init__(name, formatData)
self.choices = set()
self.outputSep = ''
def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None):
"""Return formatted output text for this field in this node.
Sets output separator prior to calling base class methods.
Arguments:
node -- the tree item storing the data
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
spotRef -- optional, used for ancestor field refs
"""
self.outputSep = node.formatRef.outputSeparator
return super().outputText(node, oneLine, noHtml, formatHtml, spotRef)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
result = self.outputSep.join(self.splitText(storedText))
return TextField.formatOutput(self, result, oneLine, noHtml,
formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Arguments:
storedText -- the source text to format
"""
if self.evalHtml:
return storedText
return saxutils.unescape(storedText)
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Also resets outputSep, to be defined at the next output.
Arguments:
editorText -- the new text entered into the editor
"""
self.outputSep = ''
if not self.evalHtml:
editorText = saxutils.escape(editorText)
selections = sorted(self.splitText(editorText), key=str.lower)
return self.joinText(selections)
def comboChoices(self):
"""Return a list of choices for the combo box.
"""
if self.evalHtml:
choices = self.choices
else:
choices = [saxutils.unescape(text) for text in
self.choices]
return sorted(choices, key=str.lower)
def comboActiveChoices(self, editorText):
"""Return a sorted list of choices currently in editorText.
Arguments:
editorText -- the text entered into the editor
"""
selections, valid = self.sortedSelections(saxutils.escape(editorText))
if self.evalHtml:
return selections
return [saxutils.unescape(text) for text in selections]
def sortedSelections(self, inText):
"""Split inText using editSep and sort like format string.
Return a tuple of resulting selection list and bool validity.
This version always returns valid.
Arguments:
inText -- the text to split and sequence
"""
selections = sorted(self.splitText(inText), key=str.lower)
return (selections, True)
def addChoice(self, text):
"""Add a new choice.
Arguments:
text -- the stored text combinations to be added
"""
for choice in self.splitText(text):
self.choices.add(choice)
def clearChoices(self):
"""Remove all current choices.
"""
self.choices = set()
class BooleanField(ChoiceField):
"""Class to handle a general boolean field format type.
Stores options and format strings for a boolean field type.
Provides methods to return formatted data.
"""
typeName = 'Boolean'
defaultFormat = _('yes/no')
evalHtmlDefault = False
fixEvalHtmlSetting = False
sortTypeStr ='30_bool'
formatHelpMenuList = [(_('true/false'), 'true/false'),
(_('T/F'), 'T/F'), ('', ''),
(_('yes/no'), 'yes/no'),
(_('Y/N'), 'Y/N'), ('', ''),
('1/0', '1/0')]
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the dict that defines this field's format
"""
super().__init__(name, formatData)
def setFormat(self, format):
"""Set the format string and initialize as required.
Arguments:
format -- the new format string
"""
HtmlTextField.setFormat(self, format)
self.strippedFormat = removeMarkup(self.format)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
try:
text = genboolean.GenBoolean(storedText).boolStr(self.format)
except ValueError:
text = _errorStr
if not self.evalHtml:
text = saxutils.escape(text)
return HtmlTextField.formatOutput(self, text, oneLine, noHtml,
formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
if not storedText:
return ''
boolFormat = self.strippedFormat if self.evalHtml else self.format
return genboolean.GenBoolean(storedText).boolStr(boolFormat)
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new text entered into the editor
"""
if not editorText:
return ''
boolFormat = self.strippedFormat if self.evalHtml else self.format
try:
return repr(genboolean.GenBoolean().setFromStr(editorText,
boolFormat))
except ValueError:
return repr(genboolean.GenBoolean(editorText))
def comboChoices(self):
"""Return a list of choices for the combo box.
"""
if self.evalHtml:
return self.splitText(self.strippedFormat)
return self.splitText(self.format)
def initDefaultChoices(self):
"""Return a list of choices for setting the init default.
"""
return self.comboChoices()
def mathValue(self, node, zeroBlanks=True, noMarkup=True):
"""Return a value to be used in math field equations.
Return None if blank and not zeroBlanks,
raise a ValueError if it isn't a valid boolean.
Arguments:
node -- the tree item storing the data
zeroBlanks -- replace blank field values with zeros if True
"""
storedText = node.data.get(self.name, '')
if storedText:
return genboolean.GenBoolean(storedText).value
return False if zeroBlanks else None
def compareValue(self, node):
"""Return a value for comparison to other nodes and for sorting.
Returns lowercase text for text fields or numbers for non-text fields.
Bool fields return True or False values.
Arguments:
node -- the tree item storing the data
"""
storedText = node.data.get(self.name, '')
try:
return genboolean.GenBoolean(storedText).value
except ValueError:
return False
def adjustedCompareValue(self, value):
"""Return value adjusted like the compareValue for use in conditionals.
Bool version converts to a bool value.
Arguments:
value -- the comparison value to adjust
"""
try:
return genboolean.GenBoolean().setFromStr(value, self.format).value
except ValueError:
try:
return genboolean.GenBoolean(value).value
except ValueError:
return False
class ExternalLinkField(HtmlTextField):
"""Class to handle a field containing various types of external HTML links.
Protocol choices include http, https, file, mailto.
Stores data as HTML tags, shows in editors as "protocol:address [name]".
"""
typeName = 'ExternalLink'
evalHtmlDefault = False
editorClassName = 'ExtLinkEditor'
sortTypeStr ='60_link'
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the attributes that define this field's format
"""
super().__init__(name, formatData)
def addressAndName(self, storedText):
"""Return the link title and the name from the given stored link.
Raise ValueError if the stored text is not formatted as a link.
Arguments:
storedText -- the source text to format
"""
if not storedText:
return ('', '')
linkMatch = linkRegExp.search(storedText)
if not linkMatch:
raise ValueError
address, name = linkMatch.groups()
return (address, name)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
if noHtml:
linkMatch = linkRegExp.search(storedText)
if linkMatch:
address, name = linkMatch.groups()
storedText = name.strip()
if not storedText:
storedText = address.lstrip('#')
return super().formatOutput(storedText, oneLine, noHtml, formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
if not storedText:
return ''
address, name = self.addressAndName(storedText)
name = name.strip()
if not name:
name = urltools.shortName(address)
return '{0} [{1}]'.format(address, name)
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new text entered into the editor
"""
if not editorText:
return ''
nameMatch = linkSeparateNameRegExp.match(editorText)
if nameMatch:
address, name = nameMatch.groups()
else:
raise ValueError
return '{1}'.format(address.strip(), name.strip())
def adjustedCompareValue(self, value):
"""Return value adjusted like the compareValue for use in conditionals.
Link fields use link address.
Arguments:
value -- the comparison value to adjust
"""
if not value:
return ''
try:
address, name = self.addressAndName(value)
except ValueError:
return value.lower()
return address.lstrip('#').lower()
class InternalLinkField(ExternalLinkField):
"""Class to handle a field containing internal links to nodes.
Stores data as HTML local link tag, shows in editors as "id [name]".
"""
typeName = 'InternalLink'
editorClassName = 'IntLinkEditor'
supportsInitDefault = False
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the attributes that define this field's format
"""
super().__init__(name, formatData)
def editorText(self, node):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Also raises a ValueError if the link is not a valid destination, with
the editor text as the second argument to the exception.
Arguments:
node -- the tree item storing the data
"""
storedText = node.data.get(self.name, '')
return self.formatEditorText(storedText, node.treeStructureRef())
def formatEditorText(self, storedText, treeStructRef):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Also raises a ValueError if the link is not a valid destination, with
the editor text as the second argument to the exception.
Arguments:
storedText -- the source text to format
treeStructRef -- ref to the tree structure to get the linked title
"""
if not storedText:
return ''
address, name = self.addressAndName(storedText)
address = address.lstrip('#')
targetNode = treeStructRef.nodeDict.get(address, None)
linkTitle = targetNode.title() if targetNode else _errorStr
name = name.strip()
if not name and targetNode:
name = linkTitle
result = 'LinkTo: {0} [{1}]'.format(linkTitle, name)
if linkTitle == _errorStr:
raise ValueError('invalid address', result)
return result
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Uses the "address [name]" format as input, not the final editor form.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new editor text in "address [name]" format
"""
if not editorText:
return ''
nameMatch = linkSeparateNameRegExp.match(editorText)
if not nameMatch:
raise ValueError
address, name = nameMatch.groups()
if not address:
raise ValueError('invalid address', '')
if not name:
name = _errorStr
result = '{1}'.format(address.strip(), name.strip())
if name == _errorStr:
raise ValueError('invalid name', result)
return result
class PictureField(HtmlTextField):
"""Class to handle a field containing various types of external HTML links.
Protocol choices include http, https, file, mailto.
Stores data as HTML tags, shows in editors as "protocol:address [name]".
"""
typeName = 'Picture'
evalHtmlDefault = False
editorClassName = 'PictureLinkEditor'
sortTypeStr ='60_link'
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the attributes that define this field's format
"""
super().__init__(name, formatData)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
if noHtml:
linkMatch = _imageRegExp.search(storedText)
if linkMatch:
address = linkMatch.group(1)
storedText = address.strip()
return super().formatOutput(storedText, oneLine, noHtml, formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
if not storedText:
return ''
linkMatch = _imageRegExp.search(storedText)
if not linkMatch:
raise ValueError
return linkMatch.group(1)
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new text entered into the editor
"""
editorText = editorText.strip()
if not editorText:
return ''
nameMatch = linkSeparateNameRegExp.match(editorText)
if nameMatch:
address, name = nameMatch.groups()
else:
address = editorText
name = urltools.shortName(address)
return '
'.format(editorText)
def adjustedCompareValue(self, value):
"""Return value adjusted like the compareValue for use in conditionals.
Link fields use link address.
Arguments:
value -- the comparison value to adjust
"""
if not value:
return ''
linkMatch = _imageRegExp.search(value)
if not linkMatch:
return value.lower()
return linkMatch.group(1).lower()
class RegularExpressionField(HtmlTextField):
"""Class to handle a field format type controlled by a regular expression.
Stores options and format strings for a number field type.
Provides methods to return formatted data.
"""
typeName = 'RegularExpression'
defaultFormat = '.*'
evalHtmlDefault = False
fixEvalHtmlSetting = False
editorClassName = 'LineEditor'
formatHelpMenuList = [(_('Any Character\t.'), '.'),
(_('End of Text\t$'), '$'),
('', ''),
(_('0 Or More Repetitions\t*'), '*'),
(_('1 Or More Repetitions\t+'), '+'),
(_('0 Or 1 Repetitions\t?'), '?'),
('', ''),
(_('Set of Numbers\t[0-9]'), '[0-9]'),
(_('Lower Case Letters\t[a-z]'), '[a-z]'),
(_('Upper Case Letters\t[A-Z]'), '[A-Z]'),
(_('Not a Number\t[^0-9]'), '[^0-9]'),
('', ''),
(_('Or\t|'), '|'),
(_('Escape a Special Character\t\\'), '\\')]
def __init__(self, name, formatData=None):
"""Initialize a field format type.
Arguments:
name -- the field name string
formatData -- the dict that defines this field's format
"""
super().__init__(name, formatData)
def setFormat(self, format):
"""Set the format string and initialize as required.
Raise a ValueError if the format is illegal.
Arguments:
format -- the new format string
"""
try:
re.compile(format)
except re.error:
raise ValueError
super().setFormat(format)
def formatOutput(self, storedText, oneLine, noHtml, formatHtml):
"""Return formatted output text from stored text for this field.
Arguments:
storedText -- the source text to format
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
"""
match = re.fullmatch(self.format, saxutils.unescape(storedText))
if not storedText or match:
text = storedText
else:
text = _errorStr
return super().formatOutput(text, oneLine, noHtml, formatHtml)
def formatEditorText(self, storedText):
"""Return text formatted for use in the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
storedText -- the source text to format
"""
if not self.evalHtml:
storedText = saxutils.unescape(storedText)
match = re.fullmatch(self.format, storedText)
if not storedText or match:
return storedText
raise ValueError
def storedText(self, editorText):
"""Return new text to be stored based on text from the data editor.
Raises a ValueError if the data does not match the format.
Arguments:
editorText -- the new text entered into the editor
"""
match = re.fullmatch(self.format, editorText)
if not editorText or match:
if self.evalHtml:
return editorText
return saxutils.escape(editorText)
raise ValueError
class AncestorLevelField(TextField):
"""Placeholder format for ref. to ancestor fields at specific levels.
"""
typeName = 'AncestorLevel'
def __init__(self, name, ancestorLevel=1):
"""Initialize a field format placeholder type.
Arguments:
name -- the field name string
ancestorLevel -- the number of generations to go back
"""
super().__init__(name, {})
self.ancestorLevel = ancestorLevel
def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None):
"""Return formatted output text for this field in this node.
Finds the appropriate ancestor node to get the field text.
Arguments:
node -- the tree node to start from
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
spotRef -- optional, used for ancestor field refs
"""
if not spotRef:
spotRef = node.spotByNumber(0)
for num in range(self.ancestorLevel):
spotRef = spotRef.parentSpot
if not spotRef:
return ''
try:
field = spotRef.nodeRef.formatRef.fieldDict[self.name]
except (AttributeError, KeyError):
return ''
return field.outputText(spotRef.nodeRef, oneLine, noHtml, formatHtml,
spotRef)
def sepName(self):
"""Return the name enclosed with {* *} separators
"""
return '{{*{0}{1}*}}'.format(self.ancestorLevel * '*', self.name)
class AnyAncestorField(TextField):
"""Placeholder format for ref. to matching ancestor fields at any level.
"""
typeName = 'AnyAncestor'
def __init__(self, name):
"""Initialize a field format placeholder type.
Arguments:
name -- the field name string
"""
super().__init__(name, {})
def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None):
"""Return formatted output text for this field in this node.
Finds the appropriate ancestor node to get the field text.
Arguments:
node -- the tree node to start from
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
spotRef -- optional, used for ancestor field refs
"""
if not spotRef:
spotRef = node.spotByNumber(0)
while spotRef.parentSpot:
spotRef = spotRef.parentSpot
try:
field = spotRef.nodeRef.formatRef.fieldDict[self.name]
except (AttributeError, KeyError):
pass
else:
return field.outputText(spotRef.nodeRef, oneLine, noHtml,
formatHtml, spotRef)
return ''
def sepName(self):
"""Return the name enclosed with {* *} separators
"""
return '{{*?{0}*}}'.format(self.name)
class ChildListField(TextField):
"""Placeholder format for ref. to matching ancestor fields at any level.
"""
typeName = 'ChildList'
def __init__(self, name):
"""Initialize a field format placeholder type.
Arguments:
name -- the field name string
"""
super().__init__(name, {})
def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None):
"""Return formatted output text for this field in this node.
Returns a joined list of matching child field data.
Arguments:
node -- the tree node to start from
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
spotRef -- optional, used for ancestor field refs
"""
result = []
for child in node.childList:
try:
field = child.formatRef.fieldDict[self.name]
except KeyError:
pass
else:
result.append(field.outputText(child, oneLine, noHtml,
formatHtml, spotRef))
outputSep = node.formatRef.outputSeparator
return outputSep.join(result)
def sepName(self):
"""Return the name enclosed with {* *} separators
"""
return '{{*&{0}*}}'.format(self.name)
class DescendantCountField(TextField):
"""Placeholder format for count of descendants at a given level.
"""
typeName = 'DescendantCount'
def __init__(self, name, descendantLevel=1):
"""Initialize a field format placeholder type.
Arguments:
name -- the field name string
descendantLevel -- the level to descend to
"""
super().__init__(name, {})
self.descendantLevel = descendantLevel
def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None):
"""Return formatted output text for this field in this node.
Returns a count of descendants at the approriate level.
Arguments:
node -- the tree node to start from
oneLine -- if True, returns only first line of output (for titles)
noHtml -- if True, removes all HTML markup (for titles, etc.)
formatHtml -- if False, escapes HTML from prefix & suffix
spotRef -- optional, used for ancestor field refs
"""
newNodes = [node]
for i in range(self.descendantLevel):
prevNodes = newNodes
newNodes = []
for child in prevNodes:
newNodes.extend(child.childList)
return repr(len(newNodes))
def sepName(self):
"""Return the name enclosed with {* *} separators
"""
return '{{*#{0}*}}'.format(self.name)
#### Utility Functions ####
def removeMarkup(text):
"""Return text with all HTML Markup removed and entities unescaped.
Any
tags are replaced with newlines.
"""
text = _lineBreakRegEx.sub('\n', text)
text = _stripTagRe.sub('', text)
return saxutils.unescape(text)
def adjOutDateFormat(dateFormat):
"""Replace Linux lead zero removal with Windows version in date formats.
Arguments:
dateFormat -- the format to modify
"""
if sys.platform.startswith('win'):
dateFormat = dateFormat.replace('%-', '%#')
return dateFormat
def adjInDateFormat(dateFormat):
"""Remove lead zero formatting in date formats for reading dates.
Arguments:
dateFormat -- the format to modify
"""
return dateFormat.replace('%-', '%')
def adjTimeAmPm(timeFormat, time):
"""Add AM/PM to timeFormat if in format and locale skips it.
Arguments:
timeFormat -- the format to modify
time -- the datetime object to check for AM/PM
"""
if '%p' in timeFormat and time.strftime('%I (%p)').endswith('()'):
amPm = 'AM' if time.hour < 12 else 'PM'
timeFormat = re.sub(r'(?