Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Вывод текста


Автор: CrazyPerson 20.12.2008, 14:06
В процессе написания программы понадобилось выводить текст на экран в определенном формате:

name1
  description11
    example11
    example12
  description2
    example21
    example22
name2
...


Сначала пытался использовать для этой цели RichEdit, но не понял как сделать отступы только для части текста.
TreeView тоже не помог, так как описания и примеры могут быть большой длинны, и содержать несколько строчек. А в TreeView, как я понял, один элемент занимает ровно одну строчку.

Никто не знает что было бы удобно использовать для такого форматирования текста?

Автор: GremlinProg 20.12.2008, 16:58
вот общий, нерекурсивный псевдо-код для вывода динамического дерева
Код

std::vector<Node>stack;

stack.push_back(root_node);

while(!stack.empty()){

  // извлекаем узел из стека
  node = stack.back();

  // определяем число табуляторов для текущего узла
  for( tabs_count = 0 , parent = get_parent(node) ; parent ; parent = get_parent(parent) , ++tabs_count );

  // выводим tabs_count табуляторов в окно
  print_tabs( tabs_count );
  // выводим, собственно, сам текст, связанный с узлом node
  print_content( node );
  // продолжаем итерации для дочерних узлов
  for( child = get_last( get_first_child( node ) ) ; child ; child = get_prev(child) ){
    stack.push_back(child);
  }

}

вместо табуляторов можно использовать любое обозначение отступа: хоть пробелы, хоть отступ на n пикселей при выводе содержимого узла в окно
если содержимое каждого узла многострочное, значит этот отступ нужно делать перед каждой строкой текущего узла

для реализации алгоритма требуется ввести все задействованные методы:
get_parent - возвращает родительский узел, или 0, если такового не существует
get_first_child - возвращает первый дочерний подузел для заданного родительского узла, или 0, если такового не существует
get_last - возвращает самый последний братский подузел, который находится на том же уровне иерархии дерева, у одного родителя, или 0, если такового не существует
get_prev - возвращает предыдущий братский подузел, или 0, если такового не существует
print_tabs - печатает заданное число табуляторов с текущей позиции
print_content  - печатает содержимое узла с текущей позиции
последние 2 метода - на выбор, если многострочный вывод, значит их нужно объединить в один, которому  в параметрах, помимо самого узла, передается tabs_count

если все дочерние узлы находятся в массиве, то можно еще проще сделать продолжение итераций, т.е. без get_first_child, get_last, get_prev:
Код

  // продолжаем итерации для дочерних узлов
  for( i = node.children.size() ; i-- ; ){
    stack.push_back( node.children[i] );
  }

, где node.children - обращение к массиву дочерних узлов, родителя node

Node - псевдо-обозначение идентификатора узла
если все элементы дерева расположены в массиве, значит в качестве Node можно использовать индекс узла в этом массиве
если под каждый элемент дерева распределяется отдельный блок памяти, значит Node - указатель на узел

CrazyPerson, если не понял как приспособить этот алгоритм, тогда выкладывай структуру своей иерархии, подскажем.

Автор: CrazyPerson 20.12.2008, 17:44
Ого, спасибо за столь развернутый ответ! Но к сожалению это несколько не то. Обход самого дерева я то быстро написал.
А проблема у меня возникла со следующей частью кода:

Код

// выводим tabs_count табуляторов в окно
  print_tabs( tabs_count );
  // выводим, собственно, сам текст, связанный с узлом node
  print_content( node );
  


Ну вот в treeView все выводить в одну строку, и соответственно это никак не подходит для многострочного вывода.

А в RichEdit я не понял как выводить "параграф текста" с заданным отступом. Ну как я понял там можно разбивать текст на параграфы и выводить их с различным форматированием. Но вот сделать этого у меня не получилось...

Автор: GremlinProg 20.12.2008, 19:13
treeView тоже может выводить многострочные узлы:
Цитата(MSDN TVITEMEX Structure)

typedef struct tagTVITEMEX {
    UINT mask;
    HTREEITEM hItem;
    UINT state;
    UINT stateMask;
    LPTSTR pszText;
    int cchTextMax;
    int iImage;
    int iSelectedImage;
    int cChildren;
    LPARAM lParam;
    int iIntegral;
} TVITEMEX, *LPTVITEMEX;

Цитата(MSDN TVITEMEX Structure)

iIntegral
Height of the item. This height is in increments of the standard item height (see TVM_SETITEMHEIGHT). By default, each item gets one increment of item height. Setting this member to 2 will give the item twice the standard height; setting this member to 3 will give the item three times the standard height; and so on. The tree-view control does not draw in this extra area. This extra space can be used by the application for drawing when using custom draw. 

а если использовать RichEdit, то параграф в принципе избыточен, есть более простой способ: писать перед каждой строкой табуляторы в числе tabs_count, размер которых впоследствии можно настроить через EM_SETTABSTOPS
Цитата(MSDN EM_SETTABSTOPS)

wParam
Specifies the number of tab stops contained in the array. If this parameter is zero, the lParam parameter is ignored and default tab stops are set at every 32 dialog template units. If this parameter is 1, tab stops are set at every n dialog template units, where n is the distance pointed to by the lParam parameter. If this parameter is greater than 1, lParam is a pointer to an array of tab stops. 
lParam
Pointer to an array of unsigned integers specifying the tab stops, in dialog template units. If the wParam parameter is 1, this parameter is a pointer to an unsigned integer containing the distance between all tab stops, in dialog template units. 
Windows 95/98/Me: The buffer pointed to by lParam must reside in writable memory, even though the message does not modify the array. 

Автор: CrazyPerson 20.12.2008, 23:27
Ага, спасибо. msdn'ом я еще не научился нормально пользоваться, но я стараюсь.
Но и тот и тот способ подразумевает что мне придется вручную разбивать текст на строки, и никакого готового word wrap'a не будет. Или я не правильно понял что в richedit придется добавлять ручками табы перед каждой строчкой?

Автор: GremlinProg 21.12.2008, 18:01
Цитата(CrazyPerson @  21.12.2008,  01:27 Найти цитируемый пост)
Но и тот и тот способ подразумевает что мне придется вручную разбивать текст на строки, и никакого готового word wrap'a не будет

да, про word wrap пока ни слова не было, я так понимал, что тебе нужно вывести дерево  с фиксированным числом строк в узлах.
word wrap в корне меняет дело
тогда нужно вводить параграфы
Цитата(MSDN EM_SETPARAFORMAT)

wParam
This parameter is not used; it must be zero. 
lParam
Pointer to a PARAFORMAT structure specifying the new paragraph formatting attributes. Only the attributes specified by the dwMask member are changed. 
Microsoft® Rich Edit 2.0 and later: This parameter can be a pointer to a PARAFORMAT2 structure, which is an extension of the PARAFORMAT structure. Before sending the EM_SETPARAFORMAT message, set the structure's cbSize member to indicate the version of the structure.

Цитата(MSDN PARAFORMAT)

typedef struct _paraformat {
    UINT cbSize;
    DWORD dwMask;
    WORD wNumbering;
    WORD wReserved;
    LONG dxStartIndent;
    LONG dxRightIndent;
    LONG dxOffset;
    WORD wAlignment;
    SHORT cTabCount;
    LONG rgxTabs[MAX_TAB_STOPS];
} PARAFORMAT;

в PARAFORMAT ключевые поля: dwMask и dxStartIndent
[quote MSDN dwMask]
dwMask
Members containing valid information or attributes to set. This parameter can be none or a combination of the following values. If both PFM_STARTINDENT and PFM_OFFSETINDENT are specified, PFM_STARTINDENT takes precedence. 
PFM_ALIGNMENT
The wAlignment member is valid.
PFM_NUMBERING
The wNumbering member is valid.
PFM_OFFSET
The dxOffset member is valid.
PFM_OFFSETINDENT
The dxStartIndent member is valid and specifies a relative value.
PFM_RIGHTINDENT
The dxRightIndent member is valid.
PFM_RTLPARA
Rich Edit 2.0: The wEffects member is valid
PFM_STARTINDENT
The dxStartIndent member is valid.
PFM_TABSTOPS
The cTabStobs and rgxTabStops members are valid.
[/quote]
[quote=MSDN dxStartIndent]
dxStartIndent
Indentation of the first line in the paragraph, in twips. If the paragraph formatting is being set and PFM_OFFSETINDENT is specified, this member is treated as a relative value that is added to the starting indentation of each affected paragraph. 
[/quote]
для установки параграфа нужно:
1. выделитть текст, к которому применяется параграф (если нужно, можно скрыть выделение от пользователя EM_HIDESELECTION)
2. выставить поля PARAFORMAT::cbSize, PARAFORMAT::dwMask и PARAFORMAT::dxStartIndent
3. Вызвать EM_SETPARAFORMAT
после этого даже word wrap не будет переводить строки левее, чем PARAFORMAT::dxStartIndent
PARAFORMAT::dxStartIndent - относительное смещение содержимого параграфа, относительно родительского параграфа

Автор: CrazyPerson 21.12.2008, 22:21
О! То что нужно! 
Еще раз спасибо. Вот про EM_SETPARAFORMAT я нашел, а про то, что еще выделять текст надо - не нашел..

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)