text-toolbar.cpp revision 07697c208fea6a18b6f8706c4b35396b272bac14
/**
* @file
* Text aux toolbar
*/
/* Authors:
* MenTaLguY <mental@rydia.net>
* Lauris Kaplinski <lauris@kaplinski.com>
* bulia byak <buliabyak@users.sf.net>
* Frank Felfe <innerspace@iname.com>
* John Cliff <simarilius@yahoo.com>
* David Turner <novalis@gnu.org>
* Josh Andler <scislac@scislac.com>
* Jon A. Cruz <jon@joncruz.org>
* Maximilian Albert <maximilian.albert@gmail.com>
* Tavmjong Bah <tavmjong@free.fr>
* Abhishek Sharma
* Kris De Gussem <Kris.DeGussem@gmail.com>
*
* Copyright (C) 2004 David Turner
* Copyright (C) 2003 MenTaLguY
* Copyright (C) 2001-2002 Ximian, Inc.
* Copyright (C) 1999-2013 authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "libnrtype/font-lister.h"
#include "text-toolbar.h"
#include "desktop-style.h"
#include "desktop.h"
#include "document-undo.h"
#include "document.h"
#include "widgets/ege-adjustment-action.h"
#include "widgets/ege-select-one-action.h"
#include "widgets/ink-action.h"
#include "widgets/ink-comboboxentry-action.h"
#include "inkscape.h"
#include "preferences.h"
#include "selection-chemistry.h"
#include "selection.h"
#include "sp-flowtext.h"
#include "sp-root.h"
#include "sp-text.h"
#include "style.h"
#include "svg/css-ostringstream.h"
#include "text-editing.h"
#include "toolbox.h"
#include "ui/icon-names.h"
#include "ui/tools/text-tool.h"
#include "ui/tools/tool-base.h"
#include "verbs.h"
using Inkscape::DocumentUndo;
//#define DEBUG_TEXT
//########################
//## Text Toolbox ##
//########################
// Functions for debugging:
#ifdef DEBUG_TEXT
<< " Style set? " << style_set
<< " FontSpec set? " << fontspec_set
<< " FontSpec: "
}
"NORMAL", "BOLD", "LIGHTER", "BOLDER", "Out of range"};
// Missing book = 380
}
}
#endif
// Font family
{
#ifdef DEBUG_TEXT
#endif
// quit if run by the _changed callbacks
#ifdef DEBUG_TEXT
#endif
return;
}
// TODO: Think about how to handle handle multiple selections. While
// the font-family may be the same for all, the styles might be different.
// See: TextEdit::onApply() for example of looping over selected items.
#ifdef DEBUG_TEXT
#endif
// Changed font-family
// New font-family, not in document, not on system (could be fallback list)
}
// active text set in sp_text_toolbox_selection_changed()
// Update default
} else {
// If there is a selection, update
_("Text: Change font family"));
}
}
// unfreeze
#ifdef DEBUG_TEXT
#endif
}
// Font size
{
// quit if run by the _changed callbacks
return;
}
return;
}
int max_size = prefs->getInt("/dialogs/textandfont/maxFontSize", 10000); // somewhat arbitrary, but text&font preview freezes with too huge fontsizes
// Set css font size.
} else {
}
// Apply font size to selected objects.
// If no selected objects, set default.
int result_numbers =
if (result_numbers == QUERY_STYLE_NOTHING)
{
} else {
// Save for undo
_("Text: Change font size"));
}
}
/*
* Font style
*/
{
// quit if run by the _changed callbacks
return;
}
// active text set in sp_text_toolbox_seletion_changed()
_("Text: Change font style"));
}
}
// Handles both Superscripts and Subscripts
{
// quit if run by the _changed callbacks
return;
}
// Called by Superscript or Subscript button?
#ifdef DEBUG_TEXT
#endif
// Query baseline
int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_BASELINES);
bool setSuper = false;
bool setSub = false;
// If not set or mixed, turn on superscript or subscript
if( prop == 0 ) {
setSuper = true;
} else {
setSub = true;
}
} else {
// Superscript
// Subscript
}
// Set css properties
// Openoffice 2.3 and Adobe use 58%, Microsoft Word 2002 uses 65%, LaTex about 70%.
// 58% looks too small to me, especially if a superscript is placed on a superscript.
// If you make a change here, consider making a change to baseline-shift amount
// in style.cpp.
} else {
}
if( setSuper ) {
} else if( setSub ) {
} else {
}
// Apply css to selected objects.
// Save for undo
if(result_baseline != QUERY_STYLE_NOTHING) {
_("Text: Change superscript or subscript"));
}
}
{
// quit if run by the _changed callbacks
return;
}
// move the x of all texts to preserve the same bbox
if (SP_IS_TEXT(*i)) {
// below, variable names suggest horizontal move, but we check the writing direction
// and move in the corresponding axis
} else {
}
if (!bbox)
continue;
// If you want to align within some frame, other than the text's own bbox, calculate
// the left and right (or top and bottom for tb text) slacks of the text inside that
// frame (currently unused)
double left_slack = 0;
double right_slack = 0;
double move = 0;
switch (mode) {
case 0:
move = -left_slack;
break;
case 1:
break;
case 2:
break;
}
} else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
switch (mode) {
case 0:
break;
case 1:
break;
case 2:
break;
}
switch (mode) {
case 0:
break;
case 1:
break;
case 2:
move = right_slack;
break;
}
}
} else {
}
item->updateRepr();
}
}
switch (mode)
{
case 0:
{
break;
}
case 1:
{
break;
}
case 2:
{
break;
}
case 3:
{
break;
}
}
int result_numbers =
// If querying returned nothing, update default style.
if (result_numbers == QUERY_STYLE_NOTHING)
{
}
if (result_numbers != QUERY_STYLE_NOTHING)
{
_("Text: Change alignment"));
}
}
{
// quit if run by the _changed callbacks
return;
}
// At the moment this handles only numerical values (i.e. no percent).
// Set css line height.
// Apply line-height to selected objects.
// Until deprecated sodipodi:linespacing purged:
bool modmade = false;
if (SP_IS_TEXT (*i)) {
(*i)->getRepr()->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
modmade = true;
}
}
// Save for undo
if(modmade) {
_("Text: Change line-height"));
}
// If no selected objects, set default.
int result_numbers =
if (result_numbers == QUERY_STYLE_NOTHING)
{
}
}
{
// quit if run by the _changed callbacks
return;
}
// At the moment this handles only numerical values (i.e. no em unit).
// Set css word-spacing
// Apply word-spacing to selected objects.
// If no selected objects, set default.
int result_numbers =
if (result_numbers == QUERY_STYLE_NOTHING)
{
} else {
// Save for undo
_("Text: Change word-spacing"));
}
}
{
// quit if run by the _changed callbacks
return;
}
// At the moment this handles only numerical values (i.e. no em unit).
// Set css letter-spacing
// Apply letter-spacing to selected objects.
// If no selected objects, set default.
int result_numbers =
if (result_numbers == QUERY_STYLE_NOTHING)
{
}
else
{
// Save for undo
_("Text: Change letter-spacing"));
}
}
{
// quit if run by the _changed callbacks
return;
}
bool modmade = false;
if( tc ) {
unsigned char_index = -1;
text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
if( attributes ) {
modmade = true;
}
}
}
if(modmade) {
// Save for undo
_("Text: Change dx (kern)"));
}
}
{
// quit if run by the _changed callbacks
return;
}
bool modmade = false;
if( tc ) {
unsigned char_index = -1;
text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
if( attributes ) {
modmade = true;
}
}
}
if(modmade) {
// Save for undo
_("Text: Change dy"));
}
}
{
// quit if run by the _changed callbacks
return;
}
bool modmade = false;
if( tc ) {
unsigned char_index = -1;
text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
if( attributes ) {
sp_te_adjust_rotation( tc->text, tc->text_sel_start, tc->text_sel_end, SP_ACTIVE_DESKTOP, delta_deg );
modmade = true;
}
}
}
// Save for undo
if(modmade) {
_("Text: Change rotate"));
}
}
{
// quit if run by the _changed callbacks
return;
}
switch (mode)
{
case 0:
{
break;
}
case 1:
{
break;
}
case 2:
{
break;
}
}
int result_numbers =
// If querying returned nothing, update default style.
if (result_numbers == QUERY_STYLE_NOTHING)
{
}
{
_("Text: Change writing mode"));
}
}
{
// quit if run by the _changed callbacks
return;
}
switch (mode)
{
case 0:
{
break;
}
case 1:
{
break;
}
case 2:
{
break;
}
}
int result_numbers =
// If querying returned nothing, update default style.
if (result_numbers == QUERY_STYLE_NOTHING)
{
}
{
_("Text: Change orientation"));
}
}
/*
* Set the default list of font sizes, scaled to the users preferred unit
*/
{
// List of font sizes for drop-down menu
int sizes[] = {
4, 6, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 28,
32, 36, 40, 48, 56, 64, 72, 144
};
// Array must be same length as SPCSSUnit in style.h
for( unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i ) {
}
}
/*
* This function sets up the text-tool tool-controls, setting the entry boxes
* etc. to the values from the current selection or the default if no selection.
* It is called whenever a text selection is changed, including stepping cursor
* through text, or setting focus to text.
*/
static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/, GObject *tbl, bool subselection = false) // don't bother to update font list if subsel changed
{
#ifdef DEBUG_TEXT
static int count = 0;
++count;
{
}
#endif
// quit if run by the _changed callbacks
#ifdef DEBUG_TEXT
#endif
return;
}
if (!subselection) {
}
// Update font list, but only if widget already created.
ink_comboboxentry_action_set_active_text( fontFamilyAction, fontlister->get_font_family().c_str(), fontlister->get_font_family_row() );
}
// Only flowed text can be justified, only normal text can be kerned...
// Find out if we have flowed text now so we can use it several places
// const gchar* id = reinterpret_cast<SPItem *>(items->data)->getId();
// std::cout << " " << id << std::endl;
if( SP_IS_FLOWTEXT(*i)) {
isFlow = true;
// std::cout << " Found flowed text" << std::endl;
break;
}
}
/*
* Query from current selection:
* Font family (font-family)
* Style (font-weight, font-style, font-stretch, font-variant, font-align)
* Numbers (font-size, letter-spacing, word-spacing, line-height, text-anchor, writing-mode)
* Font specification (Inkscape private attribute)
*/
int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTFAMILY);
int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTSTYLE);
int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_BASELINES);
int result_wmode = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_WRITINGMODES);
/*
* If no text in selection (querying returned nothing), read the style from
* tool bar already set to these preferences.
*/
if (result_family == QUERY_STYLE_NOTHING ||
result_wmode == QUERY_STYLE_NOTHING ) {
// There are no texts in selection, read from preferences.
#ifdef DEBUG_TEXT
sp_print_font( &query );
#endif
// Do not reset the toolbar style from prefs if we already did it last time
#ifdef DEBUG_TEXT
#endif
return;
}
// To ensure the value of the combobox is properly set on start-up, only mark
// the prefs set if the combobox has already been constructed.
}
} else {
}
// If we have valid query data for text (font-family, font-specification) set toolbar accordingly.
{
// Size (average of text selected)
//gchar size_text[G_ASCII_DTOSTR_BUF_SIZE];
//g_ascii_dtostr (size_text, sizeof (size_text), size);
// Freeze to ignore callbacks.
//g_object_freeze_notify( G_OBJECT( fontSizeAction->combobox ) );
//g_object_thaw_notify( G_OBJECT( fontSizeAction->combobox ) );
Glib::ustring tooltip = Glib::ustring::format(_("Font size"), " (", sp_style_get_css_unit_string(unit), ")");
// Superscript
InkToggleAction* textSuperscriptAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextSuperscriptAction" ) );
// Subscript
InkToggleAction* textSubscriptAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextSubscriptAction" ) );
// Alignment
EgeSelectOneAction* textAlignAction = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextAlignAction" ) );
// Note: SVG 1.1 doesn't include text-align, SVG 1.2 Tiny doesn't include text-align="justify"
// text-align="justify" was a draft SVG 1.2 item (along with flowed text).
// Only flowed text can be left and right justified at the same time.
// Disable button if we don't have flowed text.
// The GtkTreeModel class doesn't have a set function so we can't
// simply add an ege_select_one_action_set_sensitive method!
// We must set values directly with the GtkListStore and then
// ask that the GtkAction update the sensitive parameters.
// ege_select_one_action_set_sensitive( textAlignAction, 3, isFlow );
int activeButton = 0;
{
activeButton = 3;
} else {
}
// Line height (spacing)
double height;
} else {
} else {
}
}
// Word spacing
double wordSpacing;
// Letter spacing
double letterSpacing;
else letterSpacing = query.letter_spacing.computed; // Assume no units (change in desktop-style.cpp)
// Writing mode
int activeButton2 = 0;
// Orientation
int activeButton3 = 0;
// Disable text orientation for horizontal text.. See above for why this nonsense
}
#ifdef DEBUG_TEXT
#endif
// Kerning (xshift), yshift, rotation. NB: These are not CSS attributes.
if( tc ) {
unsigned char_index = -1;
text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
if( attributes ) {
// Dx
// Dy
// Rotation
/* SVG value is between 0 and 360 but we're using -180 to 180 in widget */
#ifdef DEBUG_TEXT
#endif
}
}
}
{
}
#ifdef DEBUG_TEXT
#endif
}
static void sp_text_toolbox_selection_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
{
}
static void
{
}
// TODO: possibly share with font-selector by moving most code to font-lister (passing family name)
static void sp_text_toolbox_select_cb( GtkEntry* entry, GtkEntryIconPosition /*position*/, GdkEvent /*event*/, gpointer /*data*/ ) {
//std::cout << "text_toolbox_missing_font_cb: selecting: " << family << std::endl;
// Get all items with matching font-family set (not inherited!).
std::vector<SPItem*> allList = get_all_items(x, document->getRoot(), desktop, false, false, true, y);
if (style) {
//std::cout << " family style from font_family: " << family_style << std::endl;
}
//std::cout << " family style from font_spec: " << family_style << std::endl;
}
//std::cout << " found: " << item->getId() << std::endl;
}
}
}
// Update selection
//std::cout << " list length: " << g_slist_length ( selectList ) << std::endl;
}
static void text_toolbox_watch_ec(SPDesktop* dt, Inkscape::UI::Tools::ToolBase* ec, GObject* holder);
// Define all the "widgets" in the toolbar.
{
/* Font family */
{
// Font list
ink_comboboxentry_action_new( "TextFontFamilyAction",
_("Font Family"),
_("Select Font Family (Alt-X to access)"),
NULL,
-1, // Entry width
50, // Extra list width
//ink_comboboxentry_action_set_warning_callback( act, sp_text_fontfamily_select_all );
// Change style of drop-down from menu to list
"style \"dropdown-as-list-style\"\n"
"{\n"
" GtkComboBox::appears-as-list = 1\n"
"}\n"
"widget \"*.TextFontFamilyAction_combobox\" style \"dropdown-as-list-style\""
"style \"fontfamily-separator-style\"\n"
"{\n"
" GtkWidget::wide-separators = 1\n"
" GtkWidget::separator-height = 6\n"
"}\n"
"widget \"*gtk-combobox-popup-window.GtkScrolledWindow.GtkTreeView\" style \"fontfamily-separator-style\"");
}
/* Font size */
{
// List of font sizes for drop-down menu
Glib::ustring tooltip = Glib::ustring::format(_("Font size"), " (", sp_style_get_css_unit_string(unit), ")");
_("Font Size"),
NULL,
4, // Width in characters
0, // Extra list width
NULL, // Cell layout
NULL, // Separator
}
/* Font styles */
{
_("Font Style"),
_("Font style"),
NULL,
12, // Width in characters
0, // Extra list width
NULL, // Cell layout
NULL, // Separator
}
/* Style - Superscript */
{
_("Toggle Superscript"), // Label
_("Toggle superscript"), // Tooltip
"text_superscript", // Icon (inkId)
secondarySize ); // Icon size
}
/* Style - Subscript */
{
_("Toggle Subscript"), // Label
_("Toggle subscript"), // Tooltip
"text_subscript", // Icon (inkId)
secondarySize ); // Icon size
}
/* Alignment */
{
GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN );
0, _("Align left"),
1, _("Align left"),
3, true,
-1 );
0, _("Align center"),
1, _("Align center"),
3, true,
-1 );
0, _("Align right"),
1, _("Align right"),
3, true,
-1 );
0, _("Justify"),
1, _("Justify (only flowed text)"),
3, false,
-1 );
_("Alignment"), // Label
_("Text alignment"), // Tooltip
NULL, // Icon name
}
/* Writing mode (Horizontal, Vertical-LR, Vertical-RL) */
{
0, _("Horizontal"),
1, _("Horizontal text"),
-1 );
0, _("Vertical — RL"),
1, _("Vertical text — lines: right to left"),
-1 );
0, _("Vertical — LR"),
1, _("Vertical text — lines: left to right"), // Mongolian!
-1 );
_("Writing mode"), // Label
_("Block progression"), // Tooltip
NULL, // Icon name
}
/* Text (glyph) orientation (Auto (mixed), Upright, Sideways) */
{
GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN );
0, _("Auto"),
1, _("Auto glyph orientation"),
3, true,
-1 );
0, _("Upright"),
1, _("Upright glyph orientation"),
3, true,
-1 );
0, _("Sideways"),
1, _("Sideways glyph orientation"),
3, true,
-1 );
_("Text orientation"), // Label
_("Text (glyph) orientation in vertical text."), // Tooltip
NULL, // Icon name
g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_changed), holder );
}
/* Line height */
{
// Drop down menu
gchar const* labels[] = {_("Smaller spacing"), 0, 0, 0, 0, C_("Text tool", "Normal"), 0, 0, 0, 0, 0, _("Larger spacing")};
"TextLineHeightAction", /* name */
_("Line Height"), /* label */
_("Line:"), /* short label */
_("Spacing between lines (times font size)"), /* tooltip */
"/tools/text/lineheight", /* preferences path */
0.0, /* default */
holder, /* dataKludge */
FALSE, /* set alt-x keyboard shortcut? */
NULL, /* altx_mark */
sp_text_lineheight_value_changed, /* callback */
NULL, /* unit tracker */
0.1, /* step (used?) */
2, /* digits to show */
1.0 /* factor (multiplies default) */
);
}
/* Word spacing */
{
// Drop down menu
gchar const* labels[] = {_("Negative spacing"), 0, 0, 0, C_("Text tool", "Normal"), 0, 0, 0, 0, 0, 0, 0, _("Positive spacing")};
"TextWordSpacingAction", /* name */
_("Word spacing"), /* label */
_("Word:"), /* short label */
_("Spacing between words (px)"), /* tooltip */
"/tools/text/wordspacing", /* preferences path */
0.0, /* default */
holder, /* dataKludge */
FALSE, /* set alt-x keyboard shortcut? */
NULL, /* altx_mark */
sp_text_wordspacing_value_changed, /* callback */
NULL, /* unit tracker */
0.1, /* step (used?) */
2, /* digits to show */
1.0 /* factor (multiplies default) */
);
}
/* Letter spacing */
{
// Drop down menu
gchar const* labels[] = {_("Negative spacing"), 0, 0, 0, C_("Text tool", "Normal"), 0, 0, 0, 0, 0, 0, 0, _("Positive spacing")};
"TextLetterSpacingAction", /* name */
_("Letter spacing"), /* label */
_("Letter:"), /* short label */
_("Spacing between letters (px)"), /* tooltip */
"/tools/text/letterspacing", /* preferences path */
0.0, /* default */
holder, /* dataKludge */
FALSE, /* set alt-x keyboard shortcut? */
NULL, /* altx_mark */
sp_text_letterspacing_value_changed, /* callback */
NULL, /* unit tracker */
0.1, /* step (used?) */
2, /* digits to show */
1.0 /* factor (multiplies default) */
);
}
/* Character kerning (horizontal shift) */
{
// Drop down menu
"TextDxAction", /* name */
_("Kerning"), /* label */
_("Kern:"), /* short label */
_("Horizontal kerning (px)"), /* tooltip */
0.0, /* default */
holder, /* dataKludge */
FALSE, /* set alt-x keyboard shortcut? */
NULL, /* altx_mark */
sp_text_dx_value_changed, /* callback */
NULL, /* unit tracker */
0.1, /* step (used?) */
2, /* digits to show */
1.0 /* factor (multiplies default) */
);
}
/* Character vertical shift */
{
// Drop down menu
"TextDyAction", /* name */
_("Vertical Shift"), /* label */
_("Vert:"), /* short label */
_("Vertical shift (px)"), /* tooltip */
0.0, /* default */
holder, /* dataKludge */
FALSE, /* set alt-x keyboard shortcut? */
NULL, /* altx_mark */
sp_text_dy_value_changed, /* callback */
NULL, /* unit tracker */
0.1, /* step (used?) */
2, /* digits to show */
1.0 /* factor (multiplies default) */
);
}
/* Character rotation */
{
// Drop down menu
"TextRotationAction", /* name */
_("Letter rotation"), /* label */
_("Rot:"), /* short label */
_("Character rotation (degrees)"),/* tooltip */
0.0, /* default */
holder, /* dataKludge */
FALSE, /* set alt-x keyboard shortcut? */
NULL, /* altx_mark */
sp_text_rotation_value_changed, /* callback */
NULL, /* unit tracker */
0.1, /* step (used?) */
2, /* digits to show */
1.0 /* factor (multiplies default) */
);
}
// Is this necessary to call? Shouldn't hurt.
}
static void text_toolbox_watch_ec(SPDesktop* desktop, Inkscape::UI::Tools::ToolBase* ec, GObject* holder) {
using sigc::connection;
static connection c_selection_changed;
static connection c_selection_modified;
static connection c_subselection_changed;
if (SP_IS_TEXT_CONTEXT(ec)) {
// Watch selection
c_selection_changed = desktop->getSelection()->connectChanged(bind(ptr_fun(sp_text_toolbox_selection_changed), holder, false));
c_selection_modified = desktop->getSelection()->connectModified(bind(ptr_fun(sp_text_toolbox_selection_modified), holder));
c_subselection_changed = desktop->connectToolSubselectionChanged(bind(ptr_fun(sp_text_toolbox_subselection_changed), holder));
} else {
if (c_selection_changed)
if (c_selection_modified)
}
}
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 :