/* This file is part of * ====================================================== * * LyX, The Document Processor * * Copyright 1995 Matthias Ettrich * Copyright 1995-2000 The LyX Team. * * ====================================================== */ #include #include #include #include #include #include #ifdef __GNUG__ #pragma implementation #endif #include "lyxlookup.h" #include "kbmap.h" #include "lyxfunc.h" #include "bufferlist.h" #include "lyxserver.h" #include "lyx.h" #include "intl.h" #include "lyx_main.h" #include "lyx_cb.h" #include "LyXAction.h" #include "insets/inseturl.h" #include "insets/insetlatexaccent.h" #include "insets/insettoc.h" #include "insets/insetlof.h" #include "insets/insetloa.h" #include "insets/insetlot.h" #include "insets/insetref.h" #include "insets/insetparent.h" #include "insets/insetindex.h" #include "insets/insetinclude.h" #include "insets/insetbib.h" #include "insets/insettext.h" #include "insets/insetert.h" #include "mathed/formulamacro.h" #include "toolbar.h" #include "spellchecker.h" // RVDK_PATCH_5 #include "minibuffer.h" #include "vspace.h" #include "LyXView.h" #include "filedlg.h" #include "lyx_gui_misc.h" #include "support/filetools.h" #include "support/FileInfo.h" #include "support/syscall.h" #include "support/lstrings.h" #include "support/path.h" #include "debug.h" #include "lyxrc.h" #include "lyxtext.h" #include "gettext.h" #include "trans_mgr.h" #include "ImportLaTeX.h" #include "ImportNoweb.h" #include "layout.h" #include "WorkArea.h" extern bool cursor_follows_scrollbar; extern void InsertAsciiFile(string const &, bool); extern void math_insert_symbol(char const *); extern Bool math_insert_greek(char const); // why "Bool"? extern BufferList bufferlist; extern LyXServer * lyxserver; extern short greek_kb_flag; extern FD_form_toc * fd_form_toc; extern bool selection_possible; extern kb_keymap * toplevel_keymap; extern void MenuWrite(Buffer *); extern void MenuWriteAs(Buffer *); extern int MenuRunLaTeX(Buffer *); extern int MenuBuildProg(Buffer *); extern int MenuRunChktex(Buffer *); extern bool CreatePostscript(Buffer *, bool); extern void MenuPrint(Buffer *); extern void MenuSendto(); extern void QuitLyX(); extern void MenuFax(Buffer *); extern void MenuExport(Buffer *, string const &); extern void MenuPasteSelection(char at); extern LyXAction lyxaction; // (alkis) extern tex_accent_struct get_accent(kb_action action); extern void AutoSave(); extern void MenuSearch(); extern void SetUpdateTimer(float timer = 0.3); extern void FreeUpdateTimer(); extern bool PreviewDVI(Buffer *); extern bool PreviewPostscript(Buffer *); extern void MenuInsertLabel(char const *); extern void MenuInsertRef(); extern void MenuLayoutCharacter(); extern void MenuLayoutParagraph(); extern void MenuLayoutDocument(); extern void MenuLayoutPaper(); extern void MenuLayoutTable(int flag); extern void MenuLayoutQuotes(); extern void MenuLayoutPreamble(); extern void MenuLayoutSave(); extern void bulletForm(); extern Buffer * NewLyxFile(string const &); extern void LoadLyXFile(string const &); extern void Reconfigure(BufferView *); extern int current_layout; extern int getISOCodeFromLaTeX(char *); extern void ShowLatexLog(); /* === globals =========================================================== */ bool LyXFunc::show_sc = true; LyXFunc::LyXFunc(LyXView * o) : owner(o) { meta_fake_bit = 0; lyx_dead_action = LFUN_NOACTION; lyx_calling_dead_action = LFUN_NOACTION; setupLocalKeymap(); } // I changed this func slightly. I commented out the ...FinishUndo(), // this means that all places that used to have a moveCursorUpdate, now // have a ...FinishUndo() as the preceeding statement. I have also added // a moveCursorUpdate to some of the functions that updated the cursor, but // that did not show its new position. inline void LyXFunc::moveCursorUpdate(bool selecting) { if (selecting || owner->view()->text->mark_set) { owner->view()->text->SetSelection(); owner->view()->toggleToggle(); owner->view()->update(0); } else { owner->view()->update(-2); // this IS necessary // (Matthias) } owner->view()->showCursor(); /* ---> Everytime the cursor is moved, show the current font state. */ // should this too me moved out of this func? //owner->getMiniBuffer()->Set(CurrentState()); owner->view()->setState(); } int LyXFunc::processKeyEvent(XEvent * ev) { char s_r[10]; string argument; XKeyEvent * keyevent = &ev->xkey; KeySym keysym_return; int num_bytes = LyXLookupString(ev, s_r, 10, &keysym_return); s_r[num_bytes] = '\0'; if (lyxerr.debugging(Debug::KEY)) { char * tmp = XKeysymToString(keysym_return); string stm = (tmp ? tmp : ""); lyxerr << "KeySym is " << stm << "[" << keysym_return << "]" << " and num_bytes is " << num_bytes << " the string returned is \"" << s_r << '\"' << endl; } // Do nothing if we have nothing (JMarc) if (num_bytes == 0 && keysym_return == NoSymbol) { lyxerr[Debug::KEY] << "Empty kbd action (probably composing)" << endl; return 0; } // this function should be used always [asierra060396] if (owner->view()->available() && owner->view()->the_locking_inset && keysym_return == XK_Escape) { owner->view()->unlockInset(owner->view()->the_locking_inset); owner->view()->text->CursorRight(); moveCursorUpdate(false); owner->getMiniBuffer()->Set(CurrentState()); return 0; } // Can we be sure that this will work for all X-Windows // implementations? (Lgb) // This code snippet makes lyx ignore some keys. Perhaps // all of them should be explictly mentioned? if((keysym_return >= XK_Shift_L && keysym_return <= XK_Hyper_R) || keysym_return == XK_Mode_switch || keysym_return == 0x0) return 0; // Do a one-deep top-level lookup for // cancel and meta-fake keys. RVDK_PATCH_5 cancel_meta_seq.reset(); int action = cancel_meta_seq.addkey(keysym_return, keyevent->state &(ShiftMask|ControlMask |Mod1Mask)); // When not cancel or meta-fake, do the normal lookup. // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards. // Mostly, meta_fake_bit = 0. RVDK_PATCH_5. if ( (action != LFUN_CANCEL) && (action != LFUN_META_FAKE) ) { // remove Caps Lock and Mod2 as a modifiers action = keyseq.addkey(keysym_return, (keyevent->state | meta_fake_bit) &(ShiftMask|ControlMask |Mod1Mask)); } // Dont remove this unless you know what you are doing. meta_fake_bit = 0; if (action == 0) action = LFUN_PREFIX; if (lyxerr.debugging(Debug::KEY)) { string buf; keyseq.print(buf); lyxerr << "Key [" << action << "][" << buf << "][" << num_bytes << "]" << endl; } // already here we know if it any point in going further // why not return already here if action == -1 and // num_bytes == 0? (Lgb) if(keyseq.length > 1 || keyseq.length < -1) { string buf; keyseq.print(buf); owner->getMiniBuffer()->Set(buf); } if (action == -1) { if (keyseq.length < -1) { // unknown key sequence... string buf; LyXBell(); keyseq.print(buf); owner->getMiniBuffer()->Set(_("Unknown sequence:"), buf); return 0; } char isochar = keyseq.getiso(); if (!(keyevent->state&ControlMask) && !(keyevent->state&Mod1Mask) && (isochar && keysym_return < 0xF000)) { argument += isochar; } if (argument.empty()) { lyxerr.debug() << "Empty argument!" << endl; // This can`t possibly be of any use // so we`ll skip the dispatch. return 0; } } else if (action == LFUN_SELFINSERT) { argument = s_r[0]; } bool tmp_sc = show_sc; show_sc = false; Dispatch(action, argument.c_str()); show_sc = tmp_sc; return 0; } LyXFunc::func_status LyXFunc::getStatus(int ac) const { kb_action action; func_status flag = LyXFunc::OK; string argument; Buffer * buf = owner->buffer(); if (lyxaction.isPseudoAction(ac)) action = lyxaction.retrieveActionArg(ac, argument); else action = static_cast(ac); if (action == LFUN_UNKNOWN_ACTION) { setErrorMessage(N_("Unknown action")); return LyXFunc::Unknown; } // Check whether we need a buffer if (!lyxaction.funcHasFlag(action, LyXAction::NoBuffer)) { // Yes we need a buffer, do we have one? if (buf) { // yes // Can we use a readonly buffer? if (buf->isReadonly() && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly)) { // no setErrorMessage(N_("Document is read-only")); flag |= LyXFunc::Disabled; } } else { // no setErrorMessage(N_("Command not allowed with" "out any document open")); flag |= LyXFunc::Disabled; } } if (flag & LyXFunc::Disabled) return flag; // I would really like to avoid having this switch and rather try to // encode this in the function itself. static bool noLaTeX = lyxrc->latex_command == "none"; bool disable = false; switch (action) { case LFUN_PREVIEW: disable = noLaTeX || lyxrc->view_dvi_command == "none"; break; case LFUN_PREVIEWPS: disable = noLaTeX || lyxrc->view_ps_command == "none"; break; case LFUN_RUNLATEX: case LFUN_RUNDVIPS: disable = noLaTeX; break; case LFUN_MENUPRINT: disable = noLaTeX || lyxrc->print_command == "none"; break; case LFUN_FAX: disable = noLaTeX || lyxrc->fax_command == "none"; break; case LFUN_IMPORT: if (argument == "latex") disable = lyxrc->relyx_command == "none"; break; case LFUN_EXPORT: if (argument == "dvi" || argument == "postscript") disable = noLaTeX; if (argument == "html") disable = lyxrc->html_command == "none"; break; case LFUN_UNDO: disable = buf->undostack.empty(); break; case LFUN_REDO: disable = buf->redostack.empty(); break; case LFUN_SPELLCHECK: disable = lyxrc->isp_command == "none"; break; case LFUN_RUNCHKTEX: disable = lyxrc->chktex_command == "none"; break; case LFUN_LAYOUT_TABLE: disable = ! owner->view()->text->cursor.par->table; break; default: break; } if (disable) flag |= LyXFunc::Disabled; if (buf) { func_status box = LyXFunc::ToggleOff; LyXFont font = owner->view()->text->real_current_font; switch (action) { case LFUN_EMPH: if (font.emph() == LyXFont::ON) box = LyXFunc::ToggleOn; break; case LFUN_NOUN: if (font.noun() == LyXFont::ON) box = LyXFunc::ToggleOn; break; case LFUN_BOLD: if (font.series() == LyXFont::BOLD_SERIES) box = LyXFunc::ToggleOn; break; case LFUN_TEX: if (font.latex() == LyXFont::ON) box = LyXFunc::ToggleOn; break; case LFUN_RTL: if (font.direction() == LyXFont::RTL_DIR) box = LyXFunc::ToggleOn; break; default: box = LyXFunc::OK; break; } flag |= box; } return flag; } string LyXFunc::Dispatch(string const & s) { // Split command string into command and argument string cmd, line = frontStrip(s); string arg = strip(frontStrip(split(line, cmd, ' '))); return Dispatch(lyxaction.LookupFunc(cmd.c_str()), arg.c_str()); } string LyXFunc::Dispatch(int ac, char const * do_not_use_this_arg) { string argument; kb_action action; // we have not done anything wrong yet. errorstat = false; dispatch_buffer.clear(); // if action is a pseudo-action, we need the real action if (lyxaction.isPseudoAction(ac)) { string tmparg; action = static_cast (lyxaction.retrieveActionArg(ac, tmparg)); if (!tmparg.empty()) argument = tmparg; } else { action = static_cast(ac); if (do_not_use_this_arg) argument = do_not_use_this_arg; // except here } selection_possible = false; if (owner->view()->available()) owner->view()->hideCursor(); // We cannot use this function here if (getStatus(action) & Disabled) goto exit_with_message; commandshortcut.clear(); if (lyxrc->display_shortcuts && show_sc) { if (action != LFUN_SELFINSERT) { // Put name of command and list of shortcuts // for it in minibuffer string comname = lyxaction.getActionName(action); int pseudoaction = action; bool argsadded = false; if (!argument.empty()) { // If we have the command with argument, // this is better pseudoaction = lyxaction.searchActionArg(action, argument.c_str()); if (pseudoaction == -1) { pseudoaction = action; } else { comname += " " + argument; argsadded = true; } } string shortcuts = toplevel_keymap->findbinding(pseudoaction); if (!shortcuts.empty()) { comname += ": " + shortcuts; } else if (!argsadded) { comname += " " + argument; } if (!comname.empty()) { comname = strip(comname); commandshortcut = "(" + comname + ')'; owner->getMiniBuffer()->Set(commandshortcut); // Here we could even add a small pause, // to annoy the user and make him learn // the shortcuts. // No! That will just annoy, not teach // anything. The user will read the messages // if they are interested. (Asger) } } } // If in math mode pass the control to // the math inset [asierra060396] if (owner->view()->available() && owner->view()->the_locking_inset) { if (action > 1 || (action == LFUN_UNKNOWN_ACTION && keyseq.length >= -1)) { if (action == LFUN_UNKNOWN_ACTION && argument.empty()) { argument = keyseq.getiso(); } // Undo/Redo pre 0.13 is a bit tricky for insets. if (action == LFUN_UNDO) { int slx, sly; UpdatableInset * inset = owner->view()->the_locking_inset; inset->GetCursorPos(slx, sly); owner->view()->unlockInset(inset); owner->view()->menuUndo(); inset = static_cast( owner->view()->text->cursor.par-> GetInset(owner->view()->text-> cursor.pos)); if (inset) inset->Edit(owner->view(),slx,sly,0); return string(); } else if (action == LFUN_REDO) { int slx, sly; UpdatableInset * inset = owner->view()-> the_locking_inset; inset->GetCursorPos(slx, sly); owner->view()->unlockInset(inset); owner->view()->menuRedo(); inset = static_cast( owner->view()->text->cursor.par-> GetInset(owner->view()->text-> cursor.pos)); if (inset) inset->Edit(owner->view(),slx,sly,0); return string(); } else if (owner->view()->the_locking_inset-> LocalDispatch(owner->view(), action, argument) == UpdatableInset::DISPATCHED) return string(); else { setMessage(N_("Text mode")); LyXDirection direction = owner->view()->text-> cursor.par->getParDirection(); if ((action == -1) || ((action == LFUN_RIGHT) && (direction == LYX_DIR_LEFT_TO_RIGHT))) { owner->view()->text->CursorRight(); moveCursorUpdate(false); owner->getMiniBuffer()-> Set(CurrentState()); } if ((action == LFUN_LEFT) && (direction == LYX_DIR_RIGHT_TO_LEFT)) { owner->view()->text->CursorRight(); moveCursorUpdate(false); owner->getMiniBuffer()-> Set(CurrentState()); } if ((action == LFUN_LEFT) || (action == LFUN_RIGHT)) return string(); } } } switch(action) { // --- Misc ------------------------------------------- case LFUN_WORDFINDFORWARD : case LFUN_WORDFINDBACKWARD : { static string last_search; string searched_string; if (!argument.empty()) { last_search = argument; searched_string = argument; } else { searched_string = last_search; } LyXText * ltCur = owner->view()->text ; if (!searched_string.empty() && ((action == LFUN_WORDFINDBACKWARD) ? ltCur->SearchBackward(searched_string.c_str()) : ltCur->SearchForward(searched_string.c_str()))) { // ??? What is that ??? owner->view()->update(-2); // ??? Needed ??? // clear the selection (if there is any) owner->view()->toggleSelection(); owner->view()->text->ClearSelection(); // Move cursor so that successive C-s 's will not stand in place. if( action == LFUN_WORDFINDFORWARD ) owner->view()->text->CursorRightOneWord(); owner->view()->text->FinishUndo(); moveCursorUpdate(false); // ??? Needed ??? // set the new selection // SetSelectionOverLenChars(owner->view()->currentBuffer()->text, iLenSelected); owner->view()->toggleSelection(false); } else LyXBell(); // REMOVED : if (owner->view()->getWorkArea()->focus) owner->view()->showCursor(); } break; case LFUN_PREFIX: { if (owner->view()->available()) { owner->view()->update(-2); } string buf; keyseq.print(buf, true); owner->getMiniBuffer()->Set(buf, string(), string(), 1); } break; // --- Misc ------------------------------------------- case LFUN_EXEC_COMMAND: owner->getMiniBuffer()->ExecCommand(); break; case LFUN_CANCEL: // RVDK_PATCH_5 keyseq.reset(); meta_fake_bit = 0; if(owner->view()->available()) // cancel any selection Dispatch(LFUN_MARK_OFF, 0); setMessage(N_("Cancel")); break; case LFUN_META_FAKE: // RVDK_PATCH_5 { meta_fake_bit = Mod1Mask; string buf; keyseq.print(buf, true); string res = string("M-") + buf; setMessage(buf); // RVDK_PATCH_5 } break; case LFUN_READ_ONLY_TOGGLE: if (owner->buffer()->lyxvc.inUse()) { owner->buffer()->lyxvc.toggleReadOnly(); } else { owner->buffer()->setReadonly( !owner->buffer()->isReadonly()); } break; case LFUN_CENTER: // this is center and redraw. owner->view()->center(); break; case LFUN_APPENDIX: if (owner->view()->available()) { owner->view()->text->toggleAppendix(); owner->view()->update(1); } break; // --- Menus ----------------------------------------------- case LFUN_MENUNEW: MenuNew(false); break; case LFUN_MENUNEWTMPLT: MenuNew(true); break; case LFUN_MENUOPEN: MenuOpen(); break; case LFUN_CLOSEBUFFER: CloseBuffer(); break; case LFUN_MENUWRITE: owner->getMiniBuffer()->Set(_("Saving document"), MakeDisplayPath(owner->buffer()->fileName()), "..."); MenuWrite(owner->buffer()); //owner->getMiniBuffer()-> // Set(_("Document saved as"), // MakeDisplayPath(owner->buffer()->fileName())); //} else { //owner->getMiniBuffer()->Set(_("Save failed!")); //} break; case LFUN_MENUWRITEAS: MenuWriteAs(owner->buffer()); break; case LFUN_MENURELOAD: reloadBuffer(); break; case LFUN_PREVIEW: PreviewDVI(owner->buffer()); break; case LFUN_PREVIEWPS: PreviewPostscript(owner->buffer()); break; case LFUN_RUNLATEX: MenuRunLaTeX(owner->buffer()); break; case LFUN_BUILDPROG: MenuBuildProg(owner->buffer()); break; case LFUN_RUNCHKTEX: MenuRunChktex(owner->buffer()); break; case LFUN_RUNDVIPS: CreatePostscript(owner->buffer(), false); break; case LFUN_MENUPRINT: MenuPrint(owner->buffer()); break; case LFUN_FAX: MenuFax(owner->buffer()); break; case LFUN_EXPORT: MenuExport(owner->buffer(), argument); break; case LFUN_IMPORT: { //needs argument as string string imtyp = argument; // latex if (imtyp == "latex") { doImportLaTeX(false); } // ascii else if (imtyp == "ascii") { doImportASCII(false); } else if (imtyp == "asciiparagraph") { doImportASCII(true); // noweb } else if (imtyp == "noweb") { doImportLaTeX(true); } else { setErrorMessage(string(N_("Unknown import type: ")) + imtyp); } break; } case LFUN_QUIT: QuitLyX(); break; case LFUN_TOCVIEW: TocUpdateCB(0, 0); if (fd_form_toc->form_toc->visible) { fl_raise_form(fd_form_toc->form_toc); } else { static int ow = -1, oh; fl_show_form(fd_form_toc->form_toc, FL_PLACE_MOUSE | FL_FREE_SIZE, FL_FULLBORDER, _("Table of Contents")); if (ow < 0) { ow = fd_form_toc->form_toc->w; oh = fd_form_toc->form_toc->h; } fl_set_form_minsize(fd_form_toc->form_toc, ow, oh); } break; case LFUN_TOC_INSERT: { Inset * new_inset = new InsetTOC(owner->buffer()); owner->view()->insertInset(new_inset, "Standard", true); break; } case LFUN_LOF_INSERT: { Inset * new_inset = new InsetLOF(owner->buffer()); owner->view()->insertInset(new_inset, "Standard", true); break; } case LFUN_LOA_INSERT: { Inset * new_inset = new InsetLOA(owner->buffer()); owner->view()->insertInset(new_inset, "Standard", true); break; } case LFUN_LOT_INSERT: { Inset * new_inset = new InsetLOT(owner->buffer()); owner->view()->insertInset(new_inset, "Standard", true); break; } case LFUN_TABLE: Table(); break; case LFUN_FIGURE: Figure(); break; case LFUN_AUTOSAVE: AutoSave(); break; case LFUN_UNDO: owner->view()->menuUndo(); break; case LFUN_REDO: owner->view()->menuRedo(); break; case LFUN_MENUSEARCH: MenuSearch(); break; case LFUN_PASTE: owner->view()->paste(); break; case LFUN_PASTESELECTION: { bool asPara = false; if (argument == "paragraph") asPara = true; MenuPasteSelection(asPara); break; } case LFUN_CUT: owner->view()->cut(); break; case LFUN_COPY: owner->view()->copy(); break; case LFUN_LAYOUT_COPY: owner->view()->copyEnvironment(); break; case LFUN_LAYOUT_PASTE: owner->view()->pasteEnvironment(); break; case LFUN_GOTOERROR: owner->view()->gotoError(); break; case LFUN_REMOVEERRORS: if (owner->view()->removeAutoInsets()) { owner->view()->redraw(); owner->view()->fitCursor(); owner->view()->updateScrollbar(); } break; case LFUN_GOTONOTE: owner->view()->gotoNote(); break; case LFUN_OPENSTUFF: owner->view()->openStuff(); break; case LFUN_HYPHENATION: owner->view()->hyphenationPoint(); break; case LFUN_LDOTS: owner->view()->ldots(); break; case LFUN_END_OF_SENTENCE: owner->view()->endOfSentenceDot(); break; case LFUN_MENU_SEPARATOR: owner->view()->menuSeparator(); break; case LFUN_HFILL: owner->view()->hfill(); break; case LFUN_DEPTH: changeDepth(owner->view(), 0); break; case LFUN_DEPTH_MIN: changeDepth(owner->view(), -1); break; case LFUN_DEPTH_PLUS: changeDepth(owner->view(), 1); break; case LFUN_FREE: Free(); break; case LFUN_TEX: Tex(); owner->view()->setState(); owner->getMiniBuffer()->Set(CurrentState()); break; case LFUN_MELT: Melt(owner->view()); break; case LFUN_RECONFIGURE: Reconfigure(owner->view()); break; case LFUN_FOOTMELT: if (owner->view()->available() && !owner->view()->text->selection && owner->view()->text->cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) { // only melt footnotes with FOOTMELT, not margins etc if(owner->view()->text->cursor.par->footnotekind == LyXParagraph::FOOTNOTE) Melt(owner->view()); } else Foot(owner->view()); owner->view()->setState(); break; case LFUN_MARGINMELT: if (owner->view()->available() && !owner->view()->text->selection && owner->view()->text->cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) { // only melt margins if(owner->view()->text->cursor.par->footnotekind == LyXParagraph::MARGIN) Melt(owner->view()); } else Margin(owner->view()); owner->view()->setState(); break; // --- version control ------------------------------- case LFUN_VC_REGISTER: { if (!owner->buffer()->lyxvc.inUse()) owner->buffer()->lyxvc.registrer(); } break; case LFUN_VC_CHECKIN: { if (owner->buffer()->lyxvc.inUse() && !owner->buffer()->isReadonly()) owner->buffer()->lyxvc.checkIn(); } break; case LFUN_VC_CHECKOUT: { if (owner->buffer()->lyxvc.inUse() && owner->buffer()->isReadonly()) owner->buffer()->lyxvc.checkOut(); } break; case LFUN_VC_REVERT: { owner->buffer()->lyxvc.revert(); } break; case LFUN_VC_UNDO: { owner->buffer()->lyxvc.undoLast(); } break; case LFUN_VC_HISTORY: { owner->buffer()->lyxvc.showLog(); break; } // --- buffers ---------------------------------------- case LFUN_FILE_INSERT: { MenuInsertLyXFile(argument); } break; case LFUN_FILE_INSERT_ASCII: { bool asPara = (argument == "paragraph"); InsertAsciiFile(string(), asPara); } break; case LFUN_FILE_NEW: { // servercmd: argument must be :