From 33b3c4a0cbd7dbe338bb29b55584fd56c08cb791 Mon Sep 17 00:00:00 2001 From: nightfox <> Date: Thu, 19 Sep 2013 03:42:25 +0000 Subject: [PATCH] SlyEdit version 1.32: Added the ability to choose a tagline to append to the message upon saving. Also added user settings, which lets users configure whether or not they want to use taglines, whether or not to prepend the original author's intials to quoted lines, and whether or not to indent quoted lines that begin with author's initials. Also made some misc. changes & bug fixes, such as: - The maximum message name length in Ice mode is now 35 characters (up from 20 characters). - The user's time left is now updated on the screen as it changes (checked & updated after each keypress). - Bug fix: The time is now displayed in the correct location when the terminal is wider than 80 characters. - Bug fix for DCT mode: Listing text replacements using Ctrl-T from the Help menu now works. - Bug fix for DCT mode: Fixed an off-by-one bug for the horizontal position of the INS/OVR text when changing between insert & overwrite mode using a terminal size more than 80 characters wide. - Some color setting name changes & addition in the color theme configuration files. --- ctrl/SlyDCTColors_Default.cfg | 5 +- ctrl/SlyDCTColors_Midnight.cfg | 5 +- ctrl/SlyEdit.cfg | 21 +- ctrl/SlyEdit_Taglines.txt | 926 ++++++++++++++++++ ctrl/SlyIceColors_BlueIce.cfg | 5 +- ctrl/SlyIceColors_EmeraldCity.cfg | 5 +- ctrl/SlyIceColors_FieryInferno.cfg | 5 +- ctrl/SlyIceColors_Fire-N-Ice.cfg | 5 +- ctrl/SlyIceColors_GenericBlue.cfg | 5 +- ctrl/SlyIceColors_ShadesOfGrey.cfg | 5 +- docs/SlyEdit_DD_Message_Lister_notes.txt | 3 +- docs/SlyEdit_ReadMe.txt | 97 +- docs/SlyEdit_Upgrading.txt | 105 +- exec/SlyEdit.js | 823 +++++++++------- exec/SlyEdit_DCTStuff.js | 93 +- exec/SlyEdit_IceStuff.js | 53 +- exec/SlyEdit_Misc.js | 1142 +++++++++++++++++++++- 17 files changed, 2828 insertions(+), 475 deletions(-) create mode 100644 ctrl/SlyEdit_Taglines.txt diff --git a/ctrl/SlyDCTColors_Default.cfg b/ctrl/SlyDCTColors_Default.cfg index 023e86d121..aee9ba67fe 100644 --- a/ctrl/SlyDCTColors_Default.cfg +++ b/ctrl/SlyDCTColors_Default.cfg @@ -73,8 +73,9 @@ crossPostChkHighlight=n4hy crossPostMsgGrpMark=nhg crossPostMsgGrpMarkHighlight=n4hg -; Colors for the text replacement list -txtReplacementList=nc +; For the text items in list boxes +listBoxItemText=nc +listBoxItemHighlight=n4wh ; Colors for message saving and sub-board post info when exiting SlyEdit msgWillBePostedHdr=nc diff --git a/ctrl/SlyDCTColors_Midnight.cfg b/ctrl/SlyDCTColors_Midnight.cfg index 8de32e937b..0937216ab8 100644 --- a/ctrl/SlyDCTColors_Midnight.cfg +++ b/ctrl/SlyDCTColors_Midnight.cfg @@ -75,8 +75,9 @@ crossPostChkHighlight=n4hy crossPostMsgGrpMark=nhg crossPostMsgGrpMarkHighlight=n4hg -; Colors for the text replacement list -txtReplacementList=nbh +; For the text items in list boxes +listBoxItemText=nbh +listBoxItemHighlight=n4wh ; Colors for message saving and sub-board post info when exiting SlyEdit msgWillBePostedHdr=nc diff --git a/ctrl/SlyEdit.cfg b/ctrl/SlyEdit.cfg index c49274ece5..6e43aa6a52 100644 --- a/ctrl/SlyEdit.cfg +++ b/ctrl/SlyEdit.cfg @@ -6,11 +6,6 @@ inputTimeoutMS=300000 ; so that they are complete but still look good when quoted. If this option is ; disabled, then quote lines will simply be trimmed to fit into the message. reWrapQuoteLines=true -; Whether or not to prefix the quote lines with the last author's initials -useQuoteLineInitials=true -; When prefixing quote lines with the last author's initials, whether or not -; to indent the quote lines with a space. -indentQuoteLinesWithInitials=true ; Whether or not to allow cross-posting allowCrossPosting=true ; Whether or not to enable text replacements (AKA macros). @@ -28,6 +23,22 @@ enableTextReplacements=true ; JavaScript commands to run upon exit of SlyEdit ;addJSOnExit +; The name of the file where tag lines are stored +tagLineFilename=SlyEdit_Taglines.txt +; Whether or not to allow users to change their user settings. +allowUserSettings=true + +; The following settings serve as defaults for the user settings, which +; each user can change for themselves: + +; Whether or not to prefix the quote lines with the last author's initials +useQuoteLineInitials=true +; When prefixing quote lines with the last author's initials, whether or not +; to indent the quote lines with a space. +indentQuoteLinesWithInitials=true +; Whether or not to enable the option to add a tagline +enableTaglines=false + ; To use a different color theme file, change the ThemeFilename ; setting. If you want, you can comment out the current setting ; (by placing a ; in front of the line) and un-commenting one diff --git a/ctrl/SlyEdit_Taglines.txt b/ctrl/SlyEdit_Taglines.txt new file mode 100644 index 0000000000..42301cf14e --- /dev/null +++ b/ctrl/SlyEdit_Taglines.txt @@ -0,0 +1,926 @@ +Power corrupts. Absolute power is kinda neat, though. +YouTube, Twitter, & FaceBook will combine to form YouTwitFace. +I broke a mirror & got 7 years of bad luck; my lawyer thinks he can get me 5. +All those who believe in psychokinesis, raise my hand. +How do you tell when you're out of invisible ink? +What's the speed of dark? +I almost had a psychic girlfriend but she left me before we met. +A lot of people are afraid of heights. Not me, I'm afraid of widths. +Curiosity killed the cat, but for a while I was a suspect. +Everywhere is within walking distance if you have the time. +I got some powdered water, but I don't know what to add. +I have an existential map. It has 'You are here' written all over it. +I intend to live forever. So far, so good. +I stayed in a really old hotel last night. They sent me a wake-up letter. +When I die, I'm leaving my body to science fiction. +When I was a little kid, we had a quicksand box. I was an only child eventually. +I used to work in a fire hydrant factory. You couldn't park anywhere near it. +I photocopied a mirror. Now I have an extra photocopy machine. +I'm not afraid of heights. I'm afraid of widths. +I got a new shadow. My last shadow wasn't doing what I was doing. +Cross-country skiing is great if you live in a small country. +What happens if you're scared half to death twice? +Prepositions are not good words to end sentences with. +Ending sentences with prepositions is something up with which I will not put. +I gave my wife a hint: I bought a love seat. +I'd rather have a bottle in front of me than a frontal lobotomy. +What do you say we make some apple juice and fax it to each other? +My psychiatrist says I have an obsession with vengeance. We'll see about that... +I want to write a mystery novel...or do I? +Frank Beard is the only member of ZZ Top without a beard. + +The average raindrop falls at 7 miles per hour. +An unbiased opinion is always absolutely valueless. +Don't drink and park; accidents cause people. +Either one of us, by himself, is expendable. Both of us are not. +Everybody is somebody else's weirdo. +First secure an independent income, then practice virtue. +There's never a good time to score an own goal. +Those who think they know it all, often upset those of us who do. +You don't get once-in-a-lifetime offers like this every day. +Bend the facts to fit the conclusion. It's easier that way. +Computers run on faith, not electrons. +Everyone has his day, and some days last longer than others. +For peace of mind, resign as general manager of the universe. +Go on, be yourself! There isn't anyone better qualified. +Have a nice day ...somewhere else. +I'm famous. That's my job. +If it ain't broke, don't fix it. +In the long run, we are all dead. +It works better if you plug it in. +42? -- 7 and a half million years and all you can come up with is 42?! +A University without students is like an ointment without a fly. +A Vulcan can no sooner be disloyal than he can exist without breathing. +A bachelor never makes the same mistake once. +A bird in the hand is safer than one overhead. +A celebrity is a person who is known for his well-knownness. +A child of 5 could understand this! Fetch me a child of 5. +A city is a large community where people are lonesome together +A clean desk is a sign of a cluttered desk drawer. +A closed mouth gathers no foot. +A communist is a socialist without a sense of humour. +A communist is one who has nothing and wishes to share it with the world. +A conclusion is simply the place where someone got tired of thinking. +A continuing flow of paper is sufficient to continue the flow of paper. +A countryman between two lawyers is like a fish between two cats. +A crises is when you CAN'T say let's forget about the whole thing! +A critic is a legless man who teaches running. +A critic is a man who knows the way, but can't drive the car. +A cynic is a person searching for an honest man, with a stolen lantern. +A damn good funeral is still one of our best and cheapest acts of theatre +A day for firm decisions!!!!! Or is it? +A day without sunshine is like night. +A diplomat is a man who thinks twice before saying nothing. +A dog is the only thing on earth that loves you more than you love yourself. +A door is what a dog is perpetually on the wrong side of. +A face like a wedding cake left out in the rain.. +A fail-safe circuit will destroy others. +A failure will not appear till a unit has passed final inspection. +A fanatic is one who can't change his mind and won't change the subject. +A fast has no real nutritional value. +A feature is a bug with seniority. +A fertile imagination is no compensation for vasectomy. +A fool must now and then be right by chance. +A gentleman is one who never swears at his wife while ladies are present. +A good man dies when a boy goes wrong. +A great deal of money is never enough once you have it. +A group of the unfit appointed by the unwilling to do the necessary. +A hunch is creativity trying to tell you something. +A husband is what is left of a man after the nerve is extracted. +A husband is what is left of the lover after the nerve has been extracted. +A jury -- twelve persons chosen to decide who has the better lawyer. +A liberal is a conservative who's been mugged by reality. +A liberal is a man too broadminded to take his own side in a quarrel. +A liberal is a man who leaves the room when a fight begins. +A lie can be half way round the world before the truth has got its boots on. +A little inaccuracy sometimes saves tons of explanation. � +A man does not look behind the door unless he has stood there himself. +A man's only as old as the woman he feels. +A memorandum is written not to inform the reader but to protect the writer +A mixture of admiration and pity is one of the surest recipes for affectio +A modest man is usually admired - if people ever hear of him. +A motor will rotate in the wrong direction. +A oscillator will oscillate at the wrong frequency ...if it oscillates. +A pnp transistor will be an npn. +A power so great, it can only be used for Good or Evil! +A professor is one who talks in someone else's sleep. +A really busy person never knows how much he ways. +A rolling stone gathers momentum. +A seeming ignorance is often a most necessary part of worldly knowledge. +A self-starting oscillator won't. +A sense of decency is often a decent man's undoing. +A sharp tongue and a dull mind are usually found in the same head! +A short cut is the longest distance between two points. +A sine curve goes off to infinity or at least the end of the blackboard. +A single death is a tragedy, a million deaths a statistic. +A specified environmental conditions will always be exceeded. +A stockbroker is someone who invests your money until it is all gone. +A straw vote only shows which way the hot air blows. +A student who changes the course of history is probably taking an exam. +A triangle which has an angle of 135 degrees is called an obscene triangle +A truly wise man never plays leapfrog with a unicorn. +A woman drove me to drink, and I never had the courtesy to thank her. +A woman should have compassion. Kirk, Catspaw, stardate 3018.2. +AI programmers only think they do it +ALIMONY: The cost of leaving. +ARRRRRGGGHHH!!!!...Tension breaker, had to be done. +Academic rivalries are so intense because the stakes are so small. +Activity is the politician's substitute for achievement. +Advertising is legalized lying. +Advertising is the rattling of a stick inside a swill bucket. +After a degree of prettiness, one pretty girl is as pretty as another. +After all is said and done, a hell of a lot more is said than done. +After two days in hospital, I took a turn for the nurse. +Ah! Mozart. He was happily married - but his wife wasn't. +Ahh! Come on Erick, just this one last little feature! +Ahh! Come on Gerard, just this one last little feature! +Ahhhhhhhh, I forget what I was going to say. +Alas! The poor Tagline. I knew it well. +Alimony is like buying oats for a dead horse. +Alimony: Bounty after the mutiny +Alimony: The ransom that the happy pay to the devil. +All Americans lecture... I suppose it is something in their climate. +All great discoveries are made by mistake. +All right, so I like spending money! But name one other extravagance. +All things are possible. Except skiing through a revolving door. +All things being equal, a fat person uses more soap than a thin person. +All turtle thoughts are of turtle. +All warranties expire upon payment of invoice. +All work and no play make Jack a dull boy and Jill a wealthy widow. +All's well that ends. +Almost everything in life is easier to get into than out of. +Always address your elders with respect; they could leave you a fortune. +Always forgive your enemies - nothing else annoys them as much. +Always mistrust a subordinate who never finds fault with his boss. +Ambition is the curse of the political class. +Ambition is the last refuge of the failure. +America never lost a war or won a conference. +Americans like fat books and thin women +Among economists, the real world is considered to be a special case. +An alcoholic is someone you don't like who drinks as much as you do. +An appeaser is one who feeds a crocodile hoping it will eat him last. +An argument is where two people are trying to get the LAST word in FIRST! +An easily understood, workable falsehood is more useful than the truth +An honest politician is one who, when bought, stays bought. +An idea is not responsible for the people who believe in it. +An idea that is dangerous is unworthy of being called an idea at all. +An independent is a guy who wants to take the politics out of politics. +An instantaneous power-supply crowbar circuit will operate too late. +An object never serves the same function as its image- or its real name. +An optimist is a man who starts a crossword puzzle with a fountain pen. +An optimist is someone who thinks the future is uncertain. +An oyster is a fish built like a nut. +Another dream that failed. There's nothing sadder. Kirk, stardate 3417.3. +Answers: $1, Short: $5, Correct: $25, dumb looks are still free. +Any club that would accept me as a member, I wouldn't want to join. +Any safety factor set as a result of practical experience will be exceeded +Any stigma is good enough to beat a dogma with. +Anyone can get old. All you have to do is live long enough. +Anyone who can walk to the welfare office can walk to work. +Anyone who goes to a psychiatrist needs his head examined! +Anyone who lives within his means suffers from a lack of imagination. +Anything anybody can say about America is true. +Anything can be made to work if you fiddle with it long enough! +Anything that keeps a politician humble is healthy for democracy. +As a boy, he swallowed a teaspoon. And he hasn't stirred since. +Assassination is the extreme form of censorship. +Autobiography is now as common as adultery - and hardly less reprehensible +Average is as close to the bottom as it is to the top. +BACHELOR: A man who never makes the same mistake once. +BEWARE - Tagline Thief is in the area... +BROOK'S LAW: Adding manpower to a late software project makes it later. +Back up my hard disk? I can't find the reverse switch! +Backup not found: (A)bort (R)etry (F)#@K it! +Be an individualist. He who follows another is always behind. +Be wary of strong drink. It can make you shoot at tax collectors and miss +Beat inflation - steal! +Beauty is only skin deep, but ugly goes right to the bone. +Beauty is transitory. Beauty survives. Spock and Kirk, stardate unknown. +Because of the greatness of the Shah, Iran is an island of stability +Bedfellows make strange politicians. +Behind every great man, there is a woman -- urging him on. +Behind every successful man is an astonished mother-in-law. +Behind every successful man stands an amazed woman. +Believe those who are seeking the truth; doubt those who find it. +Between two evils, I always pick the one I never tried before. +Beware of all enterprises requiring new clothes. +Birth is the beginning of death. +Black holes are outa sight! +Blessed are the censors, for they shall inhibit the earth. +Blessed are the meek for they shall inherit the crap. +Blessed are the young, for they shall inherit the national debt. +Blessed be he who is called a big wheel, for he goeth around in circles. +Bosses come and bosses go, but a good secretary lasts forever. +But soft, what light through yonder tagline breaks? +But, He has not one redeeming vice. +Buy Land Now. It's Not Being Made Any More. +By annihilating desires you annihilate the mind. +By the time most of us have money to burn, our fire's gone out. +California is a fine place to live -- if you happen to be an orange. +Celibacy is not hereditary. +Chicken Little was right. +Children are a comfort in old age, and they will even help you reach it. +Civil engineers do it behind schedule +Civil servants are neither civil nor servile. +Civilization is a movement, not a condition; it is a voyage, not a harbou +Classical music is the kind we keep thinking will turn into a tune. +Click...click...click...damn, out of taglines! +Committee work is like a soft chair...easy to get into but hard to get out +Committees: A group that takes minutes and wastes hours. +Common sense is instinct. Enough of it is genius. +Communism is like prohibition, it's a good idea but it won't work. +Communism is the opiate of the intellectuals. +Components that must not and cannot be assembled improperly will be. +Confidence is the feeling you had before you knew better. +Confound these ancestors They've stolen our best ideas! +Conistency is the last refuge of the unimaginative. +Conscience gets alot of credit that belongs to cold feet. +Conscience is what hurts when everything else feels so good. +Constipation is the thief of time. Diarrhoea waits for no man. +Construct a system that even a fool can use and only a fool will want to. +Could you continue your petty bickering? I find it most intriguing. +Count Dracula - your Bloody Mary is ready... +Counting time is not so important as making time count. +Crisis management works beautifully until an actual crisis occurs. +Culture is what your butcher would have if he were a surgeon. +DOS never says EXCELLENT command or filename... +Da trouble wit computers is, dey got no sense of humor. +Dachshunds are really small crocodiles with fur. +Database administrators do it with their relations +Dead people are cool +Death is hereditary +Death is mother nature's warning to slow down. +Death, when unnecessary, is a tragic thing. +Degeneration and evolution are not the same thing. +Democracy is the art of running the circus from the monkey cage. +Democracy is too goo to share with just anybody. +Deny thy father and forget thy tagline. +Desperate diseases require desperate remedies. +Die, my dear doctor? That's the last thing I shall do. +Diets are for those who are thick and tired of it. +Diogenes is still searching. +Diplomacy is the art of letting someone else have your way. +Diplomacy is the art of saying Nice doggie! till you can find a rock. +Diplomacy: The patriotic art of lying for one's country. +Distrust all in whom the impulse to punish is powerful. +Distrust your first impressions; they are invariably too favorable. +Do something unusual today. Pay a bill. +Do what you will with this tagline, just don't bother me about it! +Docs? Why would I want to look at the Docs. Nurses are better :) +Documentation - The worst part of programming. +Don't force it, get a larger hammer. +Don't go to work, there's a lot to do. +Don't hit me, Mr. Moderator! I'll go back on topic, I swear! +Don't jump on a man unless he's down. +Don't kiss an elephant on the lips today. +Don`t force it, get a larger hammer. +Double your pleasure, Double your fun. Xerox your pay-cheques. +Drop your carrier ...we have you surrounded! +Earn cash in your spare time -- blackmail your friends +Easter is cancelled this year. They've found the body. +Ebius tagline. This is a moebius tagline. This is a mo ... +Education can cause a woman's uterus to shrivel. +Elevators smell different to midgets +England has civilization but no culture. +Every instructor assumes you have nothing to do but study for his course. +Every living thing wants to survive. Spock, stardate 4731.3. +Every revolutionary ends up either by becoming an oppressor or a heretic. +Every woman is a rebel, and usually in wild revolt against herself. +Everybody has a right to pronounce foreign names as he chooses. +Everybody lies; but it doesn't matter much since nobody listens. +Everybody should believe in something: I believe I'll have another drink. +Everything beautiful has its moment and then passes away. +Experience is what you get when you don't get what you wanted. +Extinction is the ultimate fate of all species. +Extreme boredom serves to cure boredom. +Extreme sorrow laughs; extreme joy weeps. +Extremely happy and extremely unhappy men are alike prone to grow hard-hea +Eye witnesses were on the scene in minutes. +Facts cannot prevail against faith, or adamant folly. +Failure has gone to his head. +Failure is a measurement that depends on the standard applied. +Fascinating, a totally parochial attitude. Spock, stardate 3219.8. +Fashion: There'll be little change in men's pockets this year. +Fear is no great respecter of reason. +Federal Employment Principle: Confusion creates jobs. +Feed the wolf as you will; he will always look to the forest. +Fifty-eight per cent of all cars coming into Britain are imported. +File not found, I'll load something *I* think is interesting. +File not found. Should I fake it? (Y/N) +Finagle's Sixth Law: Do NOT believe in miracles -- rely on them! +Finagle's first Law: If an experiment works, something has gone wrong. +First Law of Socio-Genetics: Celibacy is not hereditary. +Fools belittle that which they do not understand. Cynics belittle everythi +Footprints in the sands of time are never made by sitting down. +For a man of fortitude, there are no walls, only avenues. +For certain people, after fifty, litigation takes the place of sex. +For every action, there is an equal and opposite criticism. +For many people, homeless simply means not having a home. +Free are those who dream dreams. +Free the indianapolis 500. +Freedom is for everyone. Or no one. +Freedom of the press is limited to those who have one. +Friends may come and friends may go, but enemies accumulate. +Friends, Romans, countrymen, lend me your taglines! +Fun, fun, fun, til her daddy takes her GoldED away! +Gambling: The sure way of getting nothing for something. +Genius is one per cent inspiration and ninety-nine per cent perspiration. +Get too many irons in your fire and you'll put it out. +Give me an example of pro and con. Progress and Congress. +God can't alter history, so he created historians. +God has Alzheimer's disease; he's forgotten that we exist. +God is a comedian playing to an audience too afraid to laugh. +God is alive- he just doesn't want to get involved. +God is dead. But don't worry - the Virgin Mary is pregnant again. +God is not dead. He is alive and autographing bibles today at Waterstones +God made everything out of nothing. But the nothingness shows through. +Golf is a walk, spoiled. +Good news. Ten weeks from Friday will be a pretty good day. +Gossip is when you hear something you like about someone you don't. +Government corruption seems always to be reported in the past tense. +Graphics recorders will deposit more ink on humans than on paper. +Gravity doesn`t exist: the earth sucks. +HANGOVER: the wrath of grapes. +HE has not a single redeeming defect. +Half Moon tonight. (At least its better than no Moon at all.) +Half of conversation is listening. +He had but one eye and the popular prejudice runs in favour of two. +He is all fault who has no fault at all. +He knew everything about literature, except how to enjoy it. +He used to be fairly indecisive, but now he's not so certain. +He was a man, all and all, I shall not look upon his like again. +He who dies with the most TAGLINES wins! +He who dies with the most toys, wins! +He who laughs last probably didn't get the joke. +He who laughs, lasts. +He who ploughs a straight furrow, is probably in a rut. +He's dead Jim. You take his phaser, I'll take his wallet! +Heads will have to roll! +Health is merely the slowest possible rate at which one can die. +Heavy, adj.: Seduced by the chocolate side of the force. +Heisenberg may have slept here +Hell hath no fury like a bureaucrat scorned. +Hello, he lied. +Help a swallow land at Capistrano. +Help fight continental drift. +Help me, I'm a prisoner in a Fortune cookie file! +Help stamp out and abolish redundancy. +Heresy is only another word for freedom of thought. +Hey! Who took the cork off my lunch??! +Hindsight is an exact science. +Hire the morally handicapped. +His ears made him look like a taxicab with both doors open. +His eyes are so bad, he has to wear contact lenses to see his glasses. +History tends to exaggerate. +Homosexuality must be hereditory -- most gays have heterosexual parents. +Honeymoon - the morning after the knot before. +Hope is a good breakfast, but a bad supper. +How come there's only one Monopolies Commission? +Humour is emotional chaos remembered in tranquillity. +Husbands are like fires. They go out if unattended. +Hypochondria is the only disease I haven't got. +Hypochondriac: someone who enjoys bad health. +I am not an Economist. I am an honest man! +I belong to no organized party - I am a democrat. +I call things as I see them; If I didn't see them, I make them up! +I can remember when a liberal was one who was generous with his own money. +I can't promise anything but I can promise 100%. +I do not often attack the labour party. They do it so well themselves. +I don't deserve this, but I have arthritis and I don't deserve that either +I don't have any solution, but I certainly admire the problem. +I don't make jokes. I just watch the government and report the facts. +I don't think it's any less important for not being terribly important. +I either want less corruption, or more chance to participate in it. +I either want less corruption, or more chance to participate in it. +I had a monumental idea this morning, but I didn't like it. +I have a new philosophy. I'm only going to dread one day at a time. +I haven't lost my mind; it's backed up on tape somewhere! +I knew I was an unwanted baby. One of my bath toys were a toaster. +I know a good tagline when I steal one. +I like a man who grins when he fights. +I like long walks, especially when they are taken by people who annoy me. +I look better on a woman! +I love criticism just so long as it's unqualified praise. +I may not be totally perfect, but parts of me are excellent. +I must apologise to the deaf for the loss of subtitles. "What?" +I must follow them. I am their leader. +I never knew a girl who was ruined by a good book. +I never used to be able to finish anything, but now I +I often quote myself; it adds spice to my conversation. +I only know two tunes. One is 'Rule Brittania' -- and the other isn't. +I only touch base with reality on an as-needed basis! +I predict that today will be remembered until tomorrow! +I profoundly believe it takes a lot of practice to become a moral slob. +I reserve my abuse for lower life forms, like Civil Servants. +I say we nuke the site from orbit, it's the only way to be sure +I shot an arrow into the air, and it stuck. +I think sex is better than logic, but I can't prove it. +I used to be an agnostic, but now I'm not so sure. +I used to be indecisive; now I'm not sure. +I used to be schizophrenic, but we're alright now. +I used to get high on life but lately I've built up a resistance. +I used to think I was indecisive, but now I'm not so sure. +I want to be what I was when I started to be what I am now. +I was brought up in a clergyman's house so I am a first-class liar. +I wish the Government would put a tax on pianos for the incompetent. +I wonder if we ccan speak through rose-tinted spectacles. +I'd give my right arm to be ambidextrous. +I'll tell you one fact - it may be rather boring but it's interesting. +I'm a soldier, not a diplomat. I can only tell the truth. +I'm afraid to die. I just don't want to be there when it happens. +I'm at that age now where just putting my cigar in its holder is a thrill. +I'm frequently appalled by the low regard you Earthmen have for life. +I'm going to make a prediction - it could go either way. +I'm not broke, I'm just badly bent. +I'm not the one that misplaced the Deltivid asteroid belt! +I've already got a female to worry about. Her name is the Enterprise. +I've always been a bit maturer that what I am. +I've found a great way to start the day - I go straight back to bed! +I've got Parkinson's disease. And he's got mine. +I've got a memory for faces, but in this case I'll make an exception. +I've got ten pairs of training shoes, one for every day of the week. +I've had enough of gardening - I'm just about ready to throw in the trowel +If I had been present at creation, I would have given some useful hints. +If I had been the Virgin Mary, I would have said No. +If a circuit cannot fail, it will. +If all economists were laid end to end, they would not reach a conclusion. +If at first you don't succeed, destroy all evidence that you tried. +If everything seems easy, you have obviously overlooked something. +If it was a bet, you wouldn't take it. +If it wasn't for C, we would be using BASI, PASAL and OBOL! +If little else, the brain is an educational toy. +If man were immortal, do you realise what his meat bills would be? +If only I could be respected without having to be respectable. +If people don't want to come to the ball park, nobody's going to stop them +If they liked it, they didn't applaud - they just let you live. +If this is dying, I don't think much of it. +If you become a success, you don't change - everyone else does. +If you can count your money you don't have a billion dollars. +If you can't learn to do it well, learn to enjoy doing it badly. +If you can't see the bright side, polish the dull side. +If you don't go to people's funerals, they won't come to yours. +If you feel strongly about graffiti, sign a partition. +If you think education is expensive, try ignorance. +If you're not confused, you're not paying attention. +If you've got them by the balls, their hearts and minds will follow. +If you've seen one REDWOOD tree, you've seen 'em all. +If you've seen one city slum, you've seen them all. +Ignorance is no excuse-it's the real thing. +Ignorance is the mother of research. +Illiterate? Write Today for Free Help. +Illiterate? Write for FREE HELP! +Illiteratets of the wlord. Untie! +Immortality consists largely of boredom. Zefrem Cochrane, stardate 3219.8. +Immortality--a fate worse than death. +In England there are sixty different religions and only one sauce. +In comparison, there's no comparison. +In every revolution, there's one man with a vision. Kirk, stardate unknown +In matters of conscience, the law of majority has no place. +In some cases non-violence requires more militancy than violence. +In the future, everyone will be famous for fifteen minutes. +Information deteriorates upward through bureaucracies. +Insufficient facts always invite danger. Spock, stardate 3141.9. +Insults are effective only where emotion is present. +Interchangable devices won`t. +Interchangeable parts won't. +Internal consistency is more highly valued than efficiency. +Intuition, however illogical, is recognized as a command prerogative. +Is man one of God's blunders or is god one of Man's blunders? +Is the US ready for self-government? +Is truth not truth for all? Natira, stardate 5476.4. +It is a good thing for an uneducated man to read books of quotations. +It is a rather pleasant experience to be alone in a bank at night. +It is a well known fact that a deceased body harms the mind. +It is better to know some of the questions than all of the answers. +It is easier to write an incorrect program than understand a correct one. +It is illegal to make liquor privately, or water publicly. +It is impossible to please the whole world and your mother-in-law. +It is morally wrong to allow suckers to keep their money. +It is more rational to sacrifice one life than six. Spock, stardate 2822.3 +It is necessary to have purpose. Alice #1, I, Mudd, stardate 4513.3. +It is not enough to succeed. Others must fail. +It is only the shallow people who do not judge by appearances. +It is undignified for a woman to play servant to a man who is not hers. +It requires a very unusual mind to make an analysis of the obvious. +It takes a long time to understand nothing. +It takes a lot of experience for a girl to kiss like a beginner. +It usually takes me more than three weeks to prepare a good impromptu spe +It was completely quiet in the stadium - but noisy. +It was such a lovely day, I thought it was a pity to get up. +It works better if you plug it in. +It would be illogical to assume that all conditions remain stable. +It would be illogical to kill without reason. Spock, stardate 3842.4. +It's a can of wormms full of Pandora's boxes. +It's always the OVERtakers who keep the UNDERtakers busy. +It's easy to be brave from a safe distance. +It's important that I NOT know. +It's innocence when it charms us, ignorance when it doesn't. +It's so true to life it's hardly true. +Its not the size of the ship, its the size of the waves. +Jargon is used as a means of succeeding by, not simplifying. +Just a little thoughtfulness brings alot of happiness. +Just because everything is different doesn't mean anything has changed. +Ketterling's Law: Logic is an organized way of going wrong with confidenc +Kettle, plug, fridge, milk, coffee. Yawn. +Landru! Guide us! +Laugh and the world laughs with you, snore and you sleep alone. +Laughter is the shortest distance between two people. +Let him who takes the plunge remember to return it by Tuesday. +Let me then switch tacks and change horses in midstream. +Liberals are a Labour-saving device. +Liberty means responsibility. That is why most men dread it. +Life and death are seldom logical. But attaining a desired goal always is. +Life is a hereditary disease. +Life is a sexually transmitted disease +Life shouldn't be printed on dollar bills. +Line noise provided by British Telecom and Mercury! +Living with a saint is more gruelling than being one. +Logic and practical information do not seem to apply here. +Love conquers all things except poverty and toothache +Love is a long term investment, not a quick return loan! +Love is being willing to share your toothbrush with someone else. +Love is like the measles - all the worse when it comes late in life. +Love is like war: easy to begin, but very hard to stop. +Love is what you've been through with somebody. +Lubarsky's Law of Cybernetic Entomology: There is always one more bug. +MONEY TALKS...but all mine ever says is GOODBYE! +Make no little plans. They have no Magic to stir Men's blood. +Many Myths are based on truth. Spock, stardate 5832.3. +Many people think Joan of Arc was immortal, but she did in fact exist. +Marie-Joseph? It's a lovely name! It just sounds silly, that's all. +Marriage is a great institution - no family should be without it. +Marriage is a great institution, but I'm not ready for an institution yet. +Marriage is not a word but a sentence. +Marriage: the price men pay for sex. Sex: the price women pay for marriag +Martyrdom is the only way a person can become famous without ability. +Martyrdom is the only way in which a man can become famous without ability +Mary had a little RAM -- only about a MEG or so. +Massachusetts has the best politicians money can buy. +Mathematicians have to PROVE they can do it +May you live all the days of your life. +Me no wanna goto work. Me wanna bang on keyboard! +Men and women are two different species, descended from different animals. +Men seldom make passes at girls who wear glasses. +Men will always be men -- no matter where they are. +Men will sooner surrender their rights than their customs. +Menu: A list of dishes which the restaurant has just run out of. +Message from God: Universe rebooting in 5 sec. Please log out. +Midgets simply belittle. +Milhouse, we live in the age of cooties! - Bart Simpson +Misery loves company, but company does not reciprocate. +Miss Stove seems to be going off the boil. +Mistrust first impulses, they are always good. +Moderation is a fatal thing - nothing succeeds like excess. +Modesty died when clothes were born. +Modesty is a vastly overrated virtue. +Monday is a hard way to spend one-seventh of your life. +Money can't buy friends but you can get a better class of enemy. +Money is a sixth sense without which you cannot make use of the other five +Money is better than poverty, if only for financial reasons. +Money isn't everything, usually it isn't even enough. +Mongo LIKE Candygram. +Monogamy leaves a lot to be desired. +Moral indignation is jealousy with a halo. +Morality consists in suspecting other people of not being legally married. +Morality is the attitude we adopt to people whom we personally dislike. +Most legends have their basis in facts. Kirk, stardate 5029.5. +Most self-made men worship their creators. +Most women loathe limericks, for the same reason that calves hate cookbook +Mother Nature is a Bitch. +Music is essentially useless, as life is. +My hard disk is full! Maybe I'll try this message section thing. +My inferiority complexes aren't as good as yours. +My other computer is a 486. +My other vehicle is a Galaxy Class Starship ... +Nationalise crime, and make sure it doesn't pay. +Never attribute to malice that which is adequately explained by stupidity. +Never drink black coffee at lunch. It will keep you awake in the afternoo +Never invest your money in anything that eats or needs painting. +Never let your feet run faster than your shoes. +Never marry a man who hates his mother because he'll end up hating you. +Never return a kindness---pass it on! +Never try to out-stubborn a cat. +Never underestimate the power of human stupidity. +New Mail not found. Start whine-pout sequence? (Y/N) +No good deed goes unpunished. +No hand signals. The driver of this car is a convicted arab shoplifter. +No more blah, blah, blah! Kirk, Miri, stardate 2713.6. +No one can feel as helpless as the owner of a sick goldfish. +No one can guarantee the actions of another. Spock, stardate unknown. +No one hates a job well done! +No one wants war. Kirk, Errand of Mercy, stardate 3201.7. +No woman can shake off her mother. There should be no mothers, only women +Nobody ever went broke underestimating the taste of the American public. +Nostalgia is OK, but it's not what it used to be. +Not ignorance, but ignorance of ingnorance, is the death of knowledge. +Not one hundred percent efficient, of course.but nothing ever is. +Not tonight honey, ...I feel a modem coming on. +Nothing I have found is factual, except the bits that sound like fiction. +Nothing is true. Everything is permitted. +Nothing matters very much, and very few things matter at all. +Now and then an innocent man is sent to the legislature. +Now is the time for all good men to come to. +Nursing Law: All the IV trees are at the other end of the hall. +OUT TO LUNCH - If not back at five, OUT TO DINNER! +Obscenity is whatever gives a judge an erection. +Old age is life's parody. +Old fishermen never die, they just smell that way. +Old? The only thing that kept it standing was the woodworm holding hands. +Omens are there to be broken. +On a clear disk you can seek forever. +One child is not enough, but two are far too many. +One does not thank logic. Sarek, Journey to Babel, stardate 3842.4. +One fifth of the people are against everything all the time. +One good turn gets most of the blanket. +One has the right to be wrong in a democracy. +One of the freedoms of the English is the freedom from culture. +One of the minor pleasures in life is to be slightly ill. +One reason I don't drink is that I want to know when I'm having a good tim +One was more wise than the other. +Only dull people are brilliant at breakfast. +Open mouth, insert foot, echo internationally. +Opportunity: A favourable occasion for grasping a disappointment. +Organization is the enemy of improvisation. +Peace was the way. Kirk, The City on the Edge of Forever, stardate unknown +Please don't ask me what the score is. I'm not even sure what the game is. +Pohl's law: Nothing is so good that somebody, somewhere, will not hate it +Predestination was doomed from the start. +Preparation, knowledge, and discipline can deal with any form of danger. +Prepare to meet thy GOD! (Evening dress optional) +Profanity is the one language all programmers know best. +Professionals build the Titanic, amateurs built the Ark. +Pros are those who do their jobs well, even when they don't feel like it. +Psychologists only do it if they feel good about it +Pure drivel tends to drive ordinary drivel off the TV screen. +QWK? I don't need no stinkin' QWK packet! +RADICAL: A conservative out of a job. +RAM = Rarely Adequate Memory +RAM DISK is NOT an installation procedure! +RCs come and RCs go, but a good NC lasts forever! +Racial prejudice is a pigment of the imagination. +Real Programmers do List Processing in FORTRAN. +Real knowledge is to know the extent of ones ignorance. +Reality is for people who can't cope with their drugs. +Religions change; Beer and Wine remain. +Remind me never to put off until tomorrow the things I've already put off +Renegade Tagline!! We're tired of Being Kidnapped!!! REBEL!!!!! +Respect is a rational process. McCoy, The Galileo Seven, stardate 2822.3. +Revolution is the opiate of the intellectuals +Rugby is played by men with odd-shaped balls!! +Running a business is about 95% people and 5% economics. +SENILE.COM found...Out of Memory... +Save Water, Shower With A Friend +Save fuel. Get cremated with a friend. +Scepticism is the beginning of faith. +Schizophrenia divides and rules, OK? +Schizophrenia rules. OK. OK. +Scratch a lover and find a foe. +Scratch & Sniff .\\essage: Scratch Here --->��������<--- +Screw up your courage! You've screwed up everything else. +Seeing is deceiving. It's eating that's believing. +Send in competition answers with your name, age and how old you are. +Several excuses are always less convincing than one. +She drowned at the end of her life. +So Carol, you're a housewife and mother. And have you got any children? +Some men are discovered; others are found out. +Some of the crowd have decided to voice their opinion by staying away. +Some people confuse boredom with security. +Some things have got to be believed to be seen. +Sometimes I sits and thinks, and sometimes I just sits. +Sometimes a cigar is just a cigar. +Sometimes a man will tell his bartender things he'll never tell his doctor +Sorrow looks back, worry looks around, faith looks up. +Sow your wild oats on Saturday night, then on Sunday pray for crop failure +Spaceballs: The Tagline +Spring is God's way of saying, One more time! +Spring---an experience in immoratality. +Success is a public affair. Failure is a private funeral. +Success is being nothing but a quote. +Success is one unpardonable sin against one's fellows. +Success usually comes to those too busy to look for it. +Superior ability breeds superior ambition. Spock, stardate 3141.9. +TACT: The ability to make guests feel at home when you wish that they were +Tag line thievery's fun ...On to the next Geraldo! +TagLine support contract for renewal. Ignore this if you've already paid. +Take my advice, I don't use it anyway. +Take what you can use and let the rest go by. +That must be wonderful! I don't understand it at all. +That unit is a woman. A mass of conflicting impulses. Spock and Nomad +The beatings will continue until the moral improves. +The Coarse Golfer: One who has to shout 'Fore' when he puts. +The English have an extraordinary ability for flying into a great calm. +The English may not like music, but they absolutely love the noise it make +The English never forgive a man for being clever. +The Falklands war was a quarrel between two bald men over a comb. +The God's play games with men as balls. +The House of Lords has a value.it is good evidence of life after death. +The House of Lords is a model of how to care for the elderly. +The House of Lords is a perfect eventide home. +The OFFICIAL tagline of the 1996 Olympics! +The Russians are going forward, more in hope than optimism. +The art of communicating with a woman is to hear what she doesn't say. +The best audience is intelligent, well-educated and a little drunk. +The best number for a dinner party is 2. Myself and a damn good headwaite +The bigger they are...the harder they hit. +The body of a dead enemy always smells sweet. +The brain is as strong as its weakest think. +The cause of problems are solutions! +The champion has retired after eight undefeated victories. +The cure for admiring the house of lords is to go and look at it. +The deceased should be preserved by electroplating them. +The earth is a hollow shell and we live on the inside. +The families of one's friends are always a disappointment. +The flush toilet is the basis of Western civilisation. +The four stages of man are: infancy, childhood, adolescence and obsolescen +The government solution to a problem is usually as bad as the problem. +The greatest problem about old age is the fear that it may go on too long. +The happiest time of anyone's life is just after the first divorce. +The hookworm larvae enters the human body through the soul. +The house of Lords is the British Outer Mongolia for retired politicians. +The man who strikes first admits that his ideas have given out. +The manner in which it is given is worth more than the gift. +The mistake you make is in trying to figure it out. +The most delicate component will drop. +The most popular labour-saving device today is still a husband with money. +The nicest thing about growing older is that it takes such a long time. +The older you get, the more important is is not to act your age. +The one charm of marriage is that it makes a life of deception a necessity +The one way sure to conciliate a tiger to allow oneself to be devoured +The only good government.is a bad one in a hell of a fright. +The only tool diplomacy has is language. Hodin of Gideon, stardate 5423.4. +The poor man. He's completely unspoiled by failure. +The public is wonderfully tolerant. It forgives everything except genius. +The purpose of computing is insight, not numbers. +The quickest way to make your own anti-freeze is to hide her nightie. +The rich will do anything for the poor but get off their backs. +The sight of death frightens them [Earthers]. +The test of the morality of a society is what it does for its children. +The things most people want to know are usually none of their business. +The time to relax is when you don't have time for it. +The trouble with facts is that there are so many of them. +The true statesman is the one who is willing to take risks. +The truest wild beasts live in the most populous places. +The truth is NOT always dressed for the evening. +The unnatural, that too is natural. +The wages of sin are unreported. +The wheel that squeaks the loudest is the one that gets the grease. +The word 'meaningful' when used today is nearly always meaningless. +The world looks as if it has been left in the custody of trolls. +The worst thing about censorship is ����������. +The writer does the most who gives the reader the most +There are always alternatives. Spock, The Galileo Seven, stardate 2822.3. +There are certain things men must do to remain men. +There are no answers, only cross-references! +There are no atheists in the foxholes. +There are some things worth dying for. Kirk, Errand of Mercy, stardate 320 +There are things that are so serious that you can only joke about them +There is a multi-legged creature crawling on your shoulder. +There is an order of things in this universe. Apollo, stardate 3468.1. +There is more to life than increasing its speed. +There is much to be said for failure. It is more interesting than success +There is no greater loan than a sympathetic ear. +There is no such thing as a nonracial society in a multiracial country. +There is no such thing as justice - in or out of court. +There they are, every colour of the rainbow: black, white, brown. +There's little worse than being peerless in a peer-review system. +There's no intelligent life down here. +There's no trick to being a humorist when you have the whole government +There's nothing moister. Than an oyster! +They couldn't hit an elephant at this dist... +Things too stupid to be spoken are sung. +Think like a man of action, act like a man of thought. +This BBS has achieved Air superiority. +This tagline is SHAREWARE! To register, send me $10 +This tagline's just for you. +This was a reminder of an unforgettable voice -- wossisname! you know? +Those who stand for nothing fall for anything. +To a friends' house, the road is never long. +To be natural is such a very difficult pose to keep up. +To die is landing on some distant shore. +To eat is human; to digest divine. +To err is human, but to really foul things up requires a computer. +To know the world one must construct it. +To live is always desirable. Eleen the Capellan, stardate 3498.9. +To love oneself is the beginning of a lifelong romance. +To my embarrassment, I was born in bed with a lady! +To remove dust from the eye, pull the eye down over the nose. +To think too long about doing a thing often becomes its undoing. +Today is the first day of the rest of the mess. +Today's extravagance becomes tomorrow's necessity. +Tolkien is hobbit-forming. +Training a child is more or less a matter of pot luck. +Treat her like a lady and she'll always bring you home. +True love is when you spend �50 for an operation on a �5 dog. +Two most common elements in the universe: Hydrogen & Stupidity. +Typographers rule, OQ +Under capitalism man exploits man; under socialism the reverse is true. +Universal suffrage is the government of a house by its nursery. +Use it up.Wear it out. Make it do Or do without. +Variables won`t; constants aren`t. +Virtue is a relative term. Spock, Friday's Child, stardate 3499.1. +Voluteers are being given fake placebos. +Vulcans worship peace above all. McCoy, Return to Tomorrow, stardate 4768. +WARNING! Removal of this tagline prohibited by law! +WWhhaatt ddooeess dduupplleexx mmeeaann?? +Wait! You have not been prepared! Mr. Atoz, stardate 3113.2. +Waiter, this chicken's rubbery! Oh, fank you velly much! More fly lice? +Want to have some fun? Walk into an antique shop and say, What's new? +War is never imperative. McCoy, Balance of Terror, stardate 1709.2 +War will cease when men refuse to fight. +We Are Open Seven Days A Week, Including Sundays. +We are going to have peace even if we have to fight for it. +We have met the enemy and he is us. +We have phasers; I vote we blast 'em! +We never know whether we are victors or whether we are defeated. +We now return to our regularly scheduled flame-throwing. +We should weep for men at their birth, not their death. +We think he's dead, but we're afraid to ask. +We're free people. We belong to no one. Kirk, stardate 3259.2. +What a man needs in gardening is a cast iron back with a hinge in it. +What do you mean? You actually read this Tagline?!? +What he doesn't know would make a library anybody would be proud of. +What is moral is what you feel good after. +What is the Latin for office automation? +What's a cult? It just means not enough people to make a minority. +What?!? This isn't the Files section?!? +Whatever it is, it won't work. +Wheat was given to us by extraterrestrials called the Manu. +When Eve arrived, this was no longer a man's world. +When GOD made women, he was only testing. +When a man brings his wife flowers for no reason - there's a reason. +When in doubt, predict that the trend will continue. +When it's three O'clock in New York, it's still 1938 in London. +When one connects a 3-phase line, the phase sequence will be wrong. +When people are free to do as they please, they usually imitate each other +When two Englishmen meet their first talk is of the weather. +When you dial a wrong number, you NEVER get a busy signal. +When you haven't got enough iodine in your blood you get a glacier. +When you smell an odourless gas, it is probably carbon monoxide. +When your work speaks for itself, don`t interrupt. +Where there's a will, there's a lawsuit. +Who is General Failure, and why is he reading my disk? +Whosoever diggeth a pit shall falleth therein. +Why risk a hangover? Stay Drunk!! +Winning isn't the end of the world. +Witch! Witch! They'll burn ya! Hag, stardate unknown. +Without followers, evil cannot spread. Spock, stardate 5029.5. +Woman is one of nature's more agreeable blunders. +Woodpecker's, like British Telecom, have long bills. +Xerox your life. If you lose it, you'll still have a copy. +Yield to temptation, it may not pass your way again. - L. Long +You can drink 'em pretty, but can you drink 'em young? +You can tell when politicians are lying...They move their lips. +You can tune a piano, but you can`t tuna fish. +You can't evaluate a man by logic alone. McCoy, I, Mudd, stardate 4513.3. +You don't have to think too hard when you talk to teachers. +You keep saying that, I don't think it means what you think it means +You know you're getting old when the candles cost more than the cake. +You! What PLANET is this? McCoy, stardate 3134.0. +You'll never walk alone with schizophrenia. +You're too beautiful to ignore. Too much woman. Kirk to Yeoman Rand +You've got to miss them to score sometimes. +Young gorillas are friendly but they soon learn. +Youth doesn't excuse everything. Dr. Janice Lester stardate 5928.5. + +Chuck Norris counted to infinity - twice. +Chuck Norris' hand is the only hand that can beat a Royal Flush. +Chuck Norris doesn't wear a watch, HE decides what time it is. +Chuck Norris can slam a revolving door. +When Chuck Norris falls in water, Chuck Norris doesn't get wet. Water gets Chuck Norris. +Chuck Norris can divide by zero. +It takes Chuck Norris 20 minutes to watch 60 Minutes. +Some people wear Superman pajamas. Superman wears Chuck Norris pajamas. +Chuck Norris did in fact, build Rome in a day. +Some kids play Kick the Can. Chuck Norris played Kick the Keg. +When Chuck Norris crosses the street, the cars have to look both ways. +Chuck Norris knows the last digit of pi. +Chuck Norris doesn't study martial arts, the martial arts study Chuck Norris. +The chief export of Chuck Norris is pain. +When Chuck Norris breaks wind, the wind stays broken. +The only match for Chuck Norris is Chuck Norris, and Chuck Norris still wins. +Chuck Norris can have his cake and eat it too. +When Chuck Norris has surgery, the anesthesia is applied to the doctors. +Chuck Norris can kick a fart back into an ass. +Chuck Norris put the "fist" in "pacifist". +Chuck Norris can sleep while he's awake. +Chuck Norris was born in a log cabin which he built. +Chuck Norris can sleep while he's awake. +Chuck Norris can get 8 hours of sleep in 2 hours. +Chuck Norris can hear sign language and speak Braille. + +One time, a cobra bit Chuck Norris in the leg. After 5 days of excruciating pain, the cobra died. +As I get older, I've been thinking more about the hereafter. I'll go into a room and think, "What did I come in here after?" +Leonardo da Vinci could write with one hand and draw with the other at the same time. +Los Angeles' full name is "El Pueblo de Nuestra Senora la Reina de los Angeles de Porciuncula". It can be abbreviated to 3.63%% of its size: L.A. +A raisin dropped in a glass of fresh champagne will bounce up and down continuously from the bottom of the glass to the top. +The name of the popular lubricant WD-40 stands for "Water Displacement, 40th formula". +I don't like the fact that Parker Brothers is the only company that makes the game Monopoly. +For my birthday I got a humidifier and a de-humidifier... I put them in the same room and let them fight it out. +It doesn't make a difference what temperature a room is, it's always room temperature. +I went to a restaurant that serves "breakfast at any time". So I ordered French Toast during the Renaissance. +Why is it a penny for your thoughts, but you have to put your two cents in? Someone is making a penny. +When I was crossing the border into Canada, they asked if I had any firearms with me. I said, "What do you need?" +I installed a skylight in my apartment... the people who live above me are furious! +My girlfriend asked me how long I was going to be gone on this tour. I said, "the whole time". +I have a microwave fireplace in my house...The other night I relaxed in front of the fire for the evening in ten minutes. +I have the world's largest collection of seashells. I keep it on all the beaches of the world... perhaps you've seen it.... \ No newline at end of file diff --git a/ctrl/SlyIceColors_BlueIce.cfg b/ctrl/SlyIceColors_BlueIce.cfg index e19588b3d1..7b23f3e612 100644 --- a/ctrl/SlyIceColors_BlueIce.cfg +++ b/ctrl/SlyIceColors_BlueIce.cfg @@ -47,8 +47,9 @@ crossPostChkHighlight=n4hy crossPostMsgGrpMark=nhg crossPostMsgGrpMarkHighlight=n4hg -; Colors for the text replacement list -txtReplacementList=nc +; For the text items in list boxes +listBoxItemText=nc +listBoxItemHighlight=n4wh ; Colors for message saving and sub-board post info when exiting SlyEdit msgWillBePostedHdr=nc diff --git a/ctrl/SlyIceColors_EmeraldCity.cfg b/ctrl/SlyIceColors_EmeraldCity.cfg index b2ec661d7a..4924ea808e 100644 --- a/ctrl/SlyIceColors_EmeraldCity.cfg +++ b/ctrl/SlyIceColors_EmeraldCity.cfg @@ -47,8 +47,9 @@ crossPostChkHighlight=n4hy crossPostMsgGrpMark=nhg crossPostMsgGrpMarkHighlight=n4hg -; Colors for the text replacement list -txtReplacementList=ng +; For the text items in list boxes +listBoxItemText=ng +listBoxItemHighlight=n4wh ; Colors for message saving and sub-board post info when exiting SlyEdit msgWillBePostedHdr=nc diff --git a/ctrl/SlyIceColors_FieryInferno.cfg b/ctrl/SlyIceColors_FieryInferno.cfg index a22b541424..d62cdc8d3c 100644 --- a/ctrl/SlyIceColors_FieryInferno.cfg +++ b/ctrl/SlyIceColors_FieryInferno.cfg @@ -47,8 +47,9 @@ crossPostChkHighlight=n4hy crossPostMsgGrpMark=nhg crossPostMsgGrpMarkHighlight=n4hg -; Colors for the text replacement list -txtReplacementList=nrh +; For the text items in list boxes +listBoxItemText=nrh +listBoxItemHighlight=n7r ; Colors for message saving and sub-board post info when exiting SlyEdit msgWillBePostedHdr=nc diff --git a/ctrl/SlyIceColors_Fire-N-Ice.cfg b/ctrl/SlyIceColors_Fire-N-Ice.cfg index 1e7b7bff29..fc92b940d3 100644 --- a/ctrl/SlyIceColors_Fire-N-Ice.cfg +++ b/ctrl/SlyIceColors_Fire-N-Ice.cfg @@ -47,8 +47,9 @@ crossPostChkHighlight=n4hy crossPostMsgGrpMark=nhg crossPostMsgGrpMarkHighlight=n4hg -; Colors for the text replacement list -txtReplacementList=nbh +; For the text items in list boxes +listBoxItemText=nbh +listBoxItemHighlight=n4wh ; Colors for message saving and sub-board post info when exiting SlyEdit msgWillBePostedHdr=nc diff --git a/ctrl/SlyIceColors_GenericBlue.cfg b/ctrl/SlyIceColors_GenericBlue.cfg index 9c120c67aa..2643030bc2 100644 --- a/ctrl/SlyIceColors_GenericBlue.cfg +++ b/ctrl/SlyIceColors_GenericBlue.cfg @@ -47,8 +47,9 @@ crossPostChkHighlight=n4hy crossPostMsgGrpMark=nhg crossPostMsgGrpMarkHighlight=n4hg -; Colors for the text replacement list -txtReplacementList=nbh +; For the text items in list boxes +listBoxItemText=nbh +listBoxItemHighlight=n4wh ; Colors for message saving and sub-board post info when exiting SlyEdit msgWillBePostedHdr=nc diff --git a/ctrl/SlyIceColors_ShadesOfGrey.cfg b/ctrl/SlyIceColors_ShadesOfGrey.cfg index 1b4f42a4e1..6b805a9eb2 100644 --- a/ctrl/SlyIceColors_ShadesOfGrey.cfg +++ b/ctrl/SlyIceColors_ShadesOfGrey.cfg @@ -47,8 +47,9 @@ crossPostChkHighlight=n4hy crossPostMsgGrpMark=nhg crossPostMsgGrpMarkHighlight=n4hg -; Colors for the text replacement list -txtReplacementList=nw +; For the text items in list boxes +listBoxItemText=nw +listBoxItemHighlight=n4wh ; Colors for message saving and sub-board post info when exiting SlyEdit msgWillBePostedHdr=nc diff --git a/docs/SlyEdit_DD_Message_Lister_notes.txt b/docs/SlyEdit_DD_Message_Lister_notes.txt index 428390c0f6..90adc7395e 100644 --- a/docs/SlyEdit_DD_Message_Lister_notes.txt +++ b/docs/SlyEdit_DD_Message_Lister_notes.txt @@ -1,6 +1,6 @@ Please note that if you use Digital Distortion Message Lister, you must update the message lister to version 1.36 in order for it to work properly with -this version of SlyEdit (1.27). The reason is due to the way SlyEdit looks up +this version of SlyEdit (1.32). The reason is due to the way SlyEdit looks up message header information to get author's initials when quoting messages, and due to information that Digital Distortion Message Lister provides to SlyEdit. Version 1.36 of the message lister now always writes the message number to its @@ -14,6 +14,7 @@ Message Lister with the recent versions of SlyEdit: SlyEdit Digital Distortion message Lister ------- --------------------------------- +1.32 1.36+ 1.31 1.36+ 1.30 1.36+ 1.29 1.36+ diff --git a/docs/SlyEdit_ReadMe.txt b/docs/SlyEdit_ReadMe.txt index b886e1936f..299945a964 100644 --- a/docs/SlyEdit_ReadMe.txt +++ b/docs/SlyEdit_ReadMe.txt @@ -1,6 +1,6 @@ SlyEdit message editor - Version 1.31 - Release date: 2013-09-07 + Version 1.32 + Release date: 2013-09-18 by @@ -28,6 +28,8 @@ Contents 8. DCT-style Color Theme Settings 9. Common colors (appearing in both Ice and DCT color theme files) 10. Text replacements (AKA Macros) +11. User settings +12. Taglines 1. Disclaimer @@ -259,22 +261,6 @@ reWrapQuoteLines Whether or not to re-wrap quote lines. Valid be trimmed to make room for the quote prefix character to be added to the front. -useQuoteLineInitials Whether or not to prefix quoted message lines - with the previous author's initials when - replying to a message. Valued values are - true and false. If this setting is disabled, - SlyEdit will simply prefix the quoted lines - with " > ", as was done in IceEdit, DCT Edit, - and other message editors of the early-mid - 1990s. This setting is enabled by default. - -indentQuoteLinesWithInitials When prefixing quoted messages lines with the - previous author's initials, this setting - specifies whether or not to indent quoted - lines with a space. Valued values are true - and false. This setting is disabled by - default. - add3rdPartyStartupScript Add a 3rd-party JavaScript script to execute (via loading) upon startup of SlyEdit. The parameter must specify the full path & filename @@ -312,6 +298,43 @@ enableTextReplacements Toggles the use of text replacements (AKA information, see section 10 (Text replacements (AKA Macros)). +tagLineFilename Specifies the name of the file that stores + taglines, which users can optionally choose + from to be appended to their message upon + saving the message. + +allowUserSettings Whether or not to allow users to configure + their own user settings. This defaults to + true. + +useQuoteLineInitials Whether or not to prefix quoted message lines + with the previous author's initials when + replying to a message. Valued values are + true and false. If this setting is disabled, + SlyEdit will simply prefix the quoted lines + with " > ", as was done in IceEdit, DCT Edit, + and other message editors of the early-mid + 1990s. This setting is enabled by default. + This setting serves as the default for the + user setting, which users can configure in + their own settings. + +indentQuoteLinesWithInitials When prefixing quoted messages lines with the + previous author's initials, this setting + specifies whether or not to indent quoted + lines with a space. Valued values are true + and false. This setting is disabled by + default. This setting serves as the default + for the user setting, which users can + configure in their own settings. + +enableTagLines Whether or not to enable the option for users + to choose a tagline to append to their + message upon saving the message. This + setting serves as the default for the user + setting, which users can configure in their + own settings. + Ice colors ---------- Setting Description @@ -612,6 +635,14 @@ emptyMsgNotSentText The color to use for the Message Not Sent genMsgErrorText The color to use for general message error text when exiting SlyEdit +listBoxItemText The color to use for text appearing in list + boxes (such as the list of text replacements + and the list of tag lines) + +listBoxItemHighlight The color to use for the currently selected + item in list boxes (such as the list of text + replacements and the list of tag lines) + 10. Text replacements (AKA Macros) ================================== SlyEdit version 1.29 added text replacements (AKA Macros), which lets you (the @@ -703,4 +734,32 @@ text. In JavaScript, each numbered capture buffer is preceded by a dollar sign ($). For example, the regular expression (darn) will match the word "darn" and store it in buffer 1, and in JavaScript (and with SlyEdit's search and replace), you would use $1 to refer to the word "darn". For example, for -(darn), the replacement $1it would replace the word "darn" with "darnit". \ No newline at end of file +(darn), the replacement $1it would replace the word "darn" with "darnit". + +11. User settings +================= +SlyEdit version 1.32 added the ability for each user to configure some settings +for themselves. The user settings include the following: +- Whether or not to enable the option to use taglines +- Whether or not to add the original author's initials to quote lines + (this was previously only configurable in SlyEdit.cfg) +- Whether or not to indent quote lines that use author's intials (this was + previously only configurable in SlyEdit.cfg) + +The user settings files will be stored in the sbbs/data/user directory with the +filename <user number>.SlyEdit_Settings, and the user number will be 0-padded +up to 4 digits. + +12. Taglines +============ +SlyEdit version 1.32 added the ability for users to optionally choose a tagline +to be appended to their message upon saving the message. Each user can +configure in their user settings whether or not they want to enable this +feature. The setting enableTaglines in SlyEdit.cfg sets the default setting, +but users are free to change it for themselves. + +The taglines are loaded from the text file SlyEdit_Taglines.txt, which can be +located in any of the following directories, searched in the following order: +- sbbs/mods +- sbbs/ctrl +- The same directory as SlyEdit's .js files \ No newline at end of file diff --git a/docs/SlyEdit_Upgrading.txt b/docs/SlyEdit_Upgrading.txt index d508ae4d43..2e3cbffec9 100644 --- a/docs/SlyEdit_Upgrading.txt +++ b/docs/SlyEdit_Upgrading.txt @@ -1,46 +1,39 @@ -This file contains upgrade notes for sysops who are upgrading from -earlier versions of SlyEdit. +This file contains upgrade notes for sysops who are upgrading from earlier +versions of SlyEdit. Only versions with significant/noteworthy configuration +changes, etc. are listed in this file, with the most recent version on top. -Contents -======== -1. "Expand Line Feeds to CRLF" option (for users of version 1.07 and - below -2. New configuration file settings - - -"Expand Line Feeds to CRLF" option -================================== -If upgrading version version 1.07 or earlier of SlyEdit, the option -"Expand Line Feeds to CRLF" in SCFG should now be set to Yes. This -option enables Synchronet to save messages properly, regardless of -whether Synchronet is running in Windows or a *nix system. Enabling -this option fixes problems with line wrapping that was seen on Linux -Synchronet systems when the messages were sent across some networks -to other BBSs. Thanks goes to Access Denied, sysop of The Pharcyde, -for helping to test this. - -In Synchronet's configuration program, your SlyEdit -configuration should look like this (ICE mode is used here): -+[�][?]--------------------------------------------------------------+ -� SlyEdit (Ice style) Editor � -�--------------------------------------------------------------------� -� �Name SlyEdit (Ice style) � -� �Internal Code SLYEDICE � -� �Remote Command Line ?SlyEdit.js %f ICE � -� �Access Requirements ANSI � -� �Intercept Standard I/O No � -� �Native (32-bit) Executable No � -� �Use Shell to Execute No � -� �Quoted Text All � -� �Editor Information Files QuickBBS MSGINF/MSGTMP � -� �Expand Line Feeds to CRLF Yes � -� �Strip FidoNet Kludge Lines No � -� �BBS Drop File Type None � -+--------------------------------------------------------------------+ +Upgrading to version 1.32 +------------------------- +A new color setting has been added, listBoxItemHighlight, which is used for the +currently selected item in a list box. + +The color setting txtReplacementList has been changed to listBoxItemText. This +color applies to items written inside any list box, including the list of text +replacements and the list of tag lines. + +Taglines: Users can now (optionally) select a tagline to append to their +message upon saving their message. Users can configure whether or not they +want to use taglines in their user settings, which is also a new feature in +this version of SlyEdit (see the paragraph below). + +New configuration settings habe been added in SlyEdit.cfg: +- tagLineFilename: This specifies the name of a text file where tag lines are +stored. By default, it is SlyEdit_Taglines.txt. +- enableTaglines: This serves as a default for user settings for whether or not +to enable the option to add a tagline to their message upon saving the message. +- allowUserSettings: Whether or not to allow users to configure their user +settings. + +User settings: Users can now configure their own user settings in this version. +Settings include whether or not to use tag lines, whether or not to quote +messages using the original author's initials, and whether or not to indent +quoted lines with initials. + +The configuration settings useQuoteLineInitials indentQuoteLinesWithInitials +in SlyEdit.cfg now serve as defaults for user settings rather than being +used for everyone. -2. Notes and new configuration file settings -============================================ Upgrading to version 1.29 ------------------------- A new "text replacements" (AKA macros) feature has been added. This feature @@ -234,4 +227,34 @@ splitLongQuoteLines Whether or not to split quote lines be wrapped (rather than truncated), with the last word(s) on their own separate line. If this option is not specified, this feature - will be disabled by default. \ No newline at end of file + will be disabled by default. + +Upgrading to version 1.07 +------------------------- +If upgrading version version 1.07 or earlier of SlyEdit, the option +"Expand Line Feeds to CRLF" in SCFG should now be set to Yes. This +option enables Synchronet to save messages properly, regardless of +whether Synchronet is running in Windows or a *nix system. Enabling +this option fixes problems with line wrapping that was seen on Linux +Synchronet systems when the messages were sent across some networks +to other BBSs. Thanks goes to Access Denied, sysop of The Pharcyde, +for helping to test this. + +In Synchronet's configuration program, your SlyEdit +configuration should look like this (ICE mode is used here): ++[�][?]--------------------------------------------------------------+ +� SlyEdit (Ice style) Editor � +�--------------------------------------------------------------------� +� �Name SlyEdit (Ice style) � +� �Internal Code SLYEDICE � +� �Remote Command Line ?SlyEdit.js %f ICE � +� �Access Requirements ANSI � +� �Intercept Standard I/O No � +� �Native (32-bit) Executable No � +� �Use Shell to Execute No � +� �Quoted Text All � +� �Editor Information Files QuickBBS MSGINF/MSGTMP � +� �Expand Line Feeds to CRLF Yes � +� �Strip FidoNet Kludge Lines No � +� �BBS Drop File Type None � ++--------------------------------------------------------------------+ \ No newline at end of file diff --git a/exec/SlyEdit.js b/exec/SlyEdit.js index 16515fe8d7..a2fa1bf092 100644 --- a/exec/SlyEdit.js +++ b/exec/SlyEdit.js @@ -45,7 +45,7 @@ * replacement text as-is. * 2013-09-07 Eric Oulashin Version 1.31 * Bug fix: Updated ReadSlyEditConfigFile() to - * default cfgObj.genColors.txtReplacementList to + * default cfgObj.genColors.listBoxItemText to * ensure that it gets defined. * Bug fix: Made use of K_NOSPIN wherever user input * is done so that the spinning cursor doesn't overwrite @@ -54,6 +54,24 @@ * and getWordFromEditLine() to TextLine member * methods TextLine_doMacroTxtReplacement() and * TextLine_getWord() in SlyEdit_Misc.js. + * Version 1.32 Beta + * Started working on color text support again + * 2013-09-07 Eric Oulashin Disabled color text key action; started working + * on generic list box, tagline support, and + * user settings (to let users enable/disable + * tagline support for themselves). Created + * the function doUserSettings(). + * 2013-09-14 Eric Oulashin Worked on user settings & tagline selection. + * 2013-09-15 Eric Oulashin Worked on user settings & tagline selection. + * Also, moved the options for author initials in + * quote lines to user settings. + * 2013-09-17 Eric Oulashin Continuing to make fixes & optimizations here + * and there over the past couple days. SlyEdit + * now updates the user's time left on the screen, + * properly updates the INS/OVR mode text for insert/ + * overwrite mode in DCT mode when using a wide + * terminal; the text replacement list now only draws + * the side borders once. */ /* Command-line arguments: @@ -91,8 +109,12 @@ if (typeof(argv[1]) != "undefined") load("sbbsdefs.js"); load(gStartupPath + "SlyEdit_Misc.js"); -// Load program settings from SlyEdit.cfg +// Determine whether the user settings file exists +const userSettingsFileExistedOnStartup = file_exists(gUserSettingsFilename); + +// Load program settings from SlyEdit.cfg, and load the user configuratio nsettings var gConfigSettings = ReadSlyEditConfigFile(); +var gUserSettings = ReadUserSettingsFile(gConfigSettings); // Load any specified 3rd-party startup scripts for (var i = 0; i < gConfigSettings.thirdPartyLoadOnStart.length; ++i) load(gConfigSettings.thirdPartyLoadOnStart[i]); @@ -101,6 +123,7 @@ for (var i = 0; i < gConfigSettings.runJSOnStart.length; ++i) eval(gConfigSettings.runJSOnStart[i]); const EDITOR_PROGRAM_NAME = "SlyEdit"; +const ERRORMSG_PAUSE_MS = 1500; // This script requires Synchronet version 3.14 or higher. // Exit if the Synchronet version is below the minimum. @@ -126,8 +149,8 @@ if (!console.term_supports(USER_ANSI)) } // Constants -const EDITOR_VERSION = "1.31"; -const EDITOR_VER_DATE = "2013-09-07"; +const EDITOR_VERSION = "1.32"; +const EDITOR_VER_DATE = "2013-09-18"; // Program variables @@ -174,7 +197,6 @@ gCrossPostMsgSubs.subCodeExists = function(pSubCode) { return false; var grpIndex = msg_area.sub[pSubCode].grp_index; - //displayDebugText(1, 3, "grpIndex: " + grpIndex, console.getxy(), true, false); var foundIt = false; if (this.hasOwnProperty(grpIndex)) foundIt = this[grpIndex].hasOwnProperty(pSubCode); @@ -239,6 +261,7 @@ var fpUpdateInsertModeOnScreen = null; var fpDisplayBottomHelpLine = null; var fpHandleESCMenu = null; var fpDisplayTime = null; +var fpDisplayTimeRemaining = null; if (EDITOR_STYLE == "DCT") { load(gStartupPath + "SlyEdit_DCTStuff.js"); @@ -257,6 +280,7 @@ if (EDITOR_STYLE == "DCT") fpDisplayBottomHelpLine = DisplayBottomHelpLine_DCTStyle; fpHandleESCMenu = handleDCTESCMenu; fpDisplayTime = displayTime_DCTStyle; + fpDisplayTimeRemaining = displayTimeRemaining_DCTStyle; } else if (EDITOR_STYLE == "ICE") { @@ -276,6 +300,7 @@ else if (EDITOR_STYLE == "ICE") fpDisplayBottomHelpLine = DisplayBottomHelpLine_IceStyle; fpHandleESCMenu = handleIceESCMenu; fpDisplayTime = displayTime_IceStyle; + fpDisplayTimeRemaining = displayTimeRemaining_IceStyle; } // Temporary (for testing): Make the edit area small @@ -291,6 +316,7 @@ const gEditHeight = gEditBottom - gEditTop + 1; // Message display & edit variables var gInsertMode = "INS"; // Insert (INS) or overwrite (OVR) mode var gQuoteLines = new Array(); // Array of quote lines loaded from file, if in quote mode +var gUserHasOpenedQuoteWindow = false; // Whether or not the user has opened the quote line selection window var gQuoteLinesTopIndex = 0; // Index of the first displayed quote line var gQuoteLinesIndex = 0; // Index of the current quote line // The gEditLines array will contain TextLine objects storing the line @@ -386,49 +412,11 @@ if (dropFileName != undefined) gMsgSubj = info[2]; gMsgArea = info[4]; - // Now that we know the name of the message area - // that the message is being posted in, call - // getCurMsgInfo() to set gMsgAreaInfo. + // Now that we know the name of the message area + // that the message is being posted in, call + // getCurMsgInfo() to set gMsgAreaInfo. gMsgAreaInfo = getCurMsgInfo(gMsgArea); setMsgAreaInfoObj = true; - - // If we're configured to use poster's initials in the - // quote lines, then do it. - if (gConfigSettings.useQuoteLineInitials) - { - // For the name to use for quote line initials: - // If posting in a message sub-board, get the author's name from the - // header of the current message being read in the sub-board (in - // case the user changes the "To" name). Otherwise (if not posting in - // a message sub-board), use the gToName value read from the drop file. - // Remove any leading, multiple, or trailing spaces. - var quotedName = ""; - if (postingInMsgSubBoard(gMsgArea)) - { - quotedName = trimSpaces(getFromNameForCurMsg(gMsgAreaInfo), true, true, true); - if (quotedName.length == 0) - quotedName = trimSpaces(gToName, true, true, true); - } - else - quotedName = trimSpaces(gToName, true, true, true); - // If configured to indent quote lines w/ initials with - // a space, then do it. - gQuotePrefix = ""; - if (gConfigSettings.indentQuoteLinesWithInitials) - gQuotePrefix = " "; - // Use the initials or first 2 characters from the - // quoted name for gQuotePrefix. - var spaceIndex = quotedName.indexOf(" "); - if (spaceIndex > -1) // If a space exists, use the initials - { - gQuotePrefix += quotedName.charAt(0).toUpperCase(); - if (quotedName.length > spaceIndex+1) - gQuotePrefix += quotedName.charAt(spaceIndex+1).toUpperCase(); - gQuotePrefix += "> "; - } - else // A space doesn't exist; use the first 2 letters - gQuotePrefix += quotedName.substr(0, 2) + "> "; - } } } file_remove(dropFileName); @@ -470,23 +458,6 @@ if (inputFile.open("r", false)) gQuoteLines.push(textLine); } } - // If the setting to re-wrap quote lines is enabled, then do it. - // wrapQuoteLines() will also prefix the quote lines with author's - // initials if configured to do so. - // If not configured to re-wrap quote lines, then if configured to - // prefix quote lines with author's initials, then we need to - // prefix them here with gQuotePrefix. - if (gQuoteLines.length > 0) - { - if (gConfigSettings.reWrapQuoteLines) - wrapQuoteLines(gConfigSettings.useQuoteLineInitials, gConfigSettings.indentQuoteLinesWithInitials); - else if (gConfigSettings.useQuoteLineInitials) - { - var maxQuoteLineWidth = gEditWidth - gQuotePrefix.length; - for (var i = 0; i < gQuoteLines.length; ++i) - gQuoteLines[i] = quote_msg(gQuoteLines[i], maxQuoteLineWidth, gQuotePrefix); - } - } } else { @@ -781,8 +752,8 @@ function doEditLoop() // 1: Aborted var returnCode = 0; - // Set the shortcut keys. Note: Avoid CTRL_H because that - // is backspace. + // Set the shortcut keys. Note: Avoid CTRL_H because that + // is backspace. const ABORT_KEY = CTRL_A; const CROSSPOST_KEY = CTRL_C; const DELETE_LINE_KEY = CTRL_D; @@ -796,6 +767,7 @@ function doEditLoop() const PROGRAM_INFO_HELP_KEY = CTRL_R; const PAGE_DOWN_KEY = CTRL_S; const LIST_TXT_REPLACEMENTS_KEY = CTRL_T; + const USER_SETTINGS_KEY = CTRL_U; const PAGE_UP_KEY = CTRL_W; const EXPORT_FILE_KEY = CTRL_X; const SAVE_KEY = CTRL_Z; @@ -813,6 +785,11 @@ function doEditLoop() curpos.y = gEditTop; console.gotoxy(curpos); + // initialTimeLeft and updateTimeLeft will be used to keep track of the user's + // time remaining so that we can update the user's time left on the screen. + var initialTimeLeft = bbs.time_left; + var updateTimeLeft = false; + // Input loop var userInput = ""; var currentWordLength = getWordLength(gEditLinesIndex, gTextLineIndex); @@ -845,7 +822,7 @@ function doEditLoop() { case ABORT_KEY: // Before aborting, ask they user if they really want to abort. - if (promptYesNo("Abort message", false, "Abort")) + if (promptYesNo("Abort message", false, "Abort", false)) { returnCode = 1; // Aborted continueOn = false; @@ -862,7 +839,8 @@ function doEditLoop() continueOn = false; break; case CMDLIST_HELP_KEY: - displayCommandList(true, true, true, gCanCrossPost, gConfigSettings.userIsSysop, gConfigSettings.enableTextReplacements); + displayCommandList(true, true, true, gCanCrossPost, gConfigSettings.userIsSysop, + gConfigSettings.enableTextReplacements, gConfigSettings.allowUserSettings); clearEditAreaBuffer(); fpRedrawScreen(gEditLeft, gEditRight, gEditTop, gEditBottom, gTextAttrs, gInsertMode, gUseQuotes, gEditLinesIndex-(curpos.y-gEditTop), @@ -905,13 +883,20 @@ function doEditLoop() // Let the user change the text color. /*if (gConfigSettings.allowColorSelection) { - var retObject = doColorSelection(curpos, currentWordLength); - curpos.x = retObject.x; - curpos.y = retObject.y; - currentWordLength = retObject.currentWordLength; - // If user input timed out, then abort. - if (retObject.timedOut) + var retObj = doColorSelection(gTextAttrs, curpos, currentWordLength); + if (!retObj.timedOut) + { + // Note: DoColorSelection() will prefix the color with the normal + // attribute. + gTextAttrs = retObj.txtAttrs; + console.print(gTextAttrs); + curpos.x = retObj.x; + curpos.y = retObj.y; + currentWordLength = retObj.currentWordLength; + } + else { + // User input timed out, so abort. returnCode = 1; // Aborted continueOn = false; console.crlf(); @@ -1158,7 +1143,8 @@ function doEditLoop() else if (retObject.showHelp) { displayProgramInfo(true, false); - displayCommandList(false, false, true, gCanCrossPost, gConfigSettings.userIsSysop, gConfigSettings.enableTextReplacements); + displayCommandList(false, false, true, gCanCrossPost, gConfigSettings.userIsSysop, + gConfigSettings.enableTextReplacements, gConfigSettings.allowUserSettings); clearEditAreaBuffer(); fpRedrawScreen(gEditLeft, gEditRight, gEditTop, gEditBottom, gTextAttrs, gInsertMode, gUseQuotes, gEditLinesIndex-(curpos.y-gEditTop), @@ -1347,6 +1333,9 @@ function doEditLoop() if (gConfigSettings.enableTextReplacements) listTextReplacements(); break; + case USER_SETTINGS_KEY: + doUserSettings(curpos, true); + break; default: // For the tab character, insert 3 spaces. Otherwise, // if it's a printable character, add the character. @@ -1374,10 +1363,50 @@ function doEditLoop() break; } - // For every 5 keys pressed, dheck the current time and update - // it on the screen if necessary. - if (numKeysPressed % 5 == 0) - updateTime(); + // Update the time strings on the screen + updateTimeLeft = (initialTimeLeft - bbs.time_left >= 60); + // For every 5 keys pressed, check the current time and update + // it on the screen if necessary. updateTime() is also being + // called when the first key is pressed so that the function's + // time string variable gets initially set. + // Note: The 2nd parameter to updateTime() is whether or not to move the + // cursor back to the original location after updating the time on the + // screen. For optimization, we don't want to do that if we'll also be + // updating the time left on the screen. + if ((numKeysPressed == 1) || (numKeysPressed % 5 == 0)) + updateTime(curpos, !updateTimeLeft); + // If the user's time left has gone down by at least 60 seconds, then + // update the time & user's time left on the screen. + if (updateTimeLeft) + { + fpDisplayTimeRemaining(); + // Change back to the edit color and place the cursor back + // where it needs to be. + console.print(chooseEditColor()); + console.gotoxy(curpos); // Place the cursor back where it needs to be + + initialTimeLeft = bbs.time_left; + } + } + + // If the user has not aborted the message and taglines is enabled in their + // user settings, then prompt them for a tag line to be appended to the message. + if ((returnCode == 0) && gUserSettings.enableTaglines && + txtFileContainsLines(gConfigSettings.tagLineFilename)) + { + if (promptYesNo("Add a tagline", true, "Add tagline", true)) + { + var taglineRetObj = doTaglineSelection(); + if (taglineRetObj.taglineWasSelected && taglineRetObj.tagline.length > 0) + { + // Append a blank line and then append the tagline to the message + gEditLines.push(new TextLine()); + var newLine = new TextLine(); + newLine.text = taglineRetObj.tagline; + gEditLines.push(newLine); + reAdjustTextLines(gEditLines, gEditLines.length-1, gEditLines.length, gEditWidth); + } + } } // If gEditLines has only 1 line in it and it's blank, then @@ -1964,7 +1993,7 @@ function doEnterKey(pCurpos, pCurrentWordLength) else if (lineUpper == "/A") { // Confirm with the user - if (promptYesNo("Abort message", false, "Abort")) + if (promptYesNo("Abort message", false, "Abort", false)) { retObj.returnCode = 1; // 1: Abort retObj.continueOn = false; @@ -2043,7 +2072,30 @@ function doEnterKey(pCurpos, pCurrentWordLength) gTextLineIndex = 0; retObj.x = gEditLeft; retObj.y = pCurpos.y; - // Blank out the /C on the screen + // Blank out the /T on the screen + //console.print("n" + gTextAttrs); + console.print(chooseEditColor()); + retObj.x = gEditLeft; + console.gotoxy(retObj.x, retObj.y); + console.print(" "); + // Put the cursor where it should be and return. + console.gotoxy(retObj.x, retObj.y); + return(retObj); + } + else if (lineUpper == "/U") + { + var currentCursorPos = new Object(); + currentCursorPos.x = retObj.x; + currentCursorPos.y = retObj.y; + doUserSettings(currentCursorPos, false); + // Blank out the data in the text line, set the data in + // retObj, and return it. + gEditLines[gEditLinesIndex].text = ""; + retObj.currentWordLength = 0; + gTextLineIndex = 0; + retObj.x = gEditLeft; + retObj.y = pCurpos.y; + // Blank out the /T on the screen //console.print("n" + gTextAttrs); console.print(chooseEditColor()); retObj.x = gEditLeft; @@ -2310,6 +2362,68 @@ function doQuoteSelection(pCurpos, pCurrentWordLength) if ((gQuoteLines.length == 0) || !gUseQuotes) return retObj; + // The first time this function runs, save the user's settings for using initials + // in quote lines and whether or not to indent quote lines containing initials. + // These will be checked against the user's current settings to see if we need + // to wrap the quote lines again in case the user changed these settings. + if (typeof(doQuoteSelection.useQuoteLineInitials) == "undefined") + doQuoteSelection.useQuoteLineInitials = gUserSettings.useQuoteLineInitials; + if (typeof(doQuoteSelection.indentQuoteLinesWithInitials) == "undefined") + doQuoteSelection.indentQuoteLinesWithInitials = gUserSettings.indentQuoteLinesWithInitials; + + // If the setting to re-wrap quote lines is enabled, then do it. + // We're re-wrapping the quote lines here in case the user changes their + // setting for prefixing quote lines with author initials. + // wrapQuoteLines() will also prefix the quote lines with author's + // initials if configured to do so. + // If not configured to re-wrap quote lines, then if configured to + // prefix quote lines with author's initials, then we need to + // prefix them here with gQuotePrefix. + if (gQuoteLines.length > 0) + { + // The first time this function runs, create a backup array of the original + // quote lines + if (typeof(doQuoteSelection.backupQuoteLines) == "undefined") + { + doQuoteSelection.backupQuoteLines = new Array(); + for (var i = 0; i < gQuoteLines.length; ++i) + doQuoteSelection.backupQuoteLines.push(gQuoteLines[i]); + } + + // If this is the first time the user has opened the quote window or if the + // user has changed their settings for using author's initials in quote lines, + // then re-copy the original quote lines into gQuoteLines and re-wrap them. + if (!gUserHasOpenedQuoteWindow || + (doQuoteSelection.useQuoteLineInitials != gUserSettings.useQuoteLineInitials) || + (doQuoteSelection.indentQuoteLinesWithInitials != gUserSettings.indentQuoteLinesWithInitials)) + { + doQuoteSelection.useQuoteLineInitials = gUserSettings.useQuoteLineInitials; + doQuoteSelection.indentQuoteLinesWithInitials = gUserSettings.indentQuoteLinesWithInitials; + + // If the user has opened the quote window before, then empty gQuoteLines + // and re-copy the original quote lines back into it. + if (gUserHasOpenedQuoteWindow) + { + gQuoteLines.length = 0; + for (var i = 0; i < doQuoteSelection.backupQuoteLines.length; ++i) + gQuoteLines.push(doQuoteSelection.backupQuoteLines[i]); + } + + gQuoteLinesTopIndex = gQuoteLinesIndex = 0; // To prevent bad things + + // Update the quote line prefix text and wrap the quote lines + setQuotePrefix(); + if (gConfigSettings.reWrapQuoteLines) + wrapQuoteLines(gUserSettings.useQuoteLineInitials, gUserSettings.indentQuoteLinesWithInitials); + else if (gUserSettings.useQuoteLineInitials) + { + var maxQuoteLineWidth = gEditWidth - gQuotePrefix.length; + for (var i = 0; i < gQuoteLines.length; ++i) + gQuoteLines[i] = quote_msg(gQuoteLines[i], maxQuoteLineWidth, gQuotePrefix); + } + } + } + // Set up some variables var curpos = new Object(); curpos.x = pCurpos.x; @@ -2485,6 +2599,8 @@ function doQuoteSelection(pCurpos, pCurrentWordLength) // Put the cursor where it should be. console.gotoxy(curpos); + gUserHasOpenedQuoteWindow = true; + // Set the settings in the return object, and return it. retObj.x = curpos.x; retObj.y = curpos.y; @@ -2641,7 +2757,7 @@ function getQuoteTextLine(pIndex, pMaxWidth) var textLine = ""; if ((pIndex >= 0) && (pIndex < gQuoteLines.length)) { - if (gConfigSettings.useQuoteLineInitials) + if (gUserSettings.useQuoteLineInitials) { if ((gQuoteLines[pIndex] != null) && (gQuoteLines[pIndex].length > 0)) textLine = gQuoteLines[pIndex].substr(0, pMaxWidth-1); @@ -2973,7 +3089,7 @@ function handleDCTESCMenu(pCurpos, pCurrentWordLength) (menuChoice == DCTMENU_FILE_ABORT)) { // Before aborting, ask they user if they really want to abort. - if (promptYesNo("Abort message", false, "Abort")) + if (promptYesNo("Abort message", false, "Abort", false)) { returnObj.returnCode = 1; // Aborted returnObj.continueOn = false; @@ -3020,7 +3136,8 @@ function handleDCTESCMenu(pCurpos, pCurrentWordLength) // Command List else if ((menuChoice == "O") || (menuChoice == DCTMENU_HELP_COMMAND_LIST)) { - displayCommandList(true, true, true, gCanCrossPost, gConfigSettings.userIsSysop, gConfigSettings.enableTextReplacements); + displayCommandList(true, true, true, gCanCrossPost, gConfigSettings.userIsSysop, + gConfigSettings.enableTextReplacements, gConfigSettings.allowUserSettings); clearEditAreaBuffer(); fpRedrawScreen(gEditLeft, gEditRight, gEditTop, gEditBottom, gTextAttrs, gInsertMode, gUseQuotes, gEditLinesIndex-(pCurpos.y-gEditTop), @@ -3070,6 +3187,9 @@ function handleDCTESCMenu(pCurpos, pCurrentWordLength) if (gConfigSettings.enableTextReplacements) listTextReplacements(); } + // User settings + else if ((menuChoice == CTRL_U) || (menuChoice == "N") || (menuChoice == DCTMENU_EDIT_SETTINGS)) + doUserSettings(pCurpos, true); // Make sure the edit color attribute is set back. //console.print("n" + gTextAttrs); @@ -3114,7 +3234,7 @@ function handleIceESCMenu(pCurpos, pCurrentWordLength) break; case ICE_ESC_MENU_ABORT: // Before aborting, ask they user if they really want to abort. - if (promptYesNo("Abort message", false, "Abort")) + if (promptYesNo("Abort message", false, "Abort", false)) { returnObj.returnCode = 1; // Aborted returnObj.continueOn = false; @@ -3123,9 +3243,13 @@ function handleIceESCMenu(pCurpos, pCurrentWordLength) case ICE_ESC_MENU_EDIT: // Nothing needs to be done for this option. break; + case ICE_ESC_MENU_SETTINGS: + doUserSettings(pCurpos, true); + break; case ICE_ESC_MENU_HELP: displayProgramInfo(true, false); - displayCommandList(false, false, true, gCanCrossPost, gConfigSettings.userIsSysop, gConfigSettings.enableTextReplacements); + displayCommandList(false, false, true, gCanCrossPost, gConfigSettings.userIsSysop, + gConfigSettings.enableTextReplacements, gConfigSettings.allowUserSettings); clearEditAreaBuffer(); fpRedrawScreen(gEditLeft, gEditRight, gEditTop, gEditBottom, gTextAttrs, gInsertMode, gUseQuotes, gEditLinesIndex-(pCurpos.y-gEditTop), @@ -3360,10 +3484,10 @@ function importFile(pIsSysop, pCurpos) continueOn = false; } else // Unable to open the file - writeWithPause(1, console.screen_rows, "yhUnable to open the file!", 1500); + writeWithPause(1, console.screen_rows, "yhUnable to open the file!", ERRORMSG_PAUSE_MS); } else // Could not find the correct case for the file (it doesn't exist?) - writeWithPause(1, console.screen_rows, "yhUnable to locate the file!", 1500); + writeWithPause(1, console.screen_rows, "yhUnable to locate the file!", ERRORMSG_PAUSE_MS); } } @@ -3450,13 +3574,13 @@ function exportToFile(pIsSysop) outFile.write(gEditLines[i].text); } outFile.close(); - writeWithPause(1, console.screen_rows, "mhMessage exported.", 1500); + writeWithPause(1, console.screen_rows, "mhMessage exported.", ERRORMSG_PAUSE_MS); } else // Could not open the file for writing - writeWithPause(1, console.screen_rows, "yhUnable to open the file for writing!", 1500); + writeWithPause(1, console.screen_rows, "yhUnable to open the file for writing!", ERRORMSG_PAUSE_MS); } else // No filename specified - writeWithPause(1, console.screen_rows, "mhMessage not exported.", 1500); + writeWithPause(1, console.screen_rows, "mhMessage not exported.", ERRORMSG_PAUSE_MS); // Refresh the help line on the bottom of the screen fpDisplayBottomHelpLine(console.screen_rows, gUseQuotes); @@ -3582,7 +3706,7 @@ function findText(pCurpos) console.gotoxy(1, console.screen_rows); console.cleartoeol("n"); console.print("yhThe text wasn't found!"); - mswait(1500); + mswait(ERRORMSG_PAUSE_MS); findText.searchStartIndex = 0; } @@ -3634,7 +3758,12 @@ function calcBottomUpdateRow(pY, pTopIndex) // This function updates the time on the screen and puts // the cursor back to where it was. -function updateTime() +// +// Parameters: +// pCurpos: An object containg the X and Y coordinates of the cursor position +// pMoveCursorBack: Boolean - Whether or not to move the cursor back after updating +// the time on the screen +function updateTime(pCurpos, pMoveCursorBack) { if (typeof(updateTime.timeStr) == "undefined") updateTime.timeStr = getCurrentTimeStr(); @@ -3646,13 +3775,14 @@ function updateTime() { // Get the current cursor position so we can move // the cursor back there when we're done. - var curpos = console.getxy(); + var curpos = (typeof(pCurpos) == "object" ? pCurpos : console.getxy()); // Display the current time on the screen fpDisplayTime(currentTime); // Make sure the edit color attribute is set. console.print("n" + gTextAttrs); // Move the cursor back to where it was - console.gotoxy(curpos); + if (pMoveCursorBack) + console.gotoxy(curpos); // Update this function's time variable updateTime.timeStr = currentTime; } @@ -3661,281 +3791,72 @@ function updateTime() // This function lets the user change the text color and is called by doEditLoop(). // // Parameters: +// pTxtAttrs: The current text color & attributes // pCurpos: An object containing x and y values representing the // cursor position. // pCurrentWordLength: The length of the current word that has been typed // // Return value: An object containing the following properties: +// txtAttrs: The chosen text color & attributes // x and y: The horizontal and vertical cursor position // timedOut: Whether or not the user input timed out (boolean) // currentWordLength: The length of the current word -function doColorSelection(pCurpos, pCurrentWordLength) +function doColorSelection(pTxtAttrs, pCurpos, pCurrentWordLength) { // Create the return object var retObj = new Object(); + retObj.txtAttrs = pTxtAttrs; retObj.x = pCurpos.x; retObj.y = pCurpos.y; retObj.timedOut = false; retObj.currentWordLength = pCurrentWordLength; + + const originalScreenY = pCurpos.y; // For screen refreshing - // Note: The current text color is stored in gTextAttrs - + // Display the 3 rows of color/attribute options and the prompt for the + // user var colorSelTopLine = console.screen_rows - 2; var curpos = new Object(); curpos.x = 1; curpos.y = colorSelTopLine; console.gotoxy(curpos); - console.print("nForeground: whK:nkBlack whR:nrRed whG:ngGreen whY:nyYellow whB:nbBlue whM:nmMagenta whC:ncCyan whW:nwWhite"); + console.print("ncForeground: whK:nkBlack whR:nrRed whG:ngGreen whY:nyYellow whB:nbBlue whM:nmMagenta whC:ncCyan whW:nwWhite"); console.cleartoeol("n"); console.crlf(); - console.print("nBackground: wh0:n" + gTextAttrs + "0Blackn wh1:n" + gTextAttrs + "1Redn wh2:n" + gTextAttrs + "2Greenn wh3:n" + gTextAttrs + "3Yellown wh4:n" + gTextAttrs + "4Bluen wh5:n" + gTextAttrs + "5Magentan wh6:n" + gTextAttrs + "6Cyann wh7:n" + gTextAttrs + "7White"); + console.print("ncBackground: wh0:n0Blackn wh1:n1Redn wh2:n2kGreenn wh3:3Yellown wh4:n4Bluen wh5:n5Magentan wh6:n6kCyann wh7:n7kWhite"); console.cleartoeol("n"); console.crlf(); console.clearline("n"); - //Special: H:High Intensity I:Blinking N:Normal � Choose Color: - console.print("Special: whH:n" + gTextAttrs + "hHigh Intensity wI:n" + gTextAttrs + "iBlinking nwhN:nNormal c� nChoose Color: "); - var attr = FORE_ATTR; - var toggle = true; + console.print("cSpecial: whH:nhHigh Intensity wI:niBlinking nwhN:nNormal hg� ncChoose colors/attributeshg: c"); + // Get the attribute codes from the user. Ideally, we'd use console.getkeys(), + // but that outputs a CR at the end, which is undesirable. So instead, we call + // getUserInputWithSetOfInputStrs (defined in SlyEdit_Misc.js). //var key = console.getkeys("KRGYBMCW01234567HIN").toString(); // Outputs a CR.. bad - var key = getUserKey(K_UPPER|K_NOCRLF|K_NOSPIN, gConfigSettings); - switch (key) - { - // Foreground colors: - case 'K': // Black - case 'R': // Red - case 'G': // Green - case 'Y': // Yellow - case 'B': // Blue - case 'M': // Magenta - case 'C': // Cyan - case 'W': // White - attr = FORE_ATTR; - break; - // Background colors: - case '0': // Black - case '1': // Red - case '2': // Green - case '3': // Yellow - case '4': // Blue - case '5': // Magenta - case '6': // Cyan - case '7': // White - attr = BKG_ATTR; - break; - // Special attributes: - case 'H': // High intensity - case 'I': // Blinking - attr = SPECIAL_ATTR; - break; - case 'N': // Normal - gTextAttrs = "N"; - toggle = false; - break; - default: - toggle = false; - break; - } - if (key != "Q") + var validKeys = ["KRGYBMCW", // Foreground color codes + "01234567", // Background color codes + "HIN"]; // Special color codes + var attrCodeKeys = getUserInputWithSetOfInputStrs(K_UPPER|K_NOCRLF|K_NOSPIN, validKeys, gConfigSettings); + // If the user entered some attributes, then set them in retObj.txtAttrs. + if (attrCodeKeys.length > 0) { - if (toggle) - { - gTextAttrs = toggleAttr(attr, gTextAttrs, key); - // TODO: Set the attribute in the current text line - } + retObj.txtAttrs = (attrCodeKeys.charAt(0) == "N" ? "" : "n"); + for (var i = 0; i < attrCodeKeys.length; ++i) + retObj.txtAttrs += "" + attrCodeKeys.charAt(i); } - // Display the parts of the screen text that we covered up with the // color selection: Message edit lines, bottom border, and bottom help line. - displayEditLines(colorSelTopLine, gEditLinesIndex-(gEditBottom-colorSelTopLine), - gEditBottom, true, true); + var screenYDiff = colorSelTopLine - originalScreenY; + displayEditLines(colorSelTopLine, gEditLinesIndex + screenYDiff, gEditBottom, true, true); fpDisplayTextAreaBottomBorder(gEditBottom+1, gUseQuotes, gEditLeft, gEditRight, gInsertMode, gConfigSettings.allowColorSelection); fpDisplayBottomHelpLine(console.screen_rows, gUseQuotes); - console.print(gTextAttrs); - // Move the cursor to where it should be before returning curpos.x = pCurpos.x; curpos.y = pCurpos.y; console.gotoxy(curpos); - // This code was copied from doQuoteSelection(). -/* - // Set up some variables - var curpos = new Object(); - curpos.x = pCurpos.x; - curpos.y = pCurpos.y; - const quoteWinHeight = 8; - // The first and last lines on the screen where quote lines are written - const quoteTopScreenRow = console.screen_rows - quoteWinHeight + 2; - const quoteBottomScreenRow = console.screen_rows - 2; - // Quote window parameters - const quoteWinTopScreenRow = quoteTopScreenRow-1; - const quoteWinWidth = gEditRight - gEditLeft + 1; - - // Display the top border of the quote window. - fpDrawQuoteWindowTopBorder(quoteWinHeight, gEditLeft, gEditRight); - - // Display the remainder of the quote window, with the quote lines in it. - displayQuoteWindowLines(gQuoteLinesTopIndex, quoteWinHeight, quoteWinWidth, true, gQuoteLinesIndex); - - // Position the cursor at the currently-selected quote line. - var screenLine = quoteTopScreenRow + (gQuoteLinesIndex - gQuoteLinesTopIndex); - console.gotoxy(gEditLeft, screenLine); - - // User input loop - var quoteLine = getQuoteTextLine(gQuoteLinesIndex, quoteWinWidth); - retObj.timedOut = false; - var userInput = null; - var continueOn = true; - while (continueOn) - { - // Get a keypress from the user - userInput = getUserKey(K_UPPER|K_NOCRLF|K_NOSPIN, gConfigSettings); - if (userInput == "") - { - // The input timeout was reached. Abort. - retObj.timedOut = true; - continueOn = false; - break; - } - - // If we got here, that means the user input didn't time out. - switch (userInput) - { - case KEY_UP: - // Go up 1 quote line - if (gQuoteLinesIndex > 0) - { - // If the cursor is at the topmost position, then - // we need to scroll up 1 line in gQuoteLines. - if (screenLine == quoteTopScreenRow) - { - --gQuoteLinesIndex; - --gQuoteLinesTopIndex; - quoteLine = getQuoteTextLine(gQuoteLinesIndex, quoteWinWidth); - // Redraw the quote lines in the quote window. - displayQuoteWindowLines(gQuoteLinesIndex, quoteWinHeight, quoteWinWidth, - true, gQuoteLinesIndex); - // Put the cursor back where it should be. - console.gotoxy(gEditLeft, screenLine); - } - // If the cursor is below the topmost position, then - // we can just go up 1 line. - else if (screenLine > quoteTopScreenRow) - { - // Write the current quote line using the normal color - // Note: This gets the quote line again using getQuoteTextLine() - // so that the color codes in the line will be correct. - quoteLine = getQuoteTextLine(gQuoteLinesIndex, quoteWinWidth); - console.gotoxy(gEditLeft, screenLine); - printf(gFormatStrWithAttr, gQuoteWinTextColor, quoteLine); - - // Go up one line and display that quote line in the - // highlighted color. - --screenLine; - --gQuoteLinesIndex; - quoteLine = strip_ctrl(getQuoteTextLine(gQuoteLinesIndex, quoteWinWidth)); - console.gotoxy(gEditLeft, screenLine); - printf(gFormatStrWithAttr, gQuoteLineHighlightColor, quoteLine); - - // Make sure the cursor is where it should be. - console.gotoxy(gEditLeft, screenLine); - } - } - break; - case KEY_DOWN: - // Go down 1 line in the quote window. - var downRetObj = moveDownOneQuoteLine(gQuoteLinesIndex, screenLine, - quoteWinHeight, quoteWinWidth, - quoteBottomScreenRow); - gQuoteLinesIndex = downRetObj.quoteLinesIndex; - screenLine = downRetObj.screenLine; - quoteLine = downRetObj.quoteLine; - break; - case KEY_ENTER: - // numTimesToMoveDown specifies how many times to move the cursor - // down after inserting the quote line into the message. - var numTimesToMoveDown = 1; - - // Insert the quote line into gEditLines after the current gEditLines index. - var insertedBelow = insertLineIntoMsg(gEditLinesIndex, quoteLine, true, true); - if (insertedBelow) - { - // The cursor will need to be moved down 1 more line. - // So, increment numTimesToMoveDown, and set curpos.x - // and gTextLineIndex to the beginning of the line. - ++numTimesToMoveDown; - curpos.x = gEditLeft; - gTextLineIndex = 0; - retObj.currentWordLength = getWordLength(gEditLinesIndex, gTextLineIndex); - } - else - retObj.currentWordLength = 0; - - // Refresh the part of the message that needs to be refreshed on the - // screen (above the quote window). - if (curpos.y < quoteTopScreenRow-1) - displayEditLines(curpos.y, gEditLinesIndex, quoteTopScreenRow-2, false, true); - - gEditLinesIndex += numTimesToMoveDown; - - // Go down one line in the quote window. - var tempReturnObj = moveDownOneQuoteLine(gQuoteLinesIndex, screenLine, - quoteWinHeight, quoteWinWidth, - quoteBottomScreenRow); - gQuoteLinesIndex = tempReturnObj.quoteLinesIndex; - screenLine = tempReturnObj.screenLine; - quoteLine = tempReturnObj.quoteLine; - - // Move the cursor down as specified by numTimesToMoveDown. If - // the cursor is at the bottom of the edit area, then refresh - // the message on the screen, scrolled down by one line. - for (var i = 0; i < numTimesToMoveDown; ++i) - { - if (curpos.y == gEditBottom) - { - // Refresh the message on the screen, scrolled down by - // one line, but only if this is the last time we're - // doing this (for efficiency). - if (i == numTimesToMoveDown-1) - { - displayEditLines(gEditTop, gEditLinesIndex-(gEditBottom-gEditTop), - quoteTopScreenRow-2, false, true); - } - } - else - ++curpos.y; - } - break; - // ESC or CTRL-Q: Stop quoting - case KEY_ESC: - case CTRL_Q: - // Quit out of the input loop (get out of quote mode). - continueOn = false; - break; - } - } - - // We've exited quote mode. Refresh the message text on the screen. Note: - // This will refresh only the quote window portion of the screen if the - // cursor row is at or below the top of the quote window, and it will also - // refresh the screen if the cursor row is above the quote window. - displayEditLines(quoteWinTopScreenRow, gEditLinesIndex-(curpos.y-quoteWinTopScreenRow), - gEditBottom, true, true); - - // Draw the bottom edit border to erase the bottom border of the - // quote window. - fpDisplayTextAreaBottomBorder(gEditBottom+1, gUseQuotes, gEditLeft, gEditRight, - gInsertMode, gConfigSettings.allowColorSelection); - - // Make sure the color is correct for editing. - //console.print("n" + gTextAttrs); - console.print(chooseEditColor()); - // Put the cursor where it should be. - console.gotoxy(curpos); -*/ // Set the settings in the return object, and return it. retObj.x = curpos.x; retObj.y = curpos.y; @@ -5320,7 +5241,7 @@ function listTextReplacements() { if (gNumTxtReplacements == 0) { - writeMsgOntBtmHelpLineWithPause("nhyThere are no text replacements.", 1500); + writeMsgOntBtmHelpLineWithPause("nhyThere are no text replacements.", ERRORMSG_PAUSE_MS); return; } @@ -5385,7 +5306,7 @@ function listTextReplacements() // printf format strings for the list if (typeof(listTextReplacements.listFormatStr) == "undefined") { - listTextReplacements.listFormatStr = "n" + gConfigSettings.genColors.txtReplacementList + listTextReplacements.listFormatStr = "n" + gConfigSettings.genColors.listBoxItemText + "%-" + txtWidth + "s %-" + txtWidth + "s"; } if (typeof(listTextReplacements.listFormatStrNormalAttr) == "undefined") @@ -5404,6 +5325,15 @@ function listTextReplacements() console.print(listTextReplacements.topBorder); console.gotoxy(boxInfo.topLeftX, boxInfo.topLeftY+boxInfo.height-1); console.print(listTextReplacements.bottomBorder); + // Draw the side borders + console.print("n" + gConfigSettings.genColors.listBoxBorder); + for (var i = 0; i < boxInfo.height-2; ++i) + { + console.gotoxy(boxInfo.topLeftX, boxInfo.topLeftY+i+1); + console.print(VERTICAL_SINGLE); + console.gotoxy(boxInfo.topLeftX+boxInfo.width-1, boxInfo.topLeftY+i+1); + console.print(VERTICAL_SINGLE); + } // Set up some variables for the user input loop const numItemsPerPage = boxInfo.height - 2; @@ -5433,24 +5363,19 @@ function listTextReplacements() screenY = boxInfo.topLeftY + 1; for (var i = startArrIndex; i < endArrIndex; ++i) { - console.gotoxy(boxInfo.topLeftX, screenY); - console.print("n" + gConfigSettings.genColors.listBoxBorder + VERTICAL_SINGLE); + console.gotoxy(boxInfo.topLeftX+1, screenY); printf(listTextReplacements.listFormatStr, listTextReplacements.txtReplacementArr[i].originalText.substr(0, txtWidth), listTextReplacements.txtReplacementArr[i].replacement.substr(0, txtWidth)); - console.print("n" + gConfigSettings.genColors.listBoxBorder + VERTICAL_SINGLE); ++screenY; } // If the current screen row is below the bottom row inside the box, // continue and write blank lines to the bottom of the inside of the box // to blank out any text that might still be there. - //if (screenY < boxInfo.topLeftY+boxInfo.height) while (screenY < boxInfo.topLeftY+boxInfo.height-1) { - console.gotoxy(boxInfo.topLeftX, screenY); - console.print("n" + gConfigSettings.genColors.listBoxBorder + VERTICAL_SINGLE); + console.gotoxy(boxInfo.topLeftX+1, screenY); printf(listTextReplacements.listFormatStrNormalAttr, "", ""); - console.print("n" + gConfigSettings.genColors.listBoxBorder + VERTICAL_SINGLE); ++screenY; } @@ -5463,7 +5388,7 @@ function listTextReplacements() // Just for sane appearance: Move the cursor to the first character of // the first row and make it the color for the text replacements. console.gotoxy(boxInfo.topLeftX+1, boxInfo.topLeftY+1); - console.print(gConfigSettings.genColors.txtReplacementList); + console.print(gConfigSettings.genColors.listBoxItemText); } // Get a key from the user (upper-case) and take action based upon it. @@ -5507,6 +5432,228 @@ function listTextReplacements() console.print(chooseEditColor()); } +// Lets the user manage their preferences/settings. +// +// Parameters: +// pCurpos: The position of the cursor on the screen when this function is called. +// This is optional. If not specified, this function will call console.getxy() +// to get the cursor position. +// pReturnCursorToOriginalPos: Optional, boolean - Whether or not to return the cursor +// to its original position when done. +function doUserSettings(pCurpos, pReturnCursorToOriginalPos) +{ + if (!gConfigSettings.allowUserSettings) + return; + + const originalCurpos = (typeof(pCurpos) == "object" ? pCurpos : console.getxy()); + var returnCursorWhenDone = true; + if (typeof(pReturnCursorToOriginalPos) == "boolean") + returnCursorWhenDone = pReturnCursorToOriginalPos; + + // Save the user's current settings so that we can check them later to see if any + // of them changed, in order to determine whether to save the user's settings file. + var originalSettings = new Object(); + for (var prop in gUserSettings) + { + if (gUserSettings.hasOwnProperty(prop)) + originalSettings[prop] = gUserSettings[prop]; + } + + // Create the user settings box + var optBoxTitle = "Setting Enabled"; + var optBoxWidth = ChoiceScrollbox_MinWidth(); + var optBoxHeight = 5; + var optBoxStartX = gEditLeft + Math.floor((gEditWidth/2) - (optBoxWidth/2)); + if (optBoxStartX < gEditLeft) + optBoxStartX = gEditLeft; + var optionBox = new ChoiceScrollbox(optBoxStartX, gEditTop+1, optBoxWidth, optBoxHeight, optBoxTitle, + gConfigSettings, false, true); + optionBox.addInputLoopExitKey(CTRL_U); + // Update the bottom help text to be more specific to the user settings box + var bottomBorderText = "nhcb, cb, cEntery=bSelectnc/hbtoggle, " + + "cESCnc/hCtrl-Uy=bClose"; + // This one contains the page navigation keys.. Don't really need to show those, + // since the settings box only has one page right now: + /*var bottomBorderText = "nhcb, cb, cNy)bext, cPy)brev, " + + "cFy)birst, cLy)bast, cEntery=bSelectnc/hbtoggle, " + + "cCtrl-Uy=bClose";*/ + + optionBox.setBottomBorderText(bottomBorderText, true, false); + + // Add the options to the option box + const checkIdx = 48; + const TAGLINE_OPT_INDEX = optionBox.addTextItem("Taglines [ ]"); + const QUOTE_INITIALS_OPT_INDEX = optionBox.addTextItem("Quote with author's initials [ ]"); + const QUOTE_INITIALS_INDENT_OPT_INDEX = optionBox.addTextItem("Indent quote lines containing initials [ ]"); + if (gUserSettings.enableTaglines) + optionBox.chgCharInTextItem(TAGLINE_OPT_INDEX, checkIdx, CHECK_CHAR); + if (gUserSettings.useQuoteLineInitials) + optionBox.chgCharInTextItem(QUOTE_INITIALS_OPT_INDEX, checkIdx, CHECK_CHAR); + if (gUserSettings.indentQuoteLinesWithInitials) + optionBox.chgCharInTextItem(QUOTE_INITIALS_INDENT_OPT_INDEX, checkIdx, CHECK_CHAR); + + // Create an object containing toggle values (true/false) for each option index + var optionToggles = new Object(); + optionToggles[TAGLINE_OPT_INDEX] = gUserSettings.enableTaglines; + optionToggles[QUOTE_INITIALS_OPT_INDEX] = gUserSettings.useQuoteLineInitials; + optionToggles[QUOTE_INITIALS_INDENT_OPT_INDEX] = gUserSettings.indentQuoteLinesWithInitials; + + // Set up the enter key in the box to toggle the selected item. + optionBox.setEnterKeyOverrideFn(function(pBox) { + var itemIndex = pBox.getChosenTextItemIndex(); + if (itemIndex > -1) + { + // If there's an option for the chosen item, then update the text on the + // screen depending on whether the option is enabled or not. + if (optionToggles.hasOwnProperty(itemIndex)) + { + // Toggle the option and refresh it on the screen + optionToggles[itemIndex] = !optionToggles[itemIndex]; + if (optionToggles[itemIndex]) + optionBox.chgCharInTextItem(itemIndex, checkIdx, CHECK_CHAR); + else + optionBox.chgCharInTextItem(itemIndex, checkIdx, " "); + optionBox.refreshItemCharOnScreen(itemIndex, checkIdx); + + // Toggle the setting for the user in global user setting object. + switch (itemIndex) + { + case TAGLINE_OPT_INDEX: + gUserSettings.enableTaglines = !gUserSettings.enableTaglines; + break; + case QUOTE_INITIALS_OPT_INDEX: + gUserSettings.useQuoteLineInitials = !gUserSettings.useQuoteLineInitials; + break; + case QUOTE_INITIALS_INDENT_OPT_INDEX: + gUserSettings.indentQuoteLinesWithInitials = !gUserSettings.indentQuoteLinesWithInitials; + break; + default: + break; + } + } + } + }); // Option box enter key override function + + // Display the option box and have it do its input loop + var boxRetObj = optionBox.doInputLoop(true); + + // If the user changed any of their settings, then save the user settings. + // If the save fails, then output an error message. + var settingsChanged = false; + for (var prop in gUserSettings) + { + if (gUserSettings.hasOwnProperty(prop)) + { + settingsChanged = (originalSettings[prop] != gUserSettings[prop]); + if (settingsChanged) + break; + } + } + if (settingsChanged) + { + if (!WriteUserSettingsFile(gUserSettings)) + writeMsgOntBtmHelpLineWithPause("nyhFailed to save settings!n", ERRORMSG_PAUSE_MS); + } + + // We're done, so erase the option box. + var editLineIndexAtSelBoxTopRow = gEditLinesIndex - (originalCurpos.y-optionBox.dimensions.topLeftY); + displayMessageRectangle(optionBox.dimensions.topLeftX, optionBox.dimensions.topLeftY, + optionBox.dimensions.width, optionBox.dimensions.height, + editLineIndexAtSelBoxTopRow, true); + + if (returnCursorWhenDone) + console.gotoxy(originalCurpos); +} + +// Allows the user to select a tagline. Returns an object with the following +// properties: +// taglineWasSelected: Boolean - Whether or not a tag line was selected +// tagline: String - The tag line that was selected +function doTaglineSelection() +{ + var retObj = new Object(); + retObj.taglineWasSelected = false; + retObj.tagline = ""; + + // Read the tagline file + var taglines = readTxtFileIntoArray(gConfigSettings.tagLineFilename, true, true, 5000); + if (taglines.length == 0) + return; + + // Create the list box for the taglines. Make the box up to 14 lines tall. + var boxHeight = (taglines.length > 12 ? 14 : taglines.length+2); + var boxTopRow = gEditTop + Math.floor((gEditHeight/2) - (boxHeight/2)); + var taglineBox = new ChoiceScrollbox(gEditLeft, boxTopRow, gEditWidth, boxHeight, + "Taglines", gConfigSettings, true, false); + var bottomBorderText = "nhcb, cb, cNy)bext, cPy)brev, " + + "cFy)birst, cLy)bast, cHOMEb, cENDb, cEntery=bSelect, " + + "cRy)bandom, cESCnc/hcQy=bEnd"; + taglineBox.setBottomBorderText(bottomBorderText, false, false); + // Add R as an input loop exit key, to choose a random tagline. + taglineBox.addInputLoopExitKey("R"); + taglineBox.addInputLoopExitKey("r"); + + // Set the tagline item array in the list box. Don't strip control characters + // because we've already done that when we read the file. + taglineBox.setItemArray(taglines, false); + // Let the user choose a tagline + var taglineRetObj = taglineBox.doInputLoop(true); + retObj.taglineWasSelected = taglineRetObj.itemWasSelected; + if (retObj.taglineWasSelected) + retObj.tagline = taglineRetObj.selectedItem; + // If the R key was pressed, then choose a random tagline. + else if ((taglineRetObj.lastKeypress == "R") || (taglineRetObj.lastKeypress == "r")) + { + retObj.tagline = taglines[random(taglines.length)]; + retObj.taglineWasSelected = true; + } + + return retObj; +} + +// Sets gQuotePrefix, the text to use for prefixing quote lines. +function setQuotePrefix() +{ + gQuotePrefix = " > "; // The default + // If we're configured to use poster's initials in the + // quote lines, then do it. + if (gUserSettings.useQuoteLineInitials) + { + // For the name to use for quote line initials: + // If posting in a message sub-board, get the author's name from the + // header of the current message being read in the sub-board (in + // case the user changes the "To" name). Otherwise (if not posting in + // a message sub-board), use the gToName value read from the drop file. + // Remove any leading, multiple, or trailing spaces. + var quotedName = ""; + if (postingInMsgSubBoard(gMsgArea)) + { + quotedName = trimSpaces(getFromNameForCurMsg(gMsgAreaInfo), true, true, true); + if (quotedName.length == 0) + quotedName = trimSpaces(gToName, true, true, true); + } + else + quotedName = trimSpaces(gToName, true, true, true); + // If configured to indent quote lines w/ initials with + // a space, then do it. + gQuotePrefix = ""; + if (gUserSettings.indentQuoteLinesWithInitials) + gQuotePrefix = " "; + // Use the initials or first 2 characters from the + // quoted name for gQuotePrefix. + var spaceIndex = quotedName.indexOf(" "); + if (spaceIndex > -1) // If a space exists, use the initials + { + gQuotePrefix += quotedName.charAt(0).toUpperCase(); + if (quotedName.length > spaceIndex+1) + gQuotePrefix += quotedName.charAt(spaceIndex+1).toUpperCase(); + gQuotePrefix += "> "; + } + else // A space doesn't exist; use the first 2 letters + gQuotePrefix += quotedName.substr(0, 2) + "> "; + } +} + // Writes some text over the bottom help line, with a pause before erasing the // text and refreshing the bottom help line. // diff --git a/exec/SlyEdit_DCTStuff.js b/exec/SlyEdit_DCTStuff.js index ce02c11551..0fb324b3b8 100644 --- a/exec/SlyEdit_DCTStuff.js +++ b/exec/SlyEdit_DCTStuff.js @@ -46,6 +46,18 @@ * SlyEdit_Misc.js) to move the general colors * into the genColors array in the configuration * object. + * 2013-09-14 Eric Oulashin Updated doDCTMenu() and related functions with + * an option for user settings. Also fixed the bug + * related to the CTRL key for listing text replacements + * not working. Simplified the code in the function + * valMatchesMenuCode(). + * 2013-09-15 Eric Oulashin Bug fix: Updated displayTime_DCTStyle() to + * properly calculate the horizontal position at + * which to write the time rather than going to + * absolute coordinates; this accommodates terminals + * of different widths. + * 2013-09-16 Eric Oulashin Fixed off-by-one bug for the horizontal position in + * Updated updateInsertModeOnScreen_DCTStyle(). */ load("sbbsdefs.js"); @@ -58,7 +70,7 @@ var DCTMENU_FILE_EDIT = 2; var DCTMENU_EDIT_INSERT_TOGGLE = 3; var DCTMENU_EDIT_FIND_TEXT = 4; //var DCTMENU_EDIT_SPELL_CHECKER = 5; -//var DCTMENU_EDIT_SETUP = 6; +var DCTMENU_EDIT_SETTINGS = 6; var DCTMENU_SYSOP_IMPORT_FILE = 7; var DCTMENU_SYSOP_EXPORT_FILE = 11; var DCTMENU_HELP_COMMAND_LIST = 8; @@ -195,7 +207,7 @@ function redrawScreen_DCTStyle(pEditLeft, pEditRight, pEditTop, pEditBottom, pEd fieldWidth = (console.screen_columns * (7/80)).toFixed(0); startX = console.screen_columns - fieldWidth - 9; console.gotoxy(startX, lineNum); - var timeStr = Math.floor(bbs.time_left / 60).toString(); + var timeStr = Math.floor(bbs.time_left / 60).toString().substr(0, fieldWidth); console.print(gConfigSettings.DCTColors.TopLabelColor + "Left" + gConfigSettings.DCTColors.TopLabelColonColor + ": " + gConfigSettings.DCTColors.TopInfoBracketColor + "[" + @@ -413,7 +425,13 @@ function DisplayBottomHelpLine_DCTStyle(pLineNum, pUsingQuotes) // pInsertMode: The insert mode ("INS" or "OVR") function updateInsertModeOnScreen_DCTStyle(pEditRight, pEditBottom, pInsertMode) { - console.gotoxy(pEditRight-6, pEditBottom+1); + // If the number of columns on the screen is more than 80, the horizontal + // position will be 1 more to the right due to the added vertical lines + // around the edit area. + if (console.screen_columns > 80) + console.gotoxy(pEditRight-5, pEditBottom+1); + else + console.gotoxy(pEditRight-6, pEditBottom+1); console.print(gConfigSettings.DCTColors.EditModeBrackets + "[" + gConfigSettings.DCTColors.EditMode + pInsertMode + gConfigSettings.DCTColors.EditModeBrackets + "]"); @@ -642,11 +660,32 @@ function promptYesNo_DCTStyle(pQuestion, pBoxTitle, pDefaultYes, pParamObj) // then this funtion will get the current time. function displayTime_DCTStyle(pTimeStr) { - console.gotoxy(52, 3); + // Calculate the horizontal location for the time string, using basically + // the formula used for calculating the horizontal location of the message + // area in redrawScreen_DCTStyle(), which the time lines up with. + var fieldWidth = (console.screen_columns * (27/80)).toFixed(0); + //var curposX = console.screen_columns - fieldWidth - 9 + 8; + var curposX = console.screen_columns - fieldWidth - 1; + console.gotoxy(curposX, 3); + if (pTimeStr == null) - console.print(gConfigSettings.DCTColors.TopTimeColor + getCurrentTimeStr()); + console.print("n" + gConfigSettings.DCTColors.TopTimeColor + getCurrentTimeStr()); else - console.print(gConfigSettings.DCTColors.TopTimeColor + pTimeStr); + console.print("n" + gConfigSettings.DCTColors.TopTimeColor + pTimeStr); +} + +// Displays the number of minutes remaining on the screen. +function displayTimeRemaining_DCTStyle() +{ + var fieldWidth = (console.screen_columns * (7/80)).toFixed(0); + var startX = console.screen_columns - fieldWidth - 1; + console.gotoxy(startX, 3); + var timeStr = Math.floor(bbs.time_left / 60).toString().substr(0, fieldWidth); + console.print(gConfigSettings.DCTColors.TopTimeLeftColor + timeStr + + gConfigSettings.DCTColors.TopTimeLeftFillColor); + fieldWidth -= (timeStr.length+1); + for (var i = 0; i < fieldWidth; ++i) + console.print(DOT_CHAR); } // Displays & handles the input loop for the DCT Edit menu. @@ -783,7 +822,6 @@ function doDCTMenu(pEditLeft, pEditRight, pEditTop, pDisplayMessageRectangle, // Set up the menu objects. Only create these objects once. if (typeof(doDCTMenu.allMenus) == "undefined") { - //function displayDebugText(pDebugX, pDebugY, pText, pOriginalPos, pClearDebugLineFirst, pPauseAfter) doDCTMenu.allMenus = new Array(); // File menu doDCTMenu.allMenus[fileMenuNum] = new DCTMenu(doDCTMenu.mainMenuItemPositions.fileX, doDCTMenu.mainMenuItemPositions.mainMenuY+1); @@ -803,9 +841,10 @@ function doDCTMenu(pEditLeft, pEditRight, pEditTop, pDisplayMessageRectangle, doDCTMenu.allMenus[editMenuNum].addItem("&Insert Mode Ctrl-I", DCTMENU_EDIT_INSERT_TOGGLE); doDCTMenu.allMenus[editMenuNum].addItem("&Find Text Ctrl-N", DCTMENU_EDIT_FIND_TEXT); //doDCTMenu.allMenus[editMenuNum].addItem("Spell &Checker Ctrl-W", DCTMENU_EDIT_SPELL_CHECKER); - //doDCTMenu.allMenus[editMenuNum].addItem("&Setup Ctrl-U", DCTMENU_EDIT_SETUP); + doDCTMenu.allMenus[editMenuNum].addItem("Setti&ngs Ctrl-U", DCTMENU_EDIT_SETTINGS); doDCTMenu.allMenus[editMenuNum].addExitLoopKey(CTRL_I, DCTMENU_EDIT_INSERT_TOGGLE); doDCTMenu.allMenus[editMenuNum].addExitLoopKey(CTRL_N, DCTMENU_EDIT_FIND_TEXT); + doDCTMenu.allMenus[editMenuNum].addExitLoopKey(CTRL_U, DCTMENU_EDIT_SETTINGS); // SysOp menu doDCTMenu.allMenus[sysopMenuNum] = new DCTMenu(doDCTMenu.mainMenuItemPositions.sysopX, doDCTMenu.mainMenuItemPositions.mainMenuY+1); @@ -821,12 +860,8 @@ function doDCTMenu(pEditLeft, pEditRight, pEditTop, pDisplayMessageRectangle, doDCTMenu.allMenus[helpMenuNum].addItem("&Program Info Ctrl-R", DCTMENU_HELP_PROGRAM_INFO); if (gConfigSettings.enableTextReplacements) { - // For some reason, Ctrl-T isn't working properly in this context - It - // exits the help menu but doesn't exit the menu overall. So for now, - // I'm not showing or allowing Ctrl-T in this context. - //doDCTMenu.allMenus[helpMenuNum].addItem("&Text replcmts Ctrl-T", DCTMENU_LIST_TXT_REPLACEMENTS); - //doDCTMenu.allMenus[helpMenuNum].addExitLoopKey(CTRL_T, DCTMENU_LIST_TXT_REPLACEMENTS); - doDCTMenu.allMenus[helpMenuNum].addItem("&Text replcmts ", DCTMENU_LIST_TXT_REPLACEMENTS); + doDCTMenu.allMenus[helpMenuNum].addItem("&Text replcmts Ctrl-T", DCTMENU_LIST_TXT_REPLACEMENTS); + doDCTMenu.allMenus[helpMenuNum].addExitLoopKey(CTRL_T, DCTMENU_LIST_TXT_REPLACEMENTS); } doDCTMenu.allMenus[helpMenuNum].addExitLoopKey(CTRL_P, DCTMENU_HELP_COMMAND_LIST); doDCTMenu.allMenus[helpMenuNum].addExitLoopKey(CTRL_G, DCTMENU_HELP_GENERAL); @@ -926,6 +961,8 @@ function doDCTMenu(pEditLeft, pEditRight, pEditTop, pDisplayMessageRectangle, case CTRL_V: // Insert/overwrite toggle case "F": // Find text case CTRL_F: // Find text + case "N": // User settings + case CTRL_U: // User settings case "O": // Command List case "G": // General help case "P": // Program info @@ -939,7 +976,7 @@ function doDCTMenu(pEditLeft, pEditRight, pEditTop, pDisplayMessageRectangle, continueOn = false; break; case "T": // List text replacements - //case CTRL_T: // List text replacements + case CTRL_T: // List text replacements if (gConfigSettings.enableTextReplacements) continueOn = false; break; @@ -967,23 +1004,15 @@ function doDCTMenu(pEditLeft, pEditRight, pEditTop, pDisplayMessageRectangle, function valMatchesMenuCode(pVal, pIsSysop) { var valMatches = false; - if (pIsSysop) - { - valMatches = ((pVal == DCTMENU_FILE_SAVE) || (pVal == DCTMENU_FILE_ABORT) || - (pVal == DCTMENU_FILE_EDIT) || (pVal == DCTMENU_EDIT_INSERT_TOGGLE) || - (pVal == DCTMENU_EDIT_FIND_TEXT) || (pVal == DCTMENU_SYSOP_IMPORT_FILE) || - (pVal == DCTMENU_SYSOP_EXPORT_FILE) || (pVal == DCTMENU_HELP_COMMAND_LIST) || - (pVal == DCTMENU_HELP_GENERAL) || (pVal == DCTMENU_HELP_PROGRAM_INFO)); - } - else - { - valMatches = ((pVal == DCTMENU_FILE_SAVE) || (pVal == DCTMENU_FILE_ABORT) || - (pVal == DCTMENU_FILE_EDIT) || (pVal == DCTMENU_EDIT_INSERT_TOGGLE) || - (pVal == DCTMENU_EDIT_FIND_TEXT) || (pVal == DCTMENU_HELP_COMMAND_LIST) || - (pVal == DCTMENU_HELP_GENERAL) || (pVal == DCTMENU_HELP_PROGRAM_INFO)); - } + valMatches = ((pVal == DCTMENU_FILE_SAVE) || (pVal == DCTMENU_FILE_ABORT) || + (pVal == DCTMENU_FILE_EDIT) || (pVal == DCTMENU_EDIT_INSERT_TOGGLE) || + (pVal == DCTMENU_EDIT_FIND_TEXT) || (pVal == DCTMENU_HELP_COMMAND_LIST) || + (pVal == DCTMENU_HELP_GENERAL) || (pVal == DCTMENU_HELP_PROGRAM_INFO) || + (pVal == DCTMENU_EDIT_SETTINGS)); if (gConfigSettings.enableTextReplacements) - valMatch = (valMatches || DCTMENU_LIST_TXT_REPLACEMENTS); + valMatches = (valMatches || (pVal == DCTMENU_LIST_TXT_REPLACEMENTS)); + if (pIsSysop) + valMatches = (valMatches || (pVal == DCTMENU_SYSOP_IMPORT_FILE) || (pVal == DCTMENU_SYSOP_EXPORT_FILE)); return valMatches; } @@ -1140,7 +1169,7 @@ function DCTMenu_AddItem(pText, pReturnVal) function DCTMenu_AddExitLoopKey(pKey, pReturnValue) { var val = -1; - if ((pReturnValue != null) && (typeof(pReturnValue) != "undefined")) + if (typeof(pReturnValue) == "number") val = pReturnValue; this.exitLoopKeys[pKey] = val; diff --git a/exec/SlyEdit_IceStuff.js b/exec/SlyEdit_IceStuff.js index d3c9d014ab..4c0893455e 100644 --- a/exec/SlyEdit_IceStuff.js +++ b/exec/SlyEdit_IceStuff.js @@ -32,6 +32,16 @@ * SlyEdit_Misc.js) to move the general colors * into the genColors array in the configuration * object. + * 2013-09-14 Eric Oulashin Added a settings option to doIceESCMenu(). + * 2013-09-15 Eric Oulashin Bug fix: Updated displayTime_IceStyle() to + * properly calculate the horizontal position at + * which to write the time rather than going to + * absolute coordinates; this accommodates terminals + * of different widths. Also, in redrawScreen_IceStyle(), + * increased the maximum message area name length to + * 35 characters. + * 2013-09-16 Eric Oulashin Added displayTimeRemaining_IceStyle(), which updates + * the time remaining on the screen. */ load("sbbsdefs.js"); @@ -41,8 +51,9 @@ load(gStartupPath + "SlyEdit_Misc.js"); var ICE_ESC_MENU_SAVE = 0; var ICE_ESC_MENU_ABORT = 1; var ICE_ESC_MENU_EDIT = 2; -var ICE_ESC_MENU_HELP = 3; -var ICE_ESC_MENU_CROSS_POST = 4; +var ICE_ESC_MENU_SETTINGS = 3; +var ICE_ESC_MENU_HELP = 4; +var ICE_ESC_MENU_CROSS_POST = 5; // Read the color configuration file readColorConfig(gConfigSettings.iceColors.ThemeFilename); @@ -204,9 +215,10 @@ function redrawScreen_IceStyle(pEditLeft, pEditRight, pEditTop, pEditBottom, pEd + THIN_RECTANGLE_RIGHT; // The message area name should be centered on the line. So, based on its - // length (up to 20 characters), figure out its starting position before + // length (up to 35 characters), figure out its starting position before // printing it. - var msgAreaName = gMsgArea.substr(0, 20); + //var msgAreaName = gMsgArea.substr(0, 20); // Used to be 20 characters + var msgAreaName = gMsgArea.substr(0, 35); // 2 is subtracted from the starting position to leave room for the // block character and the space. var startPos = (console.screen_columns/2).toFixed(0) - (msgAreaName.length/2).toFixed(0) - 2; @@ -553,11 +565,24 @@ function promptYesNo_IceStyle(pQuestion, pDefaultYes) // then this funtion will get the current time. function displayTime_IceStyle(pTimeStr) { - console.gotoxy(73, 2); + console.gotoxy(console.screen_columns-7, 2); if (pTimeStr == null) - console.print(gConfigSettings.iceColors.TopInfoBkgColor + gConfigSettings.iceColors.TopTimeColor + getCurrentTimeStr()); + console.print("n" + gConfigSettings.iceColors.TopInfoBkgColor + gConfigSettings.iceColors.TopTimeColor + getCurrentTimeStr()); else - console.print(gConfigSettings.iceColors.TopInfoBkgColor + gConfigSettings.iceColors.TopTimeColor + pTimeStr); + console.print("n" + gConfigSettings.iceColors.TopInfoBkgColor + gConfigSettings.iceColors.TopTimeColor + pTimeStr); +} + +// Displays the number of minutes remaining on the screen. +function displayTimeRemaining_IceStyle() +{ + var fieldWidth = (console.screen_columns * (4/80)).toFixed(0); + var formatStr = "n" + gConfigSettings.iceColors.TopInfoBkgColor + + gConfigSettings.iceColors.TopTimeLeftColor + "%-" + + fieldWidth + "s"; + var startX = console.screen_columns - fieldWidth - 5; + console.gotoxy(startX, 3); + var timeStr = Math.floor(bbs.time_left / 60).toString().substr(0, fieldWidth); + printf(formatStr, timeStr); } // For IceEdit mode: This function takes a string and returns a copy @@ -673,6 +698,7 @@ function doIceESCMenu(pY, pCanCrossPost) console.print(iceStyledPromptText("Save", true) + "n "); console.print(iceStyledPromptText("Abort", false) + "n "); console.print(iceStyledPromptText("Edit", false) + "n "); + console.print(iceStyledPromptText("Settings", false) + "n "); console.print(iceStyledPromptText("Help", false)); if (pCanCrossPost) console.print("n " + iceStyledPromptText("Cross-post", false)); @@ -681,6 +707,7 @@ function doIceESCMenu(pY, pCanCrossPost) console.print(iceStyledPromptText("Save", false) + "n "); console.print(iceStyledPromptText("Abort", true) + "n "); console.print(iceStyledPromptText("Edit", false) + "n "); + console.print(iceStyledPromptText("Settings", false) + "n "); console.print(iceStyledPromptText("Help", false)); if (pCanCrossPost) console.print("n " + iceStyledPromptText("Cross-post", false)); @@ -689,6 +716,16 @@ function doIceESCMenu(pY, pCanCrossPost) console.print(iceStyledPromptText("Save", false) + "n "); console.print(iceStyledPromptText("Abort", false) + "n "); console.print(iceStyledPromptText("Edit", true) + "n "); + console.print(iceStyledPromptText("Settings", false) + "n "); + console.print(iceStyledPromptText("Help", false)); + if (pCanCrossPost) + console.print("n " + iceStyledPromptText("Cross-post", false)); + break; + case ICE_ESC_MENU_SETTINGS: + console.print(iceStyledPromptText("Save", false) + "n "); + console.print(iceStyledPromptText("Abort", false) + "n "); + console.print(iceStyledPromptText("Edit", false) + "n "); + console.print(iceStyledPromptText("Settings", true) + "n "); console.print(iceStyledPromptText("Help", false)); if (pCanCrossPost) console.print("n " + iceStyledPromptText("Cross-post", false)); @@ -697,6 +734,7 @@ function doIceESCMenu(pY, pCanCrossPost) console.print(iceStyledPromptText("Save", false) + "n "); console.print(iceStyledPromptText("Abort", false) + "n "); console.print(iceStyledPromptText("Edit", false) + "n "); + console.print(iceStyledPromptText("Settings", false) + "n "); console.print(iceStyledPromptText("Help", true)); if (pCanCrossPost) console.print("n " + iceStyledPromptText("Cross-post", false)); @@ -705,6 +743,7 @@ function doIceESCMenu(pY, pCanCrossPost) console.print(iceStyledPromptText("Save", false) + "n "); console.print(iceStyledPromptText("Abort", false) + "n "); console.print(iceStyledPromptText("Edit", false) + "n "); + console.print(iceStyledPromptText("Settings", false) + "n "); console.print(iceStyledPromptText("Help", false) + "n "); console.print(iceStyledPromptText("Cross-post", true)); break; diff --git a/exec/SlyEdit_Misc.js b/exec/SlyEdit_Misc.js index 8e536bc47a..7f4688c934 100644 --- a/exec/SlyEdit_Misc.js +++ b/exec/SlyEdit_Misc.js @@ -45,12 +45,21 @@ * replacement text when in literal match & replace * mode. * 2013-09-07 Eric Oulashin Bug fix: Updated ReadSlyEditConfigFile() to - * default cfgObj.genColors.txtReplacementList to + * default cfgObj.genColors.listBoxItemText to * ensure that it gets defined. * Code refactor: Moved doMacroTxtReplacementInEditLine() * and getWordFromEditLine() to TextLine member * methods TextLine_doMacroTxtReplacement() and * TextLine_getWord(). + * 2013-09-13 + * 2013-09-14 Eric Oulashin Added functions for a new ChoiceScrollbox object + * type, which is a generic scrollable list box that + * allows a user to select an item. + * Added the functions readTxtFileIntoArray() and + * txtFileContainsLines(). Updated displayCommandList() + * to display the hotkey Ctrl-U for user settings. + * Moved the options for author initials in quote + * lines to user settings. */ // Note: These variables are declared with "var" instead of "const" to avoid @@ -149,9 +158,14 @@ var KEY_ESC = "\x1b"; // Lister, since it will be used more than once. var gDDML_DROP_FILE_NAME = system.node_dir + "DDML_SyncSMBInfo.txt"; +var gUserSettingsFilename = backslash(system.data_dir + "user") + format("%04d", user.number) + ".SlyEdit_Settings"; + /////////////////////////////////////////////////////////////////////////////////// // Object/class stuff +////// +// TextLine stuff + // TextLine object constructor: This is used to keep track of a text line, // and whether it has a hard newline at the end (i.e., if the user pressed // enter to break the line). @@ -379,6 +393,708 @@ function AbortConfirmFuncParams() this.displayMessageRectangle = displayMessageRectangle; } +////// +// ChoiceScrollbox stuff + +// Returns the minimum width for a ChoiceScrollbox +function ChoiceScrollbox_MinWidth() +{ + return 73; // To leave room for the navigation text in the bottom border +} + +// ChoiceScrollbox constructor +// +// Parameters: +// pLeftX: The horizontal component (column) of the upper-left coordinate +// pTopY: The vertical component (row) of the upper-left coordinate +// pWidth: The width of the box (including the borders) +// pHeight: The height of the box (including the borders) +// pTopBorderText: The text to include in the top border +// pSlyEdCfgObj: The SlyEdit configuration object (color settings are used) +// pAddTCharsAroundTopText: Optional, boolean - Whether or not to use left & right T characters +// around the top border text. Defaults to true. +// pReplaceTopTextSpacesWithBorderChars: Optional, boolean - Whether or not to replace +// spaces in the top border text with border characters. +// Defaults to false. +function ChoiceScrollbox(pLeftX, pTopY, pWidth, pHeight, pTopBorderText, pSlyEdCfgObj, + pAddTCharsAroundTopText, pReplaceTopTextSpacesWithBorderChars) +{ + // The default is to add left & right T characters around the top border + // text. But also use pAddTCharsAroundTopText if it's a boolean. + var addTopTCharsAroundText = true; + if (typeof(pAddTCharsAroundTopText) == "boolean") + addTopTCharsAroundText = pAddTCharsAroundTopText; + // If pReplaceTopTextSpacesWithBorderChars is true, then replace the spaces + // in pTopBorderText with border characters. + if (pReplaceTopTextSpacesWithBorderChars) + { + var startIdx = 0; + var firstSpcIdx = pTopBorderText.indexOf(" ", 0); + // Look for the first non-space after firstSpaceIdx + var nonSpcIdx = -1; + for (var i = firstSpcIdx; (i < pTopBorderText.length) && (nonSpcIdx == -1); ++i) + { + if (pTopBorderText.charAt(i) != " ") + nonSpcIdx = i; + } + var firstStrPart = ""; + var lastStrPart = ""; + numSpaces = 0; + while ((firstSpcIdx > -1) && (nonSpcIdx > -1)) + { + firstStrPart = pTopBorderText.substr(startIdx, (firstSpcIdx-startIdx)); + lastStrPart = pTopBorderText.substr(nonSpcIdx); + numSpaces = nonSpcIdx - firstSpcIdx; + if (numSpaces > 0) + { + pTopBorderText = firstStrPart + "n" + pSlyEdCfgObj.genColors.listBoxBorder; + for (var i = 0; i < numSpaces; ++i) + pTopBorderText += HORIZONTAL_SINGLE; + pTopBorderText += "n" + pSlyEdCfgObj.genColors.listBoxBorderText + lastStrPart; + } + + // Look for the next space and non-space character after that. + firstSpcIdx = pTopBorderText.indexOf(" ", nonSpcIdx); + // Look for the first non-space after firstSpaceIdx + nonSpcIdx = -1; + for (var i = firstSpcIdx; (i < pTopBorderText.length) && (nonSpcIdx == -1); ++i) + { + if (pTopBorderText.charAt(i) != " ") + nonSpcIdx = i; + } + } + } + + this.SlyEdCfgObj = pSlyEdCfgObj; + + var minWidth = ChoiceScrollbox_MinWidth(); + + this.dimensions = new Object(); + this.dimensions.topLeftX = pLeftX; + this.dimensions.topLeftY = pTopY; + // Make sure the width is the minimum width + if ((pWidth < 0) || (pWidth < minWidth)) + this.dimensions.width = minWidth; + else + this.dimensions.width = pWidth; + this.dimensions.height = pHeight; + this.dimensions.bottomRightX = this.dimensions.topLeftX + this.dimensions.width - 1; + this.dimensions.bottomRightY = this.dimensions.topLeftY + this.dimensions.height - 1; + + // The text item array and member variables relating to it and the items + // displayed on the screen during the input loop + this.txtItemList = new Array(); + this.chosenTextItemIndex = -1; + this.topItemIndex = 0; + this.bottomItemIndex = 0; + + // Top border string + var innerBorderWidth = this.dimensions.width - 2; + // Calculate the maximum top border text length to account for the left/right + // T chars and "Page #### of ####" text + var maxTopBorderTextLen = innerBorderWidth - (pAddTCharsAroundTopText ? 21 : 19); + if (strip_ctrl(pTopBorderText).length > maxTopBorderTextLen) + pTopBorderText = pTopBorderText.substr(0, maxTopBorderTextLen); + this.topBorder = "n" + pSlyEdCfgObj.genColors.listBoxBorder + UPPER_LEFT_SINGLE; + if (addTopTCharsAroundText) + this.topBorder += RIGHT_T_SINGLE; + this.topBorder += "n" + pSlyEdCfgObj.genColors.listBoxBorderText + + pTopBorderText + "n" + pSlyEdCfgObj.genColors.listBoxBorder; + if (addTopTCharsAroundText) + this.topBorder += LEFT_T_SINGLE; + const topBorderTextLen = strip_ctrl(pTopBorderText).length; + var numHorizBorderChars = innerBorderWidth - topBorderTextLen - 20; + if (addTopTCharsAroundText) + numHorizBorderChars -= 2; + for (var i = 0; i <= numHorizBorderChars; ++i) + this.topBorder += HORIZONTAL_SINGLE; + this.topBorder += RIGHT_T_SINGLE + "n" + pSlyEdCfgObj.genColors.listBoxBorderText + + "Page 1 of 1" + "n" + pSlyEdCfgObj.genColors.listBoxBorder + LEFT_T_SINGLE + + UPPER_RIGHT_SINGLE; + + // Bottom border string + this.btmBorderNavText = "nhcb, cb, cNy)bext, cPy)brev, " + + "cFy)birst, cLy)bast, cHOMEb, cENDb, cEntery=bSelect, " + + "cESCnc/hcQy=bEnd"; + this.bottomBorder = "n" + pSlyEdCfgObj.genColors.listBoxBorder + LOWER_LEFT_SINGLE + + RIGHT_T_SINGLE + this.btmBorderNavText + "n" + pSlyEdCfgObj.genColors.listBoxBorder + + LEFT_T_SINGLE; + var numCharsRemaining = this.dimensions.width - strip_ctrl(this.btmBorderNavText).length - 6; + for (var i = 0; i < numCharsRemaining; ++i) + this.bottomBorder += HORIZONTAL_SINGLE; + this.bottomBorder += LOWER_RIGHT_SINGLE; + + // Item format strings + this.listIemFormatStr = "n" + pSlyEdCfgObj.genColors.listBoxItemText + "%-" + + +(this.dimensions.width-2) + "s"; + this.listIemHighlightFormatStr = "n" + pSlyEdCfgObj.genColors.listBoxItemHighlight + "%-" + + +(this.dimensions.width-2) + "s"; + + // Key functionality override function pointers + this.enterKeyOverrideFn = null; + + // inputLoopeExitKeys is an object containing additional keypresses that will + // exit the input loop. + this.inputLoopExitKeys = new Object(); + + // "Class" functions + this.addTextItem = ChoiceScrollbox_AddTextItem; // Returns the index of the item + this.getTextItem = ChoiceScrollbox_GetTextIem; + this.replaceTextItem = ChoiceScrollbox_ReplaceTextItem; + this.delTextItem = ChoiceScrollbox_DelTextItem; + this.chgCharInTextItem = ChoiceScrollbox_ChgCharInTextItem; + this.getChosenTextItemIndex = ChoiceScrollbox_GetChosenTextItemIndex; + this.setItemArray = ChoiceScrollbox_SetItemArray; // Sets the item array; returns whether or not it was set. + this.clearItems = ChoiceScrollbox_ClearItems; // Empties the array of items + this.setEnterKeyOverrideFn = ChoiceScrollbox_SetEnterKeyOverrideFn; + this.clearEnterKeyOverrideFn = ChoiceScrollbox_ClearEnterKeyOverrideFn; + this.addInputLoopExitKey = ChoiceScrollbox_AddInputLoopExitKey; + this.setBottomBorderText = ChoiceScrollbox_SetBottomBorderText; + this.drawBorder = ChoiceScrollbox_DrawBorder; + this.refreshItemCharOnScreen = ChoiceScrollbox_RefreshItemCharOnScreen; + // Does the input loop. Returns an object with the following properties: + // itemWasSelected: Boolean - Whether or not an item was selected + // selectedIndex: The index of the selected item + // selectedItem: The text of the selected item + // lastKeypress: The last key pressed by the user + this.doInputLoop = ChoiceScrollbox_DoInputLoop; +} +function ChoiceScrollbox_AddTextItem(pTextLine, pStripCtrl) +{ + var stripCtrl = true; + if (typeof(pStripCtrl) == "boolean") + stripCtrl = pStripCtrl; + + if (stripCtrl) + this.txtItemList.push(strip_ctrl(pTextLine)); + else + this.txtItemList.push(pTextLine); + // Return the index of the added item + return this.txtItemList.length-1; +} +function ChoiceScrollbox_GetTextIem(pItemIndex) +{ + if (typeof(pItemIndex) != "number") + return ""; + if ((pItemIndex < 0) || (pItemIndex >= this.txtItemList.length)) + return ""; + + return this.txtItemList[pItemIndex]; +} +function ChoiceScrollbox_ReplaceTextItem(pItemIndexOrStr, pNewItem) +{ + if (typeof(pNewItem) != "string") + return false; + + // Find the item index + var itemIndex = -1; + if (typeof(pItemIndexOrStr) == "number") + { + if ((pItemIndexOrStr < 0) || (pItemIndexOrStr >= this.txtItemList.length)) + return false; + else + itemIndex = pItemIndexOrStr; + } + else if (typeof(pItemIndexOrStr) == "string") + { + itemIndex = -1; + for (var i = 0; (i < this.txtItemList.length) && (itemIndex == -1); ++i) + { + if (this.txtItemList[i] == pItemIndexOrStr) + itemIndex = i; + } + } + else + return false; + + // Replace the item + var replacedIt = false; + if ((itemIndex > -1) && (itemIndex < this.txtItemList.length)) + { + this.txtItemList[itemIndex] = pNewItem; + replacedIt = true; + } + return replacedIt; +} +function ChoiceScrollbox_DelTextItem(pItemIndexOrStr) +{ + // Find the item index + var itemIndex = -1; + if (typeof(pItemIndexOrStr) == "number") + { + if ((pItemIndexOrStr < 0) || (pItemIndexOrStr >= this.txtItemList.length)) + return false; + else + itemIndex = pItemIndexOrStr; + } + else if (typeof(pItemIndexOrStr) == "string") + { + itemIndex = -1; + for (var i = 0; (i < this.txtItemList.length) && (itemIndex == -1); ++i) + { + if (this.txtItemList[i] == pItemIndexOrStr) + itemIndex = i; + } + } + else + return false; + + // Remove the item + var removedIt = false; + if ((itemIndex > -1) && (itemIndex < this.txtItemList.length)) + { + this.txtItemList = this.txtItemList.splice(itemIndex, 1); + removedIt = true; + } + return removedIt; +} +function ChoiceScrollbox_ChgCharInTextItem(pItemIndexOrStr, pStrIndex, pNewText) +{ + // Find the item index + var itemIndex = -1; + if (typeof(pItemIndexOrStr) == "number") + { + if ((pItemIndexOrStr < 0) || (pItemIndexOrStr >= this.txtItemList.length)) + return false; + else + itemIndex = pItemIndexOrStr; + } + else if (typeof(pItemIndexOrStr) == "string") + { + itemIndex = -1; + for (var i = 0; (i < this.txtItemList.length) && (itemIndex == -1); ++i) + { + if (this.txtItemList[i] == pItemIndexOrStr) + itemIndex = i; + } + } + else + return false; + + // Change the character in the item + var changedIt = false; + if ((itemIndex > -1) && (itemIndex < this.txtItemList.length)) + { + this.txtItemList[itemIndex] = chgCharInStr(this.txtItemList[itemIndex], pStrIndex, pNewText); + changedIt = true; + } + return changedIt; +} +function ChoiceScrollbox_GetChosenTextItemIndex() +{ + return this.chosenTextItemIndex; +} +function ChoiceScrollbox_SetItemArray(pArray, pStripCtrl) +{ + var safeToSet = false; + if (Object.prototype.toString.call(pArray) === "[object Array]") + { + if (pArray.length > 0) + safeToSet = (typeof(pArray[0]) == "string"); + else + safeToSet = true; // It's safe to set an empty array + } + + if (safeToSet) + { + delete this.txtItemList; + this.txtItemList = pArray; + + var stripCtrl = true; + if (typeof(pStripCtrl) == "boolean") + stripCtrl = pStripCtrl; + if (stripCtrl) + { + // Remove attribute/color characters from the text lines in the array + for (var i = 0; i < this.txtItemList.length; ++i) + this.txtItemList[i] = strip_ctrl(this.txtItemList[i]); + } + } + + return safeToSet; +} +function ChoiceScrollbox_ClearItems() +{ + this.txtItemList.length = 0; +} +function ChoiceScrollbox_SetEnterKeyOverrideFn(pOverrideFn) +{ + if (Object.prototype.toString.call(pOverrideFn) == "[object Function]") + this.enterKeyOverrideFn = pOverrideFn; +} +function ChoiceScrollbox_ClearEnterKeyOverrideFn() +{ + this.enterKeyOverrideFn = null; +} +function ChoiceScrollbox_AddInputLoopExitKey(pKeypress) +{ + this.inputLoopExitKeys[pKeypress] = true; +} +function ChoiceScrollbox_SetBottomBorderText(pText, pAddTChars, pAutoStripIfTooLong) +{ + if (typeof(pText) != "string") + return; + + const innerWidth = (pAddTChars ? this.dimensions.width-4 : this.dimensions.width-2); + + if (pAutoStripIfTooLong) + { + if (strip_ctrl(pText).length > innerWidth) + pText = pText.substr(0, innerWidth); + } + + // Re-build the bottom border string based on the new text + this.bottomBorder = "n" + this.SlyEdCfgObj.genColors.listBoxBorder + LOWER_LEFT_SINGLE; + if (pAddTChars) + this.bottomBorder += RIGHT_T_SINGLE; + if (pText.indexOf("n") != 0) + this.bottomBorder += "n"; + this.bottomBorder += pText + "n" + this.SlyEdCfgObj.genColors.listBoxBorder; + if (pAddTChars) + this.bottomBorder += LEFT_T_SINGLE; + var numCharsRemaining = this.dimensions.width - strip_ctrl(this.bottomBorder).length - 3; + for (var i = 0; i < numCharsRemaining; ++i) + this.bottomBorder += HORIZONTAL_SINGLE; + this.bottomBorder += LOWER_RIGHT_SINGLE; +} +function ChoiceScrollbox_DrawBorder() +{ + console.gotoxy(this.dimensions.topLeftX, this.dimensions.topLeftY); + console.print(this.topBorder); + // Draw the side border characters + var screenRow = this.dimensions.topLeftY + 1; + for (var screenRow = this.dimensions.topLeftY+1; screenRow <= this.dimensions.bottomRightY-1; ++screenRow) + { + console.gotoxy(this.dimensions.topLeftX, screenRow); + console.print(VERTICAL_SINGLE); + console.gotoxy(this.dimensions.bottomRightX, screenRow); + console.print(VERTICAL_SINGLE); + } + // Draw the bottom border + console.gotoxy(this.dimensions.topLeftX, this.dimensions.bottomRightY); + console.print(this.bottomBorder); +} +function ChoiceScrollbox_RefreshItemCharOnScreen(pItemIndex, pCharIndex) +{ + if ((typeof(pItemIndex) != "number") || (typeof(pCharIndex) != "number")) + return; + if ((pItemIndex < 0) || (pItemIndex >= this.txtItemList.length) || + (pItemIndex < this.topItemIndex) || (pItemIndex > this.bottomItemIndex)) + { + return; + } + if ((pCharIndex < 0) || (pCharIndex >= this.txtItemList[pItemIndex].length)) + return; + + // Save the current cursor position so that we can restore it later + const originalCurpos = console.getxy(); + // Go to the character's position on the screen and set the highlight or + // normal color, depending on whether the item is the currently selected item, + // then print the character on the screen. + const charScreenX = this.dimensions.topLeftX + 1 + pCharIndex; + const itemScreenY = this.dimensions.topLeftY + 1 + (pItemIndex - this.topItemIndex); + console.gotoxy(charScreenX, itemScreenY); + if (pItemIndex == this.chosenTextItemIndex) + console.print(this.SlyEdCfgObj.genColors.listBoxItemHighlight); + else + console.print(this.SlyEdCfgObj.genColors.listBoxItemText); + console.print(this.txtItemList[pItemIndex].charAt(pCharIndex)); + // Move the cursor back to where it was originally + console.gotoxy(originalCurpos); +} +function ChoiceScrollbox_DoInputLoop(pDrawBorder) +{ + var retObj = new Object(); + retObj.itemWasSelected = false; + retObj.selectedIndex = -1; + retObj.selectedItem = ""; + retObj.lastKeypress = ""; + + // Don't do anything if the item list doesn't contain any items + if (this.txtItemList.length == 0) + return retObj; + + ////////////////////////////////// + // Locally-defined functions + + // This function returns the index of the bottommost item that + // can be displayed in the box. + // + // Parameters: + // pArray: The array containing the items + // pTopindex: The index of the topmost item displayed in the box + // pNumItemsPerPage: The number of items per page + function getBottommostItemIndex(pArray, pTopIndex, pNumItemsPerPage) + { + var bottomIndex = pTopIndex + pNumItemsPerPage - 1; + // If bottomIndex is beyond the last index, then adjust it. + if (bottomIndex >= pArray.length) + bottomIndex = pArray.length - 1; + return bottomIndex; + } + + + + ////////////////////////////////// + // Code + + // Variables for keeping track of the item list + const numItemsPerPage = this.dimensions.height - 2; + this.topItemIndex = 0; // The index of the message group at the top of the list + // Figure out the index of the last message group to appear on the screen. + this.bottomItemIndex = getBottommostItemIndex(this.txtItemList, this.topItemIndex, numItemsPerPage); + const numPages = Math.ceil(this.txtItemList.length / numItemsPerPage); + const topIndexForLastPage = (numItemsPerPage * numPages) - numItemsPerPage; + + if (pDrawBorder) + this.drawBorder(); + + // User input loop + // For the horizontal location of the page number text for the box border: + // Based on the fact that there can be up to 9999 text replacements and 10 + // per page, there will be up to 1000 pages of replacements. To write the + // text, we'll want to be 20 characters to the left of the end of the border + // of the box. + const pageNumTxtStartX = this.dimensions.topLeftX + this.dimensions.width - 19; + const maxItemWidth = this.dimensions.width - 2; + var pageNum = 0; + var startArrIndex = 0; + this.chosenTextItemIndex = retObj.selectedIndex = 0; + var endArrIndex = 0; // One past the last array item + var screenY = 0; + var curpos = new Object(); // For keeping track of the current cursor position + curpos.x = 0; + curpos.y = 0; + var refreshList = true; // For screen redraw optimizations + var continueOn = true; + while (continueOn) + { + if (refreshList) + { + this.bottomItemIndex = getBottommostItemIndex(this.txtItemList, this.topItemIndex, numItemsPerPage); + + // Write the list of items for the current page + startArrIndex = pageNum * numItemsPerPage; + endArrIndex = startArrIndex + numItemsPerPage; + if (endArrIndex > this.txtItemList.length) + endArrIndex = this.txtItemList.length; + var selectedItemRow = this.dimensions.topLeftY+1; + screenY = this.dimensions.topLeftY + 1; + for (var i = startArrIndex; i < endArrIndex; ++i) + { + console.gotoxy(this.dimensions.topLeftX+1, screenY); + if (i == retObj.selectedIndex) + { + printf(this.listIemHighlightFormatStr, this.txtItemList[i].substr(0, maxItemWidth)); + selectedItemRow = screenY; + } + else + printf(this.listIemFormatStr, this.txtItemList[i].substr(0, maxItemWidth)); + ++screenY; + } + // If the current screen row is below the bottom row inside the box, + // continue and write blank lines to the bottom of the inside of the box + // to blank out any text that might still be there. + while (screenY < this.dimensions.topLeftY+this.dimensions.height-1) + { + console.gotoxy(this.dimensions.topLeftX+1, screenY); + printf(this.listIemFormatStr, ""); + ++screenY; + } + + // Update the page number in the top border of the box. + console.gotoxy(pageNumTxtStartX, this.dimensions.topLeftY); + printf("n" + this.SlyEdCfgObj.genColors.listBoxBorderText + "Page %4d of %4d", pageNum+1, numPages); + + // Just for sane appearance: Move the cursor to the first character of + // the currently-selected row and set the appropriate color. + curpos.x = this.dimensions.topLeftX+1; + curpos.y = selectedItemRow; + console.gotoxy(curpos.x, curpos.y); + console.print(this.SlyEdCfgObj.genColors.listBoxItemHighlight); + + refreshList = false; + } + + // Get a key from the user (upper-case) and take action based upon it. + retObj.lastKeypress = getUserKey(K_UPPER|K_NOCRLF|K_NOSPIN, this.SlyEdCfgObj); + switch (retObj.lastKeypress) + { + case 'N': // Next page + refreshList = (pageNum < numPages-1); + if (refreshList) + { + ++pageNum; + this.topItemIndex += numItemsPerPage; + this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex; + // Note: this.bottomItemIndex is refreshed at the top of the loop + } + break; + case 'P': // Previous page + refreshList = (pageNum > 0); + if (refreshList) + { + --pageNum; + this.topItemIndex -= numItemsPerPage; + this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex; + // Note: this.bottomItemIndex is refreshed at the top of the loop + } + break; + case 'F': // First page + refreshList = (pageNum > 0); + if (refreshList) + { + pageNum = 0; + this.topItemIndex = 0; + this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex; + // Note: this.bottomItemIndex is refreshed at the top of the loop + } + break; + case 'L': // Last page + refreshList = (pageNum < numPages-1); + if (refreshList) + { + pageNum = numPages-1; + this.topItemIndex = topIndexForLastPage; + this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex; + // Note: this.bottomItemIndex is refreshed at the top of the loop + } + break; + case KEY_UP: + // Move the cursor up one item + if (retObj.selectedIndex > 0) + { + // If the previous item index is on the previous page, then we'll + // want to display the previous page. + var previousItemIndex = retObj.selectedIndex - 1; + if (previousItemIndex < this.topItemIndex) + { + --pageNum; + this.topItemIndex -= numItemsPerPage; + // Note: this.bottomItemIndex is refreshed at the top of the loop + refreshList = true; + } + else + { + // Display the current line un-highlighted + console.gotoxy(this.dimensions.topLeftX+1, curpos.y); + printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth)); + // Display the previous line highlighted + curpos.x = this.dimensions.topLeftX+1; + --curpos.y; + console.gotoxy(curpos); + printf(this.listIemHighlightFormatStr, this.txtItemList[previousItemIndex].substr(0, maxItemWidth)); + console.gotoxy(curpos); // Move the cursor into place where it should be + refreshList = false; + } + this.chosenTextItemIndex = retObj.selectedIndex = previousItemIndex; + } + break; + case KEY_DOWN: + // Move the cursor down one item + if (retObj.selectedIndex < this.txtItemList.length - 1) + { + // If the next item index is on the next page, then we'll want to + // display the next page. + var nextItemIndex = retObj.selectedIndex + 1; + if (nextItemIndex > this.bottomItemIndex) + { + ++pageNum; + this.topItemIndex += numItemsPerPage; + // Note: this.bottomItemIndex is refreshed at the top of the loop + refreshList = true; + } + else + { + // Display the current line un-highlighted + console.gotoxy(this.dimensions.topLeftX+1, curpos.y); + printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth)); + // Display the previous line highlighted + curpos.x = this.dimensions.topLeftX+1; + ++curpos.y; + console.gotoxy(curpos); + printf(this.listIemHighlightFormatStr, this.txtItemList[nextItemIndex].substr(0, maxItemWidth)); + console.gotoxy(curpos); // Move the cursor into place where it should be + refreshList = false; + } + this.chosenTextItemIndex = retObj.selectedIndex = nextItemIndex; + } + break; + case KEY_HOME: // Go to the first row in the box + if (retObj.selectedIndex > this.topItemIndex) + { + // Display the current line un-highlighted + console.gotoxy(this.dimensions.topLeftX+1, curpos.y); + printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth)); + // Select the top item, and display it highlighted. + this.chosenTextItemIndex = retObj.selectedIndex = this.topItemIndex; + curpos.x = this.dimensions.topLeftX+1; + curpos.y = this.dimensions.topLeftY+1; + console.gotoxy(curpos); + printf(this.listIemHighlightFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth)); + console.gotoxy(curpos); // Move the cursor into place where it should be + refreshList = false; + } + break; + case KEY_END: // Go to the last row in the box + if (retObj.selectedIndex < this.bottomItemIndex) + { + // Display the current line un-highlighted + console.gotoxy(this.dimensions.topLeftX+1, curpos.y); + printf(this.listIemFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth)); + // Select the bottommost item, and display it highlighted. + this.chosenTextItemIndex = retObj.selectedIndex = this.bottomItemIndex; + curpos.x = this.dimensions.topLeftX+1; + curpos.y = this.dimensions.bottomRightY-1; + console.gotoxy(curpos); + printf(this.listIemHighlightFormatStr, this.txtItemList[retObj.selectedIndex].substr(0, maxItemWidth)); + console.gotoxy(curpos); // Move the cursor into place where it should be + refreshList = false; + } + break; + case KEY_ENTER: + // If the enter key override function is set, then call it and pass + // this object into it. Otherwise, just select the item and quit. + if (this.enterKeyOverrideFn !== null) + this.enterKeyOverrideFn(this); + else + { + retObj.itemWasSelected = true; + // Note: retObj.selectedIndex is already set. + retObj.selectedItem = this.txtItemList[retObj.selectedIndex]; + refreshList = false; + continueOn = false; + } + break; + case KEY_ESC: // Quit + case CTRL_A: // Quit + case 'Q': // Quit + this.chosenTextItemIndex = retObj.selectedIndex = -1; + refreshList = false; + continueOn = false; + break; + default: + // If the keypress is an additional key to exit the input loop, then + // do so. + if (this.inputLoopExitKeys.hasOwnProperty(retObj.lastKeypress)) + { + this.chosenTextItemIndex = retObj.selectedIndex = -1; + refreshList = false; + continueOn = false; + } + else + { + // Unrecognized command. Don't refresh the list of the screen. + refreshList = false; + } + break; + } + } + + console.print("n"); // To prevent outputting highlight colors, etc.. + return retObj; +} + /////////////////////////////////////////////////////////////////////////////////// // Functions @@ -563,7 +1279,9 @@ function displayHelpHeader() // pCanCrossPost: Whether or not cross-posting is enabled // pIsSysop: Whether or not the user is the sysop. // pTxtReplacments: Whether or not the text replacements feature is enabled -function displayCommandList(pDisplayHeader, pClear, pPause, pCanCrossPost, pIsSysop, pTxtReplacments) +// pUserSettings: Whether or not the user settings feature is enabled +function displayCommandList(pDisplayHeader, pClear, pPause, pCanCrossPost, pIsSysop, + pTxtReplacments, pUserSettings) { if (pClear) console.clear("n"); @@ -611,6 +1329,8 @@ function displayCommandList(pDisplayHeader, pClear, pPause, pCanCrossPost, pIsSy displayCmdKeyFormattedDouble("Ctrl-R", "Program information", "/Q", "Quote message", true); if (pTxtReplacments) displayCmdKeyFormattedDouble("Ctrl-T", "List text replacements", "/T", "List text replacements", true); + if (pUserSettings) + displayCmdKeyFormattedDouble("", "", "/U", "Your settings", true); if (pCanCrossPost) displayCmdKeyFormattedDouble("", "", "/C", "Cross-post selection", true); printf(" ch%-7sg nc%s", "", "", "/?", "Show help"); @@ -629,6 +1349,9 @@ function displayCommandList(pDisplayHeader, pClear, pPause, pCanCrossPost, pIsSy if (isSysop) displayCmdKeyFormattedDouble("Ctrl-O", "Import a file", "Ctrl-X", "Export to file", true); + if (pUserSettings) + displayCmdKeyFormatted("Ctrl-U", "Your settings", true); + if (pPause) console.pause(); } @@ -743,9 +1466,13 @@ function writeWithPause(pX, pY, pText, pPauseMS, pClearLineAttrib) // pBoxTitle: For DCT mode, this specifies the title to use for the // prompt box. This is optional; if this is left out, // the prompt box title will default to "Prompt". +// pIceRefreshForBothAnswers: In Ice mode, whether or not to refresh the bottom +// line on the screen for a "yes" as well as "no" +// answer. This is optional. By default, only +// refreshes for a "no" answer. // // Return value: Boolean - true for a "Yes" answer, false for "No" -function promptYesNo(pQuestion, pDefaultYes, pBoxTitle) +function promptYesNo(pQuestion, pDefaultYes, pBoxTitle, pIceRefreshForBothAnswers) { var userResponse = pDefaultYes; @@ -768,9 +1495,10 @@ function promptYesNo(pQuestion, pDefaultYes, pBoxTitle) console.cleartoeol(); console.gotoxy(1, console.screen_rows); userResponse = promptYesNo_IceStyle(pQuestion, pDefaultYes); - // If the user chose "No", then re-display the bottom help line and - // move the cursor back to its original position. - if (!userResponse) + // If the user chose "No", or if we are to refresh the bottom line + // regardless, then re-display the bottom help line and move the + // cursor back to its original position. + if (pIceRefreshForBothAnswers || !userResponse) { fpDisplayBottomHelpLine(console.screen_rows, gUseQuotes); console.gotoxy(originalCurpos); @@ -787,6 +1515,7 @@ function ReadSlyEditConfigFile() { var cfgObj = new Object(); // Configuration object + cfgObj.userIsSysop = user.compare_ars("SYSOP"); // Whether or not the user is a sysop // Default settings cfgObj.thirdPartyLoadOnStart = new Array(); cfgObj.runJSOnStart = new Array(); @@ -798,21 +1527,17 @@ function ReadSlyEditConfigFile() cfgObj.reWrapQuoteLines = true; cfgObj.allowColorSelection = true; cfgObj.useQuoteLineInitials = true; - // The next setting specifies whether or not quote lines - // should be prefixed with a space when using author - // initials. cfgObj.indentQuoteLinesWithInitials = true; cfgObj.allowCrossPosting = true; cfgObj.enableTextReplacements = false; cfgObj.textReplacementsUseRegex = false; - cfgObj.userIsSysop = user.compare_ars("SYSOP"); // Whether or not the user is a sysop + cfgObj.enableTaglines = false; + cfgObj.tagLineFilename = "SlyEdit_Taglines.txt"; + cfgObj.allowUserSettings = true; // General SlyEdit color settings cfgObj.genColors = new Object(); // Cross-posting UI element colors - // Deprecated colors: - //cfgObj.genColors.crossPostBorder = "ng"; - //cfgObj.genColors.crossPostBorderTxt = "nbh"; cfgObj.genColors.listBoxBorder = "ng"; cfgObj.genColors.listBoxBorderText = "nbh"; cfgObj.genColors.crossPostMsgAreaNum = "nhw"; @@ -832,7 +1557,8 @@ function ReadSlyEditConfigFile() cfgObj.genColors.msgAbortedText = "nmh"; cfgObj.genColors.emptyMsgNotSentText = "nmh"; cfgObj.genColors.genMsgErrorText = "nmh"; - cfgObj.genColors.txtReplacementList = "nc"; + cfgObj.genColors.listBoxItemText = "nc"; + cfgObj.genColors.listBoxItemHighlight = "n4wh"; // Default Ice-style colors cfgObj.iceColors = new Object(); @@ -1029,6 +1755,12 @@ function ReadSlyEditConfigFile() else cfgObj.enableTextReplacements = (valueUpper == "TRUE"); } + else if (settingUpper == "ENABLETAGLINES") + cfgObj.enableTaglines = (valueUpper == "TRUE"); + else if (settingUpper == "TAGLINEFILENAME") + cfgObj.tagLineFilename = genFullPathCfgFilename(value, gStartupPath); + else if (settingUpper == "ALLOWUSERSETTINGS") + cfgObj.allowUserSettings = (valueUpper == "TRUE"); } else if (settingsMode == "ICEColors") { @@ -1783,7 +2515,6 @@ function firstNonQuoteTxtIndex(pStr, pUseAuthorInitials, pIndentQuoteLinesWithIn // If we haven't found non-quote text but the line starts with quote text, // then set the starting index & quote level in retObj. - //displayDebugText(1, 2, "Search start index: " + searchStartIndex, console.getxy(), true, true); if (lineStartsWithQuoteText && ((retObj.startIndex == -1) || (retObj.quoteLevel == 0))) { retObj.startIndex = pStr.indexOf(">") + 1; @@ -2559,7 +3290,8 @@ function moveGenColorsToGenSettings(pColorsArray, pCfgObj) colorSettingStrings.push("msgAbortedText"); colorSettingStrings.push("emptyMsgNotSentText"); colorSettingStrings.push("genMsgErrorText"); - colorSettingStrings.push("txtReplacementList"); + colorSettingStrings.push("listBoxItemText"); + colorSettingStrings.push("listBoxItemHighlight"); var colorName = ""; for (var i = 0; i < colorSettingStrings.length; ++i) @@ -2693,6 +3425,384 @@ function getUserKey(pMode, pCfgObj) return userKey; } +// Gets a string of user input such that each character is validated against a +// set of strings. Each string contains a list of valid characters, and each +// set of potential valid characters can only appear once in the user input. +// This function was written to be used in color selection in lieu of +// console.getkeys(), which outputs a carriage return at the end, which was not +// desirable. For instance, color selection involves prompting the user for +// 3 characters (foreground, background, and special). +// +// Parameters: +// pMode: The input mode bit(s). See K_* in sbbsdefs.js for mode bits. +// pValidKeyStrs: An array of strings containing valid keys +// pCfgObj: The SlyEdit configuration settings object +// pCurPos: Optional. Contains x and y coordinates specifying the current +// cursor position. If this is not specified, this function will +// get the cursor position by calling console.getxy(). +// +// Return value: The user's input, as a string. If nothing is input, this +// function will return an empty string. +function getUserInputWithSetOfInputStrs(pMode, pValidKeyStrs, pCfgObj, pCurPos) +{ + if (pValidKeyStrs == null) + return ""; + if (pValidKeyStrs.length == 0) + return ""; + + // Get the current cursor position, either from pCurPos or console.getxy(). + var curPos = (pCurPos != null ? pCurPos : console.getxy()); + + // Build one string containing all the valid keys. + var allValidKeys = ""; + for (var i = 0; i < pValidKeyStrs.length; ++i) + allValidKeys += pValidKeyStrs[i]; + + // User input loop + var displayChars = !((pMode & K_NOECHO) == K_NOECHO); + var userInput = ""; + var inputKey = ""; + var lastKey = ""; + var validKey = false; + var idx = 0; + var continueOn = true; + while (continueOn) + { + inputKey = getUserKey(pMode|K_NOECHO, pCfgObj); + // If userInput is blank, then the timeout was probably reached, so don't + // continue inputting characters. + if (inputKey.length == 0) + break; + + switch (inputKey) + { + case BACKSPACE: + // See if lastKey is in any of the strings in pValidKeyStrs. If so, + // then append that string back onto allValidKeys. + for (var i = 0; i < pValidKeyStrs.length; ++i) + { + if (pValidKeyStrs[i].indexOf(lastKey) > -1) + { + allValidKeys += pValidKeyStrs[i]; + break; + } + } + + // If userInput has some characters in it, then remove the last + // one and move the cursor back one space on the screen. + if (userInput.length > 0) + { + userInput = userInput.substr(0, userInput.length-1); + // If we are to display the input characters, then also blank out + // the character on the screen and make sure the cursor is placed + // properly on the screen. + if (displayChars) + { + console.gotoxy(--curPos.x, curPos.y); + console.print(" "); + console.gotoxy(curPos); + } + } + break; + // ESC and Ctrl-K: Cancel out of color selection, whereas + // ENTER will save the user's input before returning. + case KEY_ESC: + case CTRL_K: + userInput = ""; + case KEY_ENTER: + continueOn = false; + break; + default: + validKey = (allValidKeys.indexOf(inputKey) > -1); + if (validKey) + { + // Find the key in one of the strings in pValidKeyStrs. When + // found, remove that string from allValidKeys. + for (var i = 0; i < pValidKeyStrs.length; ++i) + { + validKey = (pValidKeyStrs[i].indexOf(inputKey) > -1); + if (validKey) + { + // Remove the current string from allValidKeys + idx = allValidKeys.indexOf(pValidKeyStrs[i]); + if (idx > -1) + { + allValidKeys = allValidKeys.substr(0, idx) + + allValidKeys.substr(idx + pValidKeyStrs[i].length); + } + + break; + } + } + } + + // If the user pressed a valid key (found in the input strings), then + // append it to userInput. + if (validKey) + { + // If K_NOECHO wasn't passed in pMode, then output the keypress + if (displayChars) + { + console.print(inputKey); + ++curPos.x; + } + userInput += inputKey; + } + break; + } + + // Update lastKey. Default to the last keypress, but if there is anything + // in userInput, then set lastKey to the last character in userInput. + lastKey = inputKey; + if (userInput.length > 0) + lastKey = userInput.substr(userInput.length-1); + } + return userInput; +} + +// Reads a text file and returns an array of strings containing the lines from +// the text file. +// +// Parameters: +// pFilename: The name of the file to read +// pStripCtrl: Boolean - Whether or not to strip control characters from the lines +// pIgnoreBlankLines: Boolean - Whether or not to ignore blank lines +// pMaxNumLines: Optional - The maximum number of lines to read from the file +// +// Return value: An array of strings read from the file +function readTxtFileIntoArray(pFilename, pStripCtrl, pIgnoreBlankLines, pMaxNumLines) +{ + var fileStrs = new Array(); + + var maxNumLines = -1; + if (typeof(pMaxNumLines) == "number") + { + if (pMaxNumLines > -1) + maxNumLines = pMaxNumLines; + } + if (maxNumLines == 0) + return fileStrs; + + var txtFile = new File(pFilename); + if (txtFile.open("r")) + { + var fileLine = null; // A line read from the file + var numLinesRead = 0; + while (!txtFile.eof) + { + // Read the next line from the config file. + fileLine = txtFile.readln(2048); + + // fileLine should be a string, but I've seen some cases + // where for some reason it isn't. If it's not a string, + // then continue onto the next line. + if (typeof(fileLine) != "string") + continue; + + if (pStripCtrl) + fileLine = strip_ctrl(fileLine); + + if (pIgnoreBlankLines && (fileLine.length == 0)) + continue; + + fileStrs.push(fileLine); + + ++numLinesRead; + if ((maxNumLines > 0) && (numLinesRead >= maxNumLines)) + break; + } + + txtFile.close(); + } + + return fileStrs; +} + +// Returns whether or not a text file contains lines in it. +// +// Parameters: +// pFilename: The name of the file to test +// +// Return value: Boolean - Whether or not the file contains lines +function txtFileContainsLines(pFilename) +{ + var fileContainsLines = false; + + var txtFile = new File(pFilename); + if (txtFile.open("r")) + { + var fileLine = null; // A line read from the file + var numLinesRead = 0; + while (!txtFile.eof && (numLinesRead == 0)) + { + // Read the next line from the config file. + fileLine = txtFile.readln(2048); + + // fileLine should be a string, but I've seen some cases + // where for some reason it isn't. If it's not a string, + // then continue onto the next line. + if (typeof(fileLine) != "string") + continue; + ++numLinesRead; + } + fileContainsLines = (numLinesRead > 0); + + txtFile.close(); + } + + return fileContainsLines; +} + +// Reads the user settings file and returns an object containing the user's +// settings. +// +// Parameters: +// pSlyEdCfgObj: The SlyEdit configuration object, which contains defaults +// for some of the user settings. This settings object should +// be populated before this function is called. +// +// Return value: An object containing the user's settings as properties. +function ReadUserSettingsFile(pSlyEdCfgObj) +{ + var userSettingsObj = new Object(); + + // Default settings + userSettingsObj.enableTaglines = pSlyEdCfgObj.enableTaglines; + userSettingsObj.useQuoteLineInitials = pSlyEdCfgObj.useQuoteLineInitials; + // The next setting specifies whether or not quote lines + // should be prefixed with a space when using author + // initials. + userSettingsObj.indentQuoteLinesWithInitials = pSlyEdCfgObj.indentQuoteLinesWithInitials; + + // Open the user settings file + var userSettingsFile = new File(gUserSettingsFilename); + if (userSettingsFile.open("r")) + { + var settingsMode = "behavior"; + var fileLine = null; // A line read from the file + var equalsPos = 0; // Position of a = in the line + var commentPos = 0; // Position of the start of a comment + var setting = null; // A setting name (string) + var settingUpper = null; // Upper-case setting name + var value = null; // A value for a setting (string) + var valueUpper = null; // Upper-cased value + while (!userSettingsFile.eof) + { + // Read the next line from the config file. + fileLine = userSettingsFile.readln(2048); + + // fileLine should be a string, but I've seen some cases + // where for some reason it isn't. If it's not a string, + // then continue onto the next line. + if (typeof(fileLine) != "string") + continue; + + // If the line starts with with a semicolon (the comment + // character) or is blank, then skip it. + if ((fileLine.substr(0, 1) == ";") || (fileLine.length == 0)) + continue; + + // If in the "behavior" section, then set the behavior-related variables. + if (fileLine.toUpperCase() == "[BEHAVIOR]") + { + settingsMode = "behavior"; + continue; + } + + // If the line has a semicolon anywhere in it, then remove + // everything from the semicolon onward. + commentPos = fileLine.indexOf(";"); + if (commentPos > -1) + fileLine = fileLine.substr(0, commentPos); + + // Look for an equals sign, and if found, separate the line + // into the setting name (before the =) and the value (after the + // equals sign). + equalsPos = fileLine.indexOf("="); + if (equalsPos > 0) + { + // Read the setting & value, and trim leading & trailing spaces. + setting = trimSpaces(fileLine.substr(0, equalsPos), true, false, true); + settingUpper = setting.toUpperCase(); + value = trimSpaces(fileLine.substr(equalsPos+1), true, false, true); + valueUpper = value.toUpperCase(); + + if (settingsMode == "behavior") + { + if (settingUpper == "ENABLETAGLINES") + userSettingsObj.enableTaglines = (valueUpper == "TRUE"); + else if (settingUpper == "USEQUOTELINEINITIALS") + userSettingsObj.useQuoteLineInitials = (valueUpper == "TRUE"); + else if (settingUpper == "INDENTQUOTELINESWITHINITIALS") + userSettingsObj.indentQuoteLinesWithInitials = (valueUpper == "TRUE"); + } + } + } + userSettingsFile.close(); + } + else + { + // We couldn't read the user settings file - So this is probably the + // first time the user has run SlyEdit. So, save the settings to the + // file. + var saveSucceeded = WriteUserSettingsFile(userSettingsObj); + } + + return userSettingsObj; +} + +// Writes the user settings to the user settings file, overwriting the +// existing file. +// +// Parameters: +// pUserSettingsObj: The object containing the user's settings +// +// Return value: Boolean - Whether or not this function succeeded in writing the file. +function WriteUserSettingsFile(pUserSettingsObj) +{ + var writeSucceeded = false; + + var userSettingsFile = new File(gUserSettingsFilename); + if (userSettingsFile.open("w")) + { + const behaviorBoolSettingNames = ["enableTaglines", + "useQuoteLineInitials", + "indentQuoteLinesWithInitials"]; + userSettingsFile.writeln("[BEAVHIOR]"); + for (var i = 0; i < behaviorBoolSettingNames.length; ++i) + { + if (pUserSettingsObj.hasOwnProperty(behaviorBoolSettingNames[i])) + userSettingsFile.writeln(behaviorBoolSettingNames[i] + "=" + (pUserSettingsObj[behaviorBoolSettingNames[i]] ? "true" : "false")); + } + + userSettingsFile.close(); + writeSucceeded = true; + } + + return writeSucceeded; +} + +// Changes a character in a string, and returns the new string. If any of the +// parameters are invalid, then the original string will be returned. +// +// Parameters: +// pStr: The original string +// pCharIndex: The index of the character to replace +// pNewText: The new character or text to place at that position in the string +// +// Return value: The new string +function chgCharInStr(pStr, pCharIndex, pNewText) +{ + if (typeof(pStr) != "string") + return ""; + if ((pCharIndex < 0) || (pCharIndex >= pStr.length)) + return pStr; + if (typeof(pNewText) != "string") + return pStr; + + return (pStr.substr(0, pCharIndex) + pNewText + pStr.substr(pCharIndex+1)); +} + // This function displays debug text at a given location on the screen, then // moves the cursor back to a given location. // -- GitLab