/** * \file FormParagraph.C * Copyright 2000-2001 The LyX Team. * See the file COPYING. * * \author Jürgen Vigna, jug@sad.it */ #include #ifdef __GNUG_ #pragma implementation #endif #include FORMS_H_LOCATION #include "FormParagraph.h" #include "form_paragraph.h" #include "Dialogs.h" #include "Liason.h" #include "LyXView.h" #include "buffer.h" #include "lyxtext.h" #include "xforms_helpers.h" #include "lyxrc.h" // to set the deafult length values #include "BufferView.h" #include "lyxtextclasslist.h" #include "Spacing.h" #include "ParagraphParameters.h" #include "input_validators.h" #include "helper_funcs.h" #include "support/lstrings.h" #include "support/LAssert.h" #include using Liason::setMinibuffer; using SigC::slot; using std::vector; using std::bind2nd; using std::remove_if; FormParagraph::FormParagraph(LyXView * lv, Dialogs * d) : FormBaseBD(lv, d, _("Paragraph Layout")), par_(0) { // let the dialog be shown // This is a permanent connection so we won't bother // storing a copy because we won't be disconnecting. d->showParagraph.connect(slot(this, &FormParagraph::show)); } void FormParagraph::connect() { cp_ = d_->updateParagraph .connect(slot(this, &FormParagraph::changedParagraph)); FormBaseBD::connect(); } void FormParagraph::disconnect() { cp_.disconnect(); FormBaseBD::disconnect(); } Paragraph const * FormParagraph::getCurrentParagraph() const { return lv_->view()->getLyXText()->cursor.par(); } void FormParagraph::changedParagraph() { /// Record the paragraph Paragraph const * const p = getCurrentParagraph(); if (p == 0 || p == par_) return; // For now, don't bother checking if the params are different. // Will the underlying paragraph accept our changes? Inset * const inset = p->inInset(); bool const accept = !(inset && inset->forceDefaultParagraphs(inset)); bc().valid(accept); if (!accept) { postWarning(_("Cannot apply paragraph settings to this inset!")); } else { clearMessage(); } } void FormParagraph::redraw() { if (form() && form()->visible) fl_redraw_form(form()); } FL_FORM * FormParagraph::form() const { if (dialog_.get()) return dialog_->form; return 0; } void FormParagraph::build() { // the tabbed folder dialog_.reset(build_paragraph()); // Allow the base class to control messages setMessageWidget(dialog_->text_warning); fl_addto_choice(dialog_->choice_space_above, _(" None | Defskip | Smallskip " "| Medskip | Bigskip | VFill | Length ")); fl_addto_choice(dialog_->choice_space_below, _(" None | Defskip | Smallskip " "| Medskip | Bigskip | VFill | Length ")); fl_addto_choice(dialog_->choice_linespacing, _(" Default | Single | OneHalf | Double | Other ")); fl_set_input_return(dialog_->input_space_above, FL_RETURN_CHANGED); fl_set_input_return(dialog_->input_space_below, FL_RETURN_CHANGED); fl_set_input_return(dialog_->input_labelwidth, FL_RETURN_CHANGED); fl_set_input_return(dialog_->input_linespacing, FL_RETURN_CHANGED); fl_set_input_filter(dialog_->input_linespacing, fl_unsigned_float_filter); setPrehandler(dialog_->input_space_above); setPrehandler(dialog_->input_space_below); setPrehandler(dialog_->input_labelwidth); setPrehandler(dialog_->input_linespacing); // Create the contents of the unit choices // Don't include the "%" terms... vector units_vec = getLatexUnits(); #if 0 for (vector::iterator it = units_vec.begin(); it != units_vec.end(); ++it) { if (contains(*it, "%")) it = units_vec.erase(it, it+1) - 1; } #else // Something similar to this is a better way to erase vector::iterator del = remove_if(units_vec.begin(), units_vec.end(), bind2nd(contains_functor(), "%")); units_vec.erase(del, units_vec.end()); #endif string units = getStringFromVector(units_vec, "|"); fl_addto_choice(dialog_->choice_value_space_above, units.c_str()); fl_addto_choice(dialog_->choice_value_space_below, units.c_str()); // Manage the ok, apply, restore and cancel/close buttons bc_.setOK(dialog_->button_ok); bc_.setApply(dialog_->button_apply); bc_.setCancel(dialog_->button_close); bc_.setRestore(dialog_->button_restore); bc_.addReadOnly(dialog_->radio_align_right); bc_.addReadOnly(dialog_->radio_align_left); bc_.addReadOnly(dialog_->radio_align_block); bc_.addReadOnly(dialog_->radio_align_center); bc_.addReadOnly(dialog_->check_lines_top); bc_.addReadOnly(dialog_->check_lines_bottom); bc_.addReadOnly(dialog_->check_pagebreaks_top); bc_.addReadOnly(dialog_->check_pagebreaks_bottom); bc_.addReadOnly(dialog_->choice_space_above); bc_.addReadOnly(dialog_->input_space_above); bc_.addReadOnly(dialog_->check_space_above); bc_.addReadOnly(dialog_->choice_space_below); bc_.addReadOnly(dialog_->input_space_below); bc_.addReadOnly(dialog_->check_space_below); bc_.addReadOnly(dialog_->choice_linespacing); bc_.addReadOnly(dialog_->input_linespacing); bc_.addReadOnly(dialog_->check_noindent); bc_.addReadOnly(dialog_->input_labelwidth); } namespace { VSpace setVSpaceFromWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length, FL_OBJECT * choice_length, FL_OBJECT * check_keep) { // Paranoia check! lyx::Assert(choice_type && choice_type->objclass == FL_CHOICE && input_length && input_length->objclass == FL_INPUT && choice_length && choice_length->objclass == FL_CHOICE && check_keep && check_keep->objclass == FL_CHECKBUTTON); VSpace space; switch (fl_get_choice(choice_type)) { case 1: space = VSpace(VSpace::NONE); break; case 2: space = VSpace(VSpace::DEFSKIP); break; case 3: space = VSpace(VSpace::SMALLSKIP); break; case 4: space = VSpace(VSpace::MEDSKIP); break; case 5: space = VSpace(VSpace::BIGSKIP); break; case 6: space = VSpace(VSpace::VFILL); break; case 7: { string const length = getLengthFromWidgets(input_length, choice_length); space = VSpace(LyXGlueLength(length)); break; } } if (fl_get_button(check_keep)) space.setKeep(true); return space; } void validateVSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length) { // Paranoia check! lyx::Assert(choice_type && choice_type->objclass == FL_CHOICE && input_length && input_length->objclass == FL_INPUT); if (fl_get_choice(choice_type) != 7) return; // If a vspace kind is "Length" but there's no text in // the input field, reset the kind to "None". string const input = strip(getStringFromInput(input_length)); if (input.empty()) fl_set_choice(choice_type, 1); } } // namespace anon void FormParagraph::apply() { if (!lv_->view()->available() || !dialog_.get()) return; // If a vspace kind is "Length" but there's no text in // the input field, reset the kind to "None". validateVSpaceWidgets(dialog_->choice_space_above, dialog_->input_space_above); validateVSpaceWidgets(dialog_->choice_space_below, dialog_->input_space_below); bool const line_top = fl_get_button(dialog_->check_lines_top); bool const line_bottom = fl_get_button(dialog_->check_lines_bottom); bool const pagebreak_top = fl_get_button(dialog_->check_pagebreaks_top); bool const pagebreak_bottom = fl_get_button(dialog_->check_pagebreaks_bottom); VSpace const space_top = setVSpaceFromWidgets(dialog_->choice_space_above, dialog_->input_space_above, dialog_->choice_value_space_above, dialog_->check_space_above); VSpace const space_bottom = setVSpaceFromWidgets(dialog_->choice_space_below, dialog_->input_space_below, dialog_->choice_value_space_below, dialog_->check_space_below); LyXAlignment align; if (fl_get_button(dialog_->radio_align_left)) align = LYX_ALIGN_LEFT; else if (fl_get_button(dialog_->radio_align_right)) align = LYX_ALIGN_RIGHT; else if (fl_get_button(dialog_->radio_align_center)) align = LYX_ALIGN_CENTER; else align = LYX_ALIGN_BLOCK; string const labelwidthstring = getStringFromInput(dialog_->input_labelwidth); bool const noindent = fl_get_button(dialog_->check_noindent); Spacing::Space linespacing = Spacing::Default; string other; switch (fl_get_choice(dialog_->choice_linespacing)) { case 1: linespacing = Spacing::Default; break; case 2: linespacing = Spacing::Single; break; case 3: linespacing = Spacing::Onehalf; break; case 4: linespacing = Spacing::Double; break; case 5: linespacing = Spacing::Other; other = getStringFromInput(dialog_->input_linespacing); break; } Spacing const spacing(linespacing, other); LyXText * text(lv_->view()->getLyXText()); text->setParagraph(lv_->view(), line_top, line_bottom, pagebreak_top, pagebreak_bottom, space_top, space_bottom, spacing, align, labelwidthstring, noindent); // Actually apply these settings lv_->view()->update(text, BufferView::SELECT | BufferView::FITCUR | BufferView::CHANGE); lv_->buffer()->markDirty(); setMinibuffer(lv_, _("Paragraph layout set")); } namespace { void setWidgetsFromVSpace(VSpace const & space, FL_OBJECT * choice_type, FL_OBJECT * input_length, FL_OBJECT * choice_length, FL_OBJECT * check_keep) { // Paranoia check! lyx::Assert(choice_type && choice_type->objclass == FL_CHOICE && input_length && input_length->objclass == FL_INPUT && choice_length && choice_length->objclass == FL_CHOICE && check_keep && check_keep->objclass == FL_CHECKBUTTON); fl_set_input(input_length, ""); setEnabled(input_length, false); setEnabled(choice_length, false); switch (space.kind()) { case VSpace::NONE: fl_set_choice(choice_type, 1); break; case VSpace::DEFSKIP: fl_set_choice(choice_type, 2); break; case VSpace::SMALLSKIP: fl_set_choice(choice_type, 3); break; case VSpace::MEDSKIP: fl_set_choice(choice_type, 4); break; case VSpace::BIGSKIP: fl_set_choice(choice_type, 5); break; case VSpace::VFILL: fl_set_choice(choice_type, 6); break; case VSpace::LENGTH: { fl_set_choice(choice_type, 7); setEnabled(input_length, true); setEnabled(choice_length, true); bool const metric = lyxrc.default_papersize > 3; string const default_unit = metric ? "cm" : "in"; string const length = space.length().asString(); updateWidgetsFromLengthString(input_length, choice_length, length, default_unit); break; } } fl_set_button(check_keep, space.keep()); } } // namespace anon void FormParagraph::update() { if (!dialog_.get()) return; // Do this first; some objects may be de/activated subsequently. bc_.readOnly(lv_->buffer()->isReadonly()); /// Record the paragraph par_ = getCurrentParagraph(); fl_set_input(dialog_->input_labelwidth, par_->getLabelWidthString().c_str()); setEnabled(dialog_->input_labelwidth, (par_->getLabelWidthString() != _("Senseless with this layout!"))); fl_set_button(dialog_->radio_align_right, 0); fl_set_button(dialog_->radio_align_left, 0); fl_set_button(dialog_->radio_align_center, 0); fl_set_button(dialog_->radio_align_block, 0); LyXTextClass const & tclass = textclasslist[lv_->view()->buffer()->params.textclass]; int align = par_->getAlign(); if (align == LYX_ALIGN_LAYOUT) align = tclass[par_->layout()].align; switch (align) { case LYX_ALIGN_RIGHT: fl_set_button(dialog_->radio_align_right, 1); break; case LYX_ALIGN_LEFT: fl_set_button(dialog_->radio_align_left, 1); break; case LYX_ALIGN_CENTER: fl_set_button(dialog_->radio_align_center, 1); break; default: fl_set_button(dialog_->radio_align_block, 1); break; } LyXAlignment alignpos = tclass[par_->layout()].alignpossible; setEnabled(dialog_->radio_align_block, bool(alignpos & LYX_ALIGN_BLOCK)); setEnabled(dialog_->radio_align_center, bool(alignpos & LYX_ALIGN_CENTER)); setEnabled(dialog_->radio_align_left, bool(alignpos & LYX_ALIGN_LEFT)); setEnabled(dialog_->radio_align_right, bool(alignpos & LYX_ALIGN_RIGHT)); // no inset-text-owned paragraph may have pagebreaks setEnabled(dialog_->check_pagebreaks_top, !par_->inInset()); setEnabled(dialog_->check_pagebreaks_bottom, !par_->inInset()); fl_set_button(dialog_->check_lines_top, par_->params().lineTop()); fl_set_button(dialog_->check_lines_bottom, par_->params().lineBottom()); fl_set_button(dialog_->check_pagebreaks_top, par_->params().pagebreakTop()); fl_set_button(dialog_->check_pagebreaks_bottom, par_->params().pagebreakBottom()); fl_set_button(dialog_->check_noindent, par_->params().noindent()); int linespacing; Spacing const space = par_->params().spacing(); switch (space.getSpace()) { default: linespacing = 1; break; case Spacing::Single: linespacing = 2; break; case Spacing::Onehalf: linespacing = 3; break; case Spacing::Double: linespacing = 4; break; case Spacing::Other: linespacing = 5; break; } fl_set_choice(dialog_->choice_linespacing, linespacing); if (space.getSpace() == Spacing::Other) { string const sp = tostr(space.getValue()); fl_set_input(dialog_->input_linespacing, sp.c_str()); setEnabled(dialog_->input_linespacing, true); } else { fl_set_input(dialog_->input_linespacing, ""); setEnabled(dialog_->input_linespacing, false); } setWidgetsFromVSpace(par_->params().spaceTop(), dialog_->choice_space_above, dialog_->input_space_above, dialog_->choice_value_space_above, dialog_->check_space_above); setWidgetsFromVSpace(par_->params().spaceBottom(), dialog_->choice_space_below, dialog_->input_space_below, dialog_->choice_value_space_below, dialog_->check_space_below); fl_set_button(dialog_->check_noindent, par_->params().noindent()); } namespace { void synchronizeSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length, FL_OBJECT * choice_length, bool readonly) { // Paranoia check! lyx::Assert(choice_type && choice_type->objclass == FL_CHOICE && input_length && input_length->objclass == FL_INPUT && choice_length && choice_length->objclass == FL_CHOICE); if (fl_get_choice(choice_type) != 7) { fl_set_input(input_length, ""); setEnabled(input_length, false); setEnabled(choice_length, false); } else { setEnabled(input_length, !readonly); setEnabled(choice_length, !readonly); string const length = getStringFromInput(input_length); if (strip(length).empty()) { bool const metric = lyxrc.default_papersize > 3; int const default_unit = metric ? 8 : 9; fl_set_choice(choice_length, default_unit); } } } bool validSpaceWidgets(FL_OBJECT * choice_type, FL_OBJECT * input_length) { // Paranoia check! lyx::Assert(choice_type && choice_type->objclass == FL_CHOICE && input_length && input_length->objclass == FL_INPUT); if (fl_get_choice(choice_type) != 7) return true; string const input = getStringFromInput(input_length); return (input.empty() || isValidGlueLength(input) || isStrDbl(input)); } } // namespace anon bool FormParagraph::input(FL_OBJECT * ob, long) { clearMessage(); // First check the buttons which are exclusive and you have to // check only the actuall de/activated button. // // "Synchronize" the choices and input fields, making it // impossible to commit senseless data. if (ob == dialog_->choice_space_above) { synchronizeSpaceWidgets(dialog_->choice_space_above, dialog_->input_space_above, dialog_->choice_value_space_above, lv_->buffer()->isReadonly()); } if (ob == dialog_->choice_space_below) { synchronizeSpaceWidgets(dialog_->choice_space_below, dialog_->input_space_below, dialog_->choice_value_space_below, lv_->buffer()->isReadonly()); } // Display a warning if the input is senseless bool valid = (validSpaceWidgets(dialog_->choice_space_above, dialog_->input_space_above) && validSpaceWidgets(dialog_->choice_space_below, dialog_->input_space_below)); if (!valid) { postWarning(_("Invalid Length (valid example: 10mm)")); } int const choice_spacing = fl_get_choice(dialog_->choice_linespacing); if (choice_spacing == 5) setEnabled(dialog_->input_linespacing, true); else { fl_set_input(dialog_->input_linespacing, ""); setEnabled(dialog_->input_linespacing, false); } double const spacing = strToDbl(getStringFromInput(dialog_->input_linespacing)); if (choice_spacing == 5 && int(spacing) == 0) valid = false; return valid; }